From 8cad280f219afe74b4476f3d731c307a9dc13d0a Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Wed, 17 Sep 2025 04:14:34 -0600 Subject: [PATCH 1/7] BridgeJS: Add support for static / class functions --- .../Sources/BridgeJSCore/ExportSwift.swift | 188 +++++++--- .../Sources/BridgeJSLink/BridgeJSLink.swift | 304 ++++++++++++++-- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 29 +- .../Inputs/StaticFunctions.swift | 40 +++ .../StaticFunctions.Export.d.ts | 60 ++++ .../StaticFunctions.Export.js | 292 +++++++++++++++ .../__Snapshots__/ExportSwiftTests/Async.json | 7 + .../ExportSwiftTests/EnumAssociatedValue.json | 26 ++ .../ExportSwiftTests/EnumCase.json | 19 + .../ExportSwiftTests/EnumNamespace.json | 21 ++ .../ExportSwiftTests/EnumRawType.json | 75 ++++ .../ExportSwiftTests/Namespaces.json | 7 + .../ExportSwiftTests/Optionals.json | 19 + .../ExportSwiftTests/PrimitiveParameters.json | 1 + .../ExportSwiftTests/PrimitiveReturn.json | 4 + .../ExportSwiftTests/PropertyTypes.json | 4 + .../ExportSwiftTests/StaticFunctions.json | 299 ++++++++++++++++ .../ExportSwiftTests/StaticFunctions.swift | 166 +++++++++ .../ExportSwiftTests/StringParameter.json | 2 + .../ExportSwiftTests/StringReturn.json | 1 + .../ExportSwiftTests/SwiftClass.json | 4 + .../ExportSwiftTests/Throws.json | 1 + .../VoidParameterVoidReturn.json | 1 + .../BridgeJS/Exporting-Swift-to-JavaScript.md | 1 + .../Exporting-Swift/Exporting-Swift-Class.md | 2 +- .../Exporting-Swift-Static-Functions.md | 189 ++++++++++ .../BridgeJSRuntimeTests/ExportAPITests.swift | 28 ++ .../Generated/BridgeJS.ExportSwift.swift | 99 +++++ .../JavaScript/BridgeJS.ExportSwift.json | 337 ++++++++++++++++++ Tests/prelude.mjs | 9 +- 30 files changed, 2166 insertions(+), 69 deletions(-) create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticFunctions.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.d.ts create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.json create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.swift create mode 100644 Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Functions.md diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 0b94442a..4b049341 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -93,6 +93,7 @@ public class ExportSwift { var name: String? var cases: [EnumCase] = [] var rawType: String? + var staticMethods: [ExportedFunction] = [] } var currentEnum = CurrentEnum() @@ -152,28 +153,54 @@ public class ExportSwift { } override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind { + guard node.attributes.hasJSAttribute() else { + return .skipChildren + } + + let isStatic = node.modifiers.contains { modifier in + modifier.name.tokenKind == .keyword(.static) || + modifier.name.tokenKind == .keyword(.class) + } + switch state { case .topLevel: - if let exportedFunction = visitFunction( - node: node - ) { + if isStatic { + diagnose(node: node, message: "Top-level functions cannot be static") + return .skipChildren + } + if let exportedFunction = visitFunction(node: node, isStatic: false) { exportedFunctions.append(exportedFunction) } return .skipChildren - case .classBody(_, let classKey): + case .classBody(let className, let classKey): if let exportedFunction = visitFunction( - node: node + node: node, + isStatic: isStatic, + className: className, + classKey: classKey ) { exportedClassByName[classKey]?.methods.append(exportedFunction) } return .skipChildren - case .enumBody: - diagnose(node: node, message: "Functions are not supported inside enums") + case .enumBody(let enumName): + if !isStatic { + diagnose(node: node, message: "Only static functions are supported in enums") + return .skipChildren + } + if let exportedFunction = visitFunction(node: node, isStatic: isStatic, enumName: enumName) { + currentEnum.staticMethods.append(exportedFunction) + } return .skipChildren } } - private func visitFunction(node: FunctionDeclSyntax) -> ExportedFunction? { + private func visitFunction( + node: FunctionDeclSyntax, + isStatic: Bool, + className: String? = nil, + classKey: String? = nil, + enumName: String? = nil + ) -> ExportedFunction? { guard let jsAttribute = node.attributes.firstJSAttribute else { return nil } @@ -189,6 +216,14 @@ public class ExportSwift { ) } + if namespace != nil, case .enumBody = state { + diagnose( + node: jsAttribute, + message: "Namespace is not supported for enum static functions", + hint: "Remove the namespace from @JS attribute - enum functions inherit namespace from enum" + ) + } + var parameters: [Parameter] = [] for param in node.signature.parameterClause.parameters { let resolvedType = self.parent.lookupType(for: param.type) @@ -226,20 +261,52 @@ public class ExportSwift { } let abiName: String + let staticContext: StaticContext? + switch state { case .topLevel: abiName = "bjs_\(name)" + staticContext = nil case .classBody(let className, _): - abiName = "bjs_\(className)_\(name)" - case .enumBody: - abiName = "" - diagnose( - node: node, - message: "Functions are not supported inside enums" - ) + if isStatic { + abiName = "bjs_\(className)_static_\(name)" + staticContext = .className(className) + } else { + abiName = "bjs_\(className)_\(name)" + staticContext = nil + } + case .enumBody(let enumName): + if !isStatic { + diagnose(node: node, message: "Only static functions are supported in enums") + return nil + } + + let isNamespaceEnum = currentEnum.cases.isEmpty + + if isNamespaceEnum { + // For namespace enums, compute the full Swift call path manually + var swiftPath: [String] = [] + var currentNode: Syntax? = node.parent + while let parent = currentNode { + if let enumDecl = parent.as(EnumDeclSyntax.self), + enumDecl.attributes.hasJSAttribute() + { + swiftPath.insert(enumDecl.name.text, at: 0) + } + currentNode = parent.parent + } + let fullEnumCallName = swiftPath.joined(separator: ".") + + // ABI name should include full namespace path to avoid conflicts + abiName = "bjs_\(swiftPath.joined(separator: "_"))_\(name)" + staticContext = .namespaceEnum(fullEnumCallName) + } else { + abiName = "bjs_\(enumName)_static_\(name)" + staticContext = .enumName(enumName) + } } - guard let effects = collectEffects(signature: node.signature) else { + guard let effects = collectEffects(signature: node.signature, isStatic: isStatic) else { return nil } @@ -249,11 +316,12 @@ public class ExportSwift { parameters: parameters, returnType: returnType, effects: effects, - namespace: namespace + namespace: namespace, + staticContext: staticContext ) } - private func collectEffects(signature: FunctionSignatureSyntax) -> Effects? { + private func collectEffects(signature: FunctionSignatureSyntax, isStatic: Bool = false) -> Effects? { let isAsync = signature.effectSpecifiers?.asyncSpecifier != nil var isThrows = false if let throwsClause: ThrowsClauseSyntax = signature.effectSpecifiers?.throwsClause { @@ -274,7 +342,7 @@ public class ExportSwift { } isThrows = true } - return Effects(isAsync: isAsync, isThrows: isThrows) + return Effects(isAsync: isAsync, isThrows: isThrows, isStatic: isStatic) } private func extractNamespace( @@ -537,15 +605,25 @@ public class ExportSwift { } let emitStyle = extractEnumStyle(from: jsAttribute) ?? .const - if case .tsEnum = emitStyle, - let raw = currentEnum.rawType, - let rawEnum = SwiftEnumRawType.from(raw), rawEnum == .bool - { - diagnose( - node: jsAttribute, - message: "TypeScript enum style is not supported for Bool raw-value enums", - hint: "Use enumStyle: .const or change the raw type to String or a numeric type" - ) + + if case .tsEnum = emitStyle { + if let raw = currentEnum.rawType, + let rawEnum = SwiftEnumRawType.from(raw), rawEnum == .bool + { + diagnose( + node: jsAttribute, + message: "TypeScript enum style is not supported for Bool raw-value enums", + hint: "Use enumStyle: .const or change the raw type to String or a numeric type" + ) + } + + if !currentEnum.staticMethods.isEmpty { + diagnose( + node: jsAttribute, + message: "TypeScript enum style does not support static functions", + hint: "Use enumStyle: .const to generate a const object that supports static functions" + ) + } } if currentEnum.cases.contains(where: { !$0.associatedValues.isEmpty }) { @@ -597,7 +675,8 @@ public class ExportSwift { cases: currentEnum.cases, rawType: currentEnum.rawType, namespace: effectiveNamespace, - emitStyle: emitStyle + emitStyle: emitStyle, + staticMethods: currentEnum.staticMethods ) exportedEnumByName[enumName] = exportedEnum exportedEnumNames.append(enumName) @@ -862,6 +941,10 @@ public class ExportSwift { case .namespace: () } + + for staticMethod in enumDef.staticMethods { + decls.append(try renderSingleExportedFunction(function: staticMethod)) + } } for function in exportedFunctions { @@ -1269,7 +1352,24 @@ public class ExportSwift { for param in function.parameters { try builder.liftParameter(param: param) } - builder.call(name: function.name, returnType: function.returnType) + + if function.effects.isStatic, let staticContext = function.staticContext { + let callName: String + switch staticContext { + case .className(let className): + callName = "\(className).\(function.name)" + case .enumName(let enumName): + callName = "\(enumName).\(function.name)" + case .namespaceEnum(let enumName): + callName = "\(enumName).\(function.name)" + case .explicitNamespace(let namespace): + callName = "\(namespace.joined(separator: ".")).\(function.name)" + } + builder.call(name: callName, returnType: function.returnType) + } else { + builder.call(name: function.name, returnType: function.returnType) + } + try builder.lowerReturnValue(returnType: function.returnType) return builder.render(abiName: function.abiName) } @@ -1335,17 +1435,25 @@ public class ExportSwift { } for method in klass.methods { let builder = ExportedThunkBuilder(effects: method.effects) - try builder.liftParameter( - param: Parameter(label: nil, name: "_self", type: BridgeType.swiftHeapObject(klass.swiftCallName)) - ) - for param in method.parameters { - try builder.liftParameter(param: param) + + if method.effects.isStatic { + for param in method.parameters { + try builder.liftParameter(param: param) + } + builder.call(name: "\(klass.swiftCallName).\(method.name)", returnType: method.returnType) + } else { + try builder.liftParameter( + param: Parameter(label: nil, name: "_self", type: BridgeType.swiftHeapObject(klass.swiftCallName)) + ) + for param in method.parameters { + try builder.liftParameter(param: param) + } + builder.callMethod( + klassName: klass.swiftCallName, + methodName: method.name, + returnType: method.returnType + ) } - builder.callMethod( - klassName: klass.swiftCallName, - methodName: method.name, - returnType: method.returnType - ) try builder.lowerReturnValue(returnType: method.returnType) decls.append(builder.render(abiName: method.abiName)) } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 9715014c..741f6158 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -76,6 +76,7 @@ struct BridgeJSLink { var topLevelEnumLines: [String] = [] var topLevelDtsEnumLines: [String] = [] var importObjectBuilders: [ImportObjectBuilder] = [] + var enumStaticAssignments: [String] = [] } private func collectLinkData() throws -> LinkData { @@ -145,9 +146,19 @@ struct BridgeJSLink { // Process functions for function in skeleton.functions { + if function.effects.isStatic, + case .enumName(_) = function.staticContext + { + continue + } + var (js, dts) = try renderExportedFunction(function: function) - if function.namespace != nil { + if function.effects.isStatic, + case .namespaceEnum(_) = function.staticContext + { + data.namespacedFunctions.append(function) + } else if function.namespace != nil { data.namespacedFunctions.append(function) } @@ -156,6 +167,42 @@ struct BridgeJSLink { data.exportsLines.append(contentsOf: js) data.dtsExportLines.append(contentsOf: dts) } + + for enumDefinition in skeleton.enums where enumDefinition.enumType == .namespace { + for function in enumDefinition.staticMethods { + // Create namespace function with full namespace path (parent namespace + enum name) + var functionWithNamespace = function + let fullNamespace = (enumDefinition.namespace ?? []) + [enumDefinition.name] + functionWithNamespace.namespace = fullNamespace + data.namespacedFunctions.append(functionWithNamespace) + + var (js, dts) = try renderExportedFunction(function: functionWithNamespace) + js[0] = "\(functionWithNamespace.name): " + js[0] + js[js.count - 1] += "," + data.exportsLines.append(contentsOf: js) + data.dtsExportLines.append(contentsOf: dts) + } + } + + for enumDefinition in skeleton.enums where enumDefinition.enumType != .namespace { + for function in enumDefinition.staticMethods { + let (funcJs, _) = try renderEnumStaticFunction(function: function, enumName: enumDefinition.name) + var assignmentJs = funcJs + if !assignmentJs.isEmpty && assignmentJs.last?.hasSuffix(",") == true { + assignmentJs[assignmentJs.count - 1] = String(assignmentJs.last?.dropLast() ?? "") + } + if !assignmentJs.isEmpty { + let firstLine = assignmentJs[0] + if firstLine.contains("(") { + assignmentJs[0] = + "\(enumDefinition.name).\(function.name) = function" + + firstLine.dropFirst(function.name.count) + } + } + data.enumStaticAssignments.append(contentsOf: assignmentJs) + } + } + } // Process imported skeletons @@ -575,6 +622,8 @@ struct BridgeJSLink { let hasNamespacedItems = !data.namespacedFunctions.isEmpty || !data.namespacedClasses.isEmpty printer.write(lines: data.classLines) + printer.write(lines: data.enumStaticAssignments) + if hasNamespacedItems { printer.write("const exports = {") printer.indent { @@ -825,6 +874,7 @@ struct BridgeJSLink { _ = fragment.printCode([enumDefinition.name], scope, printer, cleanup) jsLines.append(contentsOf: printer.lines) + try addStaticFunctionsToEnum(enumDefinition: enumDefinition, jsLines: &jsLines, dtsLines: &dtsLines) case .rawValue: guard enumDefinition.rawType != nil else { throw BridgeJSLinkError(message: "Raw value enum \(enumDefinition.name) is missing rawType") @@ -834,11 +884,13 @@ struct BridgeJSLink { _ = fragment.printCode([enumDefinition.name], scope, printer, cleanup) jsLines.append(contentsOf: printer.lines) + try addStaticFunctionsToEnum(enumDefinition: enumDefinition, jsLines: &jsLines, dtsLines: &dtsLines) case .associatedValue: let fragment = IntrinsicJSFragment.associatedValueEnumHelper(enumDefinition: enumDefinition) _ = fragment.printCode([enumDefinition.name], scope, printer, cleanup) jsLines.append(contentsOf: printer.lines) + try addStaticFunctionsToEnum(enumDefinition: enumDefinition, jsLines: &jsLines, dtsLines: &dtsLines) case .namespace: break } @@ -850,9 +902,48 @@ struct BridgeJSLink { return (jsLines, dtsLines) } + private func addStaticFunctionsToEnum( + enumDefinition: ExportedEnum, + jsLines: inout [String], + dtsLines: inout [String] + ) throws { + let enumStaticFunctions = enumDefinition.staticMethods + + guard !enumStaticFunctions.isEmpty else { return } + + let enumName = enumDefinition.name + + if !jsLines.isEmpty { + for i in 0.. [String] { let printer = CodeFragmentPrinter() + let enumStaticFunctions = enumDefinition.staticMethods + switch enumDefinition.emitStyle { case .tsEnum: switch enumDefinition.enumType { @@ -891,6 +982,15 @@ struct BridgeJSLink { ) printer.write("readonly \(caseName): \(value);") } + + for function in enumStaticFunctions { + let signature = renderTSSignature( + parameters: function.parameters, + returnType: function.returnType, + effects: function.effects + ) + printer.write("\(function.name)\(signature);") + } } printer.write("};") printer.write( @@ -907,6 +1007,15 @@ struct BridgeJSLink { } } printer.write("};") + + for function in enumStaticFunctions { + let signature = renderTSSignature( + parameters: function.parameters, + returnType: function.returnType, + effects: function.effects + ) + printer.write("\(function.name)\(signature);") + } } printer.write("};") printer.nextLine() @@ -955,6 +1064,10 @@ struct BridgeJSLink { extension BridgeJSLink { func renderExportedFunction(function: ExportedFunction) throws -> (js: [String], dts: [String]) { + if function.effects.isStatic, let staticContext = function.staticContext { + return try renderStaticFunction(function: function, staticContext: staticContext) + } + let thunkBuilder = ExportedThunkBuilder(effects: function.effects) for param in function.parameters { try thunkBuilder.lowerParameter(param: param) @@ -974,6 +1087,97 @@ extension BridgeJSLink { return (funcLines, dtsLines) } + private func renderStaticFunction( + function: ExportedFunction, + staticContext: StaticContext + ) throws -> (js: [String], dts: [String]) { + switch staticContext { + case .className(let className): + return try renderClassStaticFunction(function: function, className: className) + case .enumName(let enumName): + return try renderEnumStaticFunction(function: function, enumName: enumName) + case .namespaceEnum(let enumName): + return try renderNamespaceFunction(function: function, namespace: enumName) + case .explicitNamespace(let namespace): + return try renderNamespaceFunction(function: function, namespace: namespace.joined(separator: ".")) + } + } + + private func renderClassStaticFunction( + function: ExportedFunction, + className: String + ) throws -> (js: [String], dts: [String]) { + let thunkBuilder = ExportedThunkBuilder(effects: function.effects) + for param in function.parameters { + try thunkBuilder.lowerParameter(param: param) + } + let returnExpr = try thunkBuilder.call(abiName: function.abiName, returnType: function.returnType) + + let funcLines = thunkBuilder.renderFunction( + name: function.name, + parameters: function.parameters, + returnExpr: returnExpr, + declarationPrefixKeyword: "static" + ) + + let dtsLines = [ + "static \(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));" + ] + + return (funcLines, dtsLines) + } + + private func renderEnumStaticFunction( + function: ExportedFunction, + enumName: String + ) throws -> (js: [String], dts: [String]) { + let thunkBuilder = ExportedThunkBuilder(effects: function.effects) + for param in function.parameters { + try thunkBuilder.lowerParameter(param: param) + } + let returnExpr = try thunkBuilder.call(abiName: function.abiName, returnType: function.returnType) + + let printer = CodeFragmentPrinter() + printer.write("\(function.name)(\(function.parameters.map { $0.name }.joined(separator: ", "))) {") + printer.indent { + printer.write(contentsOf: thunkBuilder.body) + printer.write(contentsOf: thunkBuilder.cleanupCode) + printer.write(lines: thunkBuilder.checkExceptionLines()) + if let returnExpr = returnExpr { + printer.write("return \(returnExpr);") + } + } + printer.write("},") + + let dtsLines = [ + "\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));" + ] + + return (printer.lines, dtsLines) + } + + private func renderNamespaceFunction( + function: ExportedFunction, + namespace: String + ) throws -> (js: [String], dts: [String]) { + let thunkBuilder = ExportedThunkBuilder(effects: function.effects) + for param in function.parameters { + try thunkBuilder.lowerParameter(param: param) + } + let returnExpr = try thunkBuilder.call(abiName: function.abiName, returnType: function.returnType) + + let funcLines = thunkBuilder.renderFunction( + name: function.abiName, + parameters: function.parameters, + returnExpr: returnExpr, + declarationPrefixKeyword: "function" + ) + + let dtsLines: [String] = [] + + return (funcLines, dtsLines) + } + func renderExportedClass( _ klass: ExportedClass ) throws -> (js: [String], dtsType: [String], dtsExportEntry: [String]) { @@ -1023,28 +1227,53 @@ extension BridgeJSLink { } for method in klass.methods { - let thunkBuilder = ExportedThunkBuilder(effects: method.effects) - thunkBuilder.lowerSelf() - for param in method.parameters { - try thunkBuilder.lowerParameter(param: param) - } - let returnExpr = try thunkBuilder.call(abiName: method.abiName, returnType: method.returnType) + if method.effects.isStatic { + let thunkBuilder = ExportedThunkBuilder(effects: method.effects) + for param in method.parameters { + try thunkBuilder.lowerParameter(param: param) + } + let returnExpr = try thunkBuilder.call(abiName: method.abiName, returnType: method.returnType) - jsPrinter.indent { - jsPrinter.write( - lines: thunkBuilder.renderFunction( - name: method.name, - parameters: method.parameters, - returnExpr: returnExpr, - declarationPrefixKeyword: nil + jsPrinter.indent { + jsPrinter.write( + lines: thunkBuilder.renderFunction( + name: method.name, + parameters: method.parameters, + returnExpr: returnExpr, + declarationPrefixKeyword: "static" + ) ) - ) - } + } - dtsTypePrinter.indent { - dtsTypePrinter.write( - "\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: method.effects));" - ) + dtsExportEntryPrinter.indent { + dtsExportEntryPrinter.write( + "\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: method.effects));" + ) + } + } else { + let thunkBuilder = ExportedThunkBuilder(effects: method.effects) + thunkBuilder.lowerSelf() + for param in method.parameters { + try thunkBuilder.lowerParameter(param: param) + } + let returnExpr = try thunkBuilder.call(abiName: method.abiName, returnType: method.returnType) + + jsPrinter.indent { + jsPrinter.write( + lines: thunkBuilder.renderFunction( + name: method.name, + parameters: method.parameters, + returnExpr: returnExpr, + declarationPrefixKeyword: nil + ) + ) + } + + dtsTypePrinter.indent { + dtsTypePrinter.write( + "\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType, effects: method.effects));" + ) + } } } @@ -1422,7 +1651,13 @@ extension BridgeJSLink { for skeleton in exportedSkeletons { for function in skeleton.functions { - if let namespace = function.namespace { + if function.effects.isStatic, + case .namespaceEnum(let enumName) = function.staticContext + { + var currentNode = rootNode + currentNode = currentNode.addChild(enumName) + currentNode.content.functions.append(function) + } else if let namespace = function.namespace { var currentNode = rootNode for part in namespace { currentNode = currentNode.addChild(part) @@ -1447,6 +1682,18 @@ extension BridgeJSLink { } currentNode.content.enums.append(enumDefinition) } + + if enumDefinition.enumType == .namespace { + for function in enumDefinition.staticMethods { + var currentNode = rootNode + // Build full namespace path: parent namespace + enum name + let fullNamespace = (enumDefinition.namespace ?? []) + [enumDefinition.name] + for part in fullNamespace { + currentNode = currentNode.addChild(part) + } + currentNode.content.functions.append(function) + } + } } } @@ -1786,3 +2033,18 @@ extension WasmCoreType { } } } + +extension StaticContext { + var namespaceName: String { + switch self { + case .className(let name): + return name + case .enumName(let name): + return name + case .namespaceEnum(let name): + return name + case .explicitNamespace(let components): + return components.joined(separator: ".") + } + } +} diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 32df40d1..c7f7d128 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -62,7 +62,7 @@ public enum SwiftEnumRawType: String, CaseIterable, Codable, Sendable { } } -public struct Parameter: Codable { +public struct Parameter: Codable, Equatable, Sendable { public let label: String? public let name: String public let type: BridgeType @@ -74,16 +74,27 @@ public struct Parameter: Codable { } } -public struct Effects: Codable { +public struct Effects: Codable, Equatable, Sendable { public var isAsync: Bool public var isThrows: Bool + public var isStatic: Bool - public init(isAsync: Bool, isThrows: Bool) { + public init(isAsync: Bool, isThrows: Bool, isStatic: Bool = false) { self.isAsync = isAsync self.isThrows = isThrows + self.isStatic = isStatic } } +// MARK: - Static Function Context + +public enum StaticContext: Codable, Equatable, Sendable { + case className(String) + case enumName(String) + case namespaceEnum(String) + case explicitNamespace([String]) +} + // MARK: - Enum Skeleton public struct AssociatedValue: Codable, Equatable, Sendable { @@ -124,6 +135,7 @@ public struct ExportedEnum: Codable, Equatable, Sendable { public let rawType: String? public let namespace: [String]? public let emitStyle: EnumEmitStyle + public var staticMethods: [ExportedFunction] public var enumType: EnumType { if cases.isEmpty { return .namespace @@ -141,7 +153,8 @@ public struct ExportedEnum: Codable, Equatable, Sendable { cases: [EnumCase], rawType: String?, namespace: [String]?, - emitStyle: EnumEmitStyle + emitStyle: EnumEmitStyle, + staticMethods: [ExportedFunction] = [] ) { self.name = name self.swiftCallName = swiftCallName @@ -150,6 +163,7 @@ public struct ExportedEnum: Codable, Equatable, Sendable { self.rawType = rawType self.namespace = namespace self.emitStyle = emitStyle + self.staticMethods = staticMethods } } @@ -162,13 +176,14 @@ public enum EnumType: String, Codable, Sendable { // MARK: - Exported Skeleton -public struct ExportedFunction: Codable { +public struct ExportedFunction: Codable, Equatable, Sendable { public var name: String public var abiName: String public var parameters: [Parameter] public var returnType: BridgeType public var effects: Effects public var namespace: [String]? + public var staticContext: StaticContext? public init( name: String, @@ -176,7 +191,8 @@ public struct ExportedFunction: Codable { parameters: [Parameter], returnType: BridgeType, effects: Effects, - namespace: [String]? = nil + namespace: [String]? = nil, + staticContext: StaticContext? = nil ) { self.name = name self.abiName = abiName @@ -184,6 +200,7 @@ public struct ExportedFunction: Codable { self.returnType = returnType self.effects = effects self.namespace = namespace + self.staticContext = staticContext } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticFunctions.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticFunctions.swift new file mode 100644 index 00000000..120d6888 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticFunctions.swift @@ -0,0 +1,40 @@ +@JS class MathUtils { + @JS init() {} + + @JS class func subtract(a: Int, b: Int) -> Int { + return a - b + } + + @JS static func add(a: Int, b: Int) -> Int { + return a + b + } + + @JS func multiply(x: Int, y: Int) -> Int { + return x * y + } +} + +@JS enum Calculator { + case scientific + case basic + + @JS static func square(value: Int) -> Int { + return value * value + } +} + +@JS +enum APIResult { + case success(String) + case failure(Int) + + @JS static func roundtrip(value: APIResult) -> APIResult { } +} + +@JS enum Utils { + @JS enum String { + @JS static func uppercase(_ text: String) -> String { + return text.uppercased() + } + } +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.d.ts new file mode 100644 index 00000000..715d9d8c --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.d.ts @@ -0,0 +1,60 @@ +// 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 const Calculator: { + readonly Scientific: 0; + readonly Basic: 1; + square(value: number): number; +}; +export type Calculator = typeof Calculator[keyof typeof Calculator]; + +export const APIResult: { + readonly Tag: { + readonly Success: 0; + readonly Failure: 1; + }; + roundtrip(value: APIResult): APIResult; +}; + +export type APIResult = + { tag: typeof APIResult.Tag.Success; param0: string } | { tag: typeof APIResult.Tag.Failure; param0: number } + +export {}; + +declare global { + namespace Utils { + namespace String { + uppercase(text: string): string; + } + } +} + +/// Represents a Swift heap object like a class instance or an actor instance. +export interface SwiftHeapObject { + /// Release the heap object. + /// + /// Note: Calling this method will release the heap object and it will no longer be accessible. + release(): void; +} +export interface MathUtils extends SwiftHeapObject { + multiply(x: number, y: number): number; +} +export type Exports = { + MathUtils: { + new(): MathUtils; + subtract(a: number, b: number): number; + add(a: number, b: number): number; + } +} +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/StaticFunctions.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js new file mode 100644 index 00000000..280332dc --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js @@ -0,0 +1,292 @@ +// 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 const Calculator = { + Scientific: 0, + Basic: 1, + square: null, +}; + +export const APIResult = { + Tag: { + Success: 0, + Failure: 1, + } + roundtrip: null, +}; + +const __bjs_createAPIResultHelpers = () => { + return (tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift) => ({ + lower: (value) => { + const enumTag = value.tag; + switch (enumTag) { + case APIResult.Tag.Success: { + const bytes = textEncoder.encode(value.param0); + const id = swift.memory.retain(bytes); + tmpParamInts.push(bytes.length); + tmpParamInts.push(id); + const cleanup = () => { + swift.memory.release(id); + }; + return { caseId: APIResult.Tag.Success, cleanup }; + } + case APIResult.Tag.Failure: { + tmpParamInts.push((value.param0 | 0)); + const cleanup = undefined; + return { caseId: APIResult.Tag.Failure, cleanup }; + } + default: throw new Error("Unknown APIResult tag: " + String(enumTag)); + } + }, + raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s) => { + const tag = tmpRetTag | 0; + switch (tag) { + case APIResult.Tag.Success: { + const string = tmpRetStrings.pop(); + return { tag: APIResult.Tag.Success, param0: string }; + } + case APIResult.Tag.Failure: { + const int = tmpRetInts.pop(); + return { tag: APIResult.Tag.Failure, param0: int }; + } + default: throw new Error("Unknown APIResult tag returned from Swift: " + String(tag)); + } + } + }); +}; +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + 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 tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpParamInts = []; + let tmpParamF32s = []; + let tmpParamF64s = []; + const enumHelpers = {}; + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + const bjs = {}; + importObject["bjs"] = bjs; + const imports = options.getImports(importsContext); + bjs["swift_js_return_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + return swift.memory.retain(textDecoder.decode(bytes)); + } + 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_tag"] = function(tag) { + tmpRetTag = tag; + } + bjs["swift_js_push_int"] = function(v) { + tmpRetInts.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + tmpRetF32s.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + const value = textDecoder.decode(bytes); + tmpRetStrings.push(value); + } + bjs["swift_js_pop_param_int32"] = function() { + return tmpParamInts.pop(); + } + bjs["swift_js_pop_param_f32"] = function() { + return tmpParamF32s.pop(); + } + bjs["swift_js_pop_param_f64"] = function() { + return tmpParamF64s.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 { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + 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; + } + } + // Wrapper functions for module: TestModule + if (!importObject["TestModule"]) { + importObject["TestModule"] = {}; + } + importObject["TestModule"]["bjs_MathUtils_wrap"] = function(pointer) { + const obj = MathUtils.__construct(pointer); + return swift.memory.retain(obj); + }; + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + const APIResultHelpers = __bjs_createAPIResultHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); + enumHelpers.APIResult = APIResultHelpers; + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + /// Represents a Swift heap object like a class instance or an actor instance. + class SwiftHeapObject { + static __wrap(pointer, deinit, prototype) { + const obj = Object.create(prototype); + obj.pointer = pointer; + obj.hasReleased = false; + obj.deinit = deinit; + obj.registry = new FinalizationRegistry((pointer) => { + deinit(pointer); + }); + obj.registry.register(this, obj.pointer); + return obj; + } + + release() { + this.registry.unregister(this); + this.deinit(this.pointer); + } + } + class MathUtils extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_MathUtils_deinit, MathUtils.prototype); + } + + constructor() { + const ret = instance.exports.bjs_MathUtils_init(); + return MathUtils.__construct(ret); + } + static subtract(a, b) { + const ret = instance.exports.bjs_MathUtils_static_subtract(a, b); + return ret; + } + static add(a, b) { + const ret = instance.exports.bjs_MathUtils_static_add(a, b); + return ret; + } + multiply(x, y) { + const ret = instance.exports.bjs_MathUtils_multiply(this.pointer, x, y); + return ret; + } + } + Calculator.square = function(value) { + const ret = instance.exports.bjs_Calculator_static_square(value); + return ret; + } + APIResult.roundtrip = function(value) { + const { caseId: valueCaseId, cleanup: valueCleanup } = enumHelpers.APIResult.lower(value); + instance.exports.bjs_APIResult_static_roundtrip(valueCaseId); + const ret = enumHelpers.APIResult.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s); + if (valueCleanup) { valueCleanup(); } + return ret; + } + const exports = { + MathUtils, + uppercase: function bjs_Utils_String_uppercase(text) { + const textBytes = textEncoder.encode(text); + const textId = swift.memory.retain(textBytes); + instance.exports.bjs_Utils_String_uppercase(textId, textBytes.length); + const ret = tmpRetString; + tmpRetString = undefined; + swift.memory.release(textId); + return ret; + }, + }; + if (typeof globalThis.Utils === 'undefined') { + globalThis.Utils = {}; + } + if (typeof globalThis.Utils.String === 'undefined') { + globalThis.Utils.String = {}; + } + globalThis.Utils.String.uppercase = exports.uppercase; + return exports; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Async.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Async.json index c0d5347d..b9f86357 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Async.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Async.json @@ -10,6 +10,7 @@ "abiName" : "bjs_asyncReturnVoid", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncReturnVoid", @@ -26,6 +27,7 @@ "abiName" : "bjs_asyncRoundTripInt", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripInt", @@ -50,6 +52,7 @@ "abiName" : "bjs_asyncRoundTripString", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripString", @@ -74,6 +77,7 @@ "abiName" : "bjs_asyncRoundTripBool", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripBool", @@ -98,6 +102,7 @@ "abiName" : "bjs_asyncRoundTripFloat", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripFloat", @@ -122,6 +127,7 @@ "abiName" : "bjs_asyncRoundTripDouble", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripDouble", @@ -146,6 +152,7 @@ "abiName" : "bjs_asyncRoundTripJSObject", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripJSObject", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json index 1f2b15fe..a5620206 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json @@ -74,6 +74,9 @@ ], "emitStyle" : "const", "name" : "APIResult", + "staticMethods" : [ + + ], "swiftCallName" : "APIResult" }, { @@ -238,6 +241,9 @@ ], "emitStyle" : "const", "name" : "ComplexResult", + "staticMethods" : [ + + ], "swiftCallName" : "ComplexResult" }, { @@ -304,6 +310,9 @@ "name" : "Result", "namespace" : [ "Utilities" + ], + "staticMethods" : [ + ], "swiftCallName" : "Utilities.Result" }, @@ -345,6 +354,9 @@ "name" : "NetworkingResult", "namespace" : [ "API" + ], + "staticMethods" : [ + ], "swiftCallName" : "NetworkingResult" }, @@ -434,6 +446,9 @@ ], "emitStyle" : "const", "name" : "APIOptionalResult", + "staticMethods" : [ + + ], "swiftCallName" : "APIOptionalResult" } ], @@ -442,6 +457,7 @@ "abiName" : "bjs_handle", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "handle", @@ -466,6 +482,7 @@ "abiName" : "bjs_getResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getResult", @@ -482,6 +499,7 @@ "abiName" : "bjs_roundtripAPIResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtripAPIResult", @@ -506,6 +524,7 @@ "abiName" : "bjs_roundTripOptionalAPIResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalAPIResult", @@ -538,6 +557,7 @@ "abiName" : "bjs_handleComplex", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "handleComplex", @@ -562,6 +582,7 @@ "abiName" : "bjs_getComplexResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getComplexResult", @@ -578,6 +599,7 @@ "abiName" : "bjs_roundtripComplexResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtripComplexResult", @@ -602,6 +624,7 @@ "abiName" : "bjs_roundTripOptionalComplexResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalComplexResult", @@ -634,6 +657,7 @@ "abiName" : "bjs_roundTripOptionalUtilitiesResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalUtilitiesResult", @@ -666,6 +690,7 @@ "abiName" : "bjs_roundTripOptionalNetworkingResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalNetworkingResult", @@ -698,6 +723,7 @@ "abiName" : "bjs_roundTripOptionalAPIOptionalResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalAPIOptionalResult", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.json index 38b26020..3f8ae035 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.json @@ -32,6 +32,9 @@ ], "emitStyle" : "const", "name" : "Direction", + "staticMethods" : [ + + ], "swiftCallName" : "Direction" }, { @@ -57,6 +60,9 @@ ], "emitStyle" : "const", "name" : "Status", + "staticMethods" : [ + + ], "swiftCallName" : "Status" }, { @@ -88,6 +94,9 @@ ], "emitStyle" : "tsEnum", "name" : "TSDirection", + "staticMethods" : [ + + ], "swiftCallName" : "TSDirection" }, { @@ -102,6 +111,9 @@ "emitStyle" : "const", "explicitAccessControl" : "public", "name" : "PublicStatus", + "staticMethods" : [ + + ], "swiftCallName" : "PublicStatus" } ], @@ -110,6 +122,7 @@ "abiName" : "bjs_setDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setDirection", @@ -134,6 +147,7 @@ "abiName" : "bjs_getDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getDirection", @@ -150,6 +164,7 @@ "abiName" : "bjs_processDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "processDirection", @@ -174,6 +189,7 @@ "abiName" : "bjs_roundTripOptionalDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalDirection", @@ -206,6 +222,7 @@ "abiName" : "bjs_setTSDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setTSDirection", @@ -230,6 +247,7 @@ "abiName" : "bjs_getTSDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getTSDirection", @@ -246,6 +264,7 @@ "abiName" : "bjs_roundTripOptionalTSDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalTSDirection", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json index 2ec852f1..0ac995ec 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json @@ -5,6 +5,7 @@ "abiName" : "bjs_Converter_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -16,6 +17,7 @@ "abiName" : "bjs_Converter_toString", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "toString", @@ -51,6 +53,7 @@ "abiName" : "bjs_HTTPServer_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -62,6 +65,7 @@ "abiName" : "bjs_HTTPServer_call", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "call", @@ -98,6 +102,7 @@ "abiName" : "bjs_TestServer_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -109,6 +114,7 @@ "abiName" : "bjs_TestServer_call", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "call", @@ -149,6 +155,9 @@ ], "emitStyle" : "const", "name" : "Utils", + "staticMethods" : [ + + ], "swiftCallName" : "Utils" }, { @@ -183,6 +192,9 @@ "namespace" : [ "Networking", "API" + ], + "staticMethods" : [ + ], "swiftCallName" : "Networking.API.Method" }, @@ -223,6 +235,9 @@ "Configuration" ], "rawType" : "String", + "staticMethods" : [ + + ], "swiftCallName" : "Configuration.LogLevel" }, { @@ -255,6 +270,9 @@ "Configuration" ], "rawType" : "Int", + "staticMethods" : [ + + ], "swiftCallName" : "Configuration.Port" }, { @@ -278,6 +296,9 @@ "Networking", "APIV2", "Internal" + ], + "staticMethods" : [ + ], "swiftCallName" : "Internal.SupportedMethod" } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.json index c6f17c0a..0711cd29 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.json @@ -30,6 +30,9 @@ "emitStyle" : "const", "name" : "Theme", "rawType" : "String", + "staticMethods" : [ + + ], "swiftCallName" : "Theme" }, { @@ -59,6 +62,9 @@ "emitStyle" : "tsEnum", "name" : "TSTheme", "rawType" : "String", + "staticMethods" : [ + + ], "swiftCallName" : "TSTheme" }, { @@ -81,6 +87,9 @@ "emitStyle" : "const", "name" : "FeatureFlag", "rawType" : "Bool", + "staticMethods" : [ + + ], "swiftCallName" : "FeatureFlag" }, { @@ -110,6 +119,9 @@ "emitStyle" : "const", "name" : "HttpStatus", "rawType" : "Int", + "staticMethods" : [ + + ], "swiftCallName" : "HttpStatus" }, { @@ -139,6 +151,9 @@ "emitStyle" : "tsEnum", "name" : "TSHttpStatus", "rawType" : "Int", + "staticMethods" : [ + + ], "swiftCallName" : "TSHttpStatus" }, { @@ -182,6 +197,9 @@ "emitStyle" : "const", "name" : "Priority", "rawType" : "Int32", + "staticMethods" : [ + + ], "swiftCallName" : "Priority" }, { @@ -218,6 +236,9 @@ "emitStyle" : "const", "name" : "FileSize", "rawType" : "Int64", + "staticMethods" : [ + + ], "swiftCallName" : "FileSize" }, { @@ -247,6 +268,9 @@ "emitStyle" : "const", "name" : "UserId", "rawType" : "UInt", + "staticMethods" : [ + + ], "swiftCallName" : "UserId" }, { @@ -276,6 +300,9 @@ "emitStyle" : "const", "name" : "TokenId", "rawType" : "UInt32", + "staticMethods" : [ + + ], "swiftCallName" : "TokenId" }, { @@ -305,6 +332,9 @@ "emitStyle" : "const", "name" : "SessionId", "rawType" : "UInt64", + "staticMethods" : [ + + ], "swiftCallName" : "SessionId" }, { @@ -334,6 +364,9 @@ "emitStyle" : "const", "name" : "Precision", "rawType" : "Float", + "staticMethods" : [ + + ], "swiftCallName" : "Precision" }, { @@ -370,6 +403,9 @@ "emitStyle" : "const", "name" : "Ratio", "rawType" : "Double", + "staticMethods" : [ + + ], "swiftCallName" : "Ratio" } ], @@ -378,6 +414,7 @@ "abiName" : "bjs_setTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setTheme", @@ -403,6 +440,7 @@ "abiName" : "bjs_getTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getTheme", @@ -420,6 +458,7 @@ "abiName" : "bjs_roundTripOptionalTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalTheme", @@ -454,6 +493,7 @@ "abiName" : "bjs_setTSTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setTSTheme", @@ -479,6 +519,7 @@ "abiName" : "bjs_getTSTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getTSTheme", @@ -496,6 +537,7 @@ "abiName" : "bjs_roundTripOptionalTSTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalTSTheme", @@ -530,6 +572,7 @@ "abiName" : "bjs_setFeatureFlag", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setFeatureFlag", @@ -555,6 +598,7 @@ "abiName" : "bjs_getFeatureFlag", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getFeatureFlag", @@ -572,6 +616,7 @@ "abiName" : "bjs_roundTripOptionalFeatureFlag", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalFeatureFlag", @@ -606,6 +651,7 @@ "abiName" : "bjs_setHttpStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setHttpStatus", @@ -631,6 +677,7 @@ "abiName" : "bjs_getHttpStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getHttpStatus", @@ -648,6 +695,7 @@ "abiName" : "bjs_roundTripOptionalHttpStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalHttpStatus", @@ -682,6 +730,7 @@ "abiName" : "bjs_setTSHttpStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setTSHttpStatus", @@ -707,6 +756,7 @@ "abiName" : "bjs_getTSHttpStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getTSHttpStatus", @@ -724,6 +774,7 @@ "abiName" : "bjs_roundTripOptionalHttpStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalHttpStatus", @@ -758,6 +809,7 @@ "abiName" : "bjs_setPriority", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setPriority", @@ -783,6 +835,7 @@ "abiName" : "bjs_getPriority", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getPriority", @@ -800,6 +853,7 @@ "abiName" : "bjs_roundTripOptionalPriority", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalPriority", @@ -834,6 +888,7 @@ "abiName" : "bjs_setFileSize", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setFileSize", @@ -859,6 +914,7 @@ "abiName" : "bjs_getFileSize", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getFileSize", @@ -876,6 +932,7 @@ "abiName" : "bjs_roundTripOptionalFileSize", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalFileSize", @@ -910,6 +967,7 @@ "abiName" : "bjs_setUserId", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setUserId", @@ -935,6 +993,7 @@ "abiName" : "bjs_getUserId", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getUserId", @@ -952,6 +1011,7 @@ "abiName" : "bjs_roundTripOptionalUserId", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalUserId", @@ -986,6 +1046,7 @@ "abiName" : "bjs_setTokenId", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setTokenId", @@ -1011,6 +1072,7 @@ "abiName" : "bjs_getTokenId", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getTokenId", @@ -1028,6 +1090,7 @@ "abiName" : "bjs_roundTripOptionalTokenId", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalTokenId", @@ -1062,6 +1125,7 @@ "abiName" : "bjs_setSessionId", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setSessionId", @@ -1087,6 +1151,7 @@ "abiName" : "bjs_getSessionId", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getSessionId", @@ -1104,6 +1169,7 @@ "abiName" : "bjs_roundTripOptionalSessionId", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalSessionId", @@ -1138,6 +1204,7 @@ "abiName" : "bjs_setPrecision", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setPrecision", @@ -1163,6 +1230,7 @@ "abiName" : "bjs_getPrecision", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getPrecision", @@ -1180,6 +1248,7 @@ "abiName" : "bjs_roundTripOptionalPrecision", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalPrecision", @@ -1214,6 +1283,7 @@ "abiName" : "bjs_setRatio", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setRatio", @@ -1239,6 +1309,7 @@ "abiName" : "bjs_getRatio", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getRatio", @@ -1256,6 +1327,7 @@ "abiName" : "bjs_roundTripOptionalRatio", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalRatio", @@ -1290,6 +1362,7 @@ "abiName" : "bjs_processTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "processTheme", @@ -1316,6 +1389,7 @@ "abiName" : "bjs_convertPriority", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "convertPriority", @@ -1342,6 +1416,7 @@ "abiName" : "bjs_validateSession", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "validateSession", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.json index bb81e29a..c9e00721 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.json @@ -5,6 +5,7 @@ "abiName" : "bjs_Greeter_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -24,6 +25,7 @@ "abiName" : "bjs_Greeter_greet", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "greet", @@ -52,6 +54,7 @@ "abiName" : "bjs_Converter_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -63,6 +66,7 @@ "abiName" : "bjs_Converter_toString", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "toString", @@ -100,6 +104,7 @@ "abiName" : "bjs_UUID_uuidString", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "uuidString", @@ -132,6 +137,7 @@ "abiName" : "bjs_plainFunction", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "plainFunction", @@ -148,6 +154,7 @@ "abiName" : "bjs_namespacedFunction", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "namespacedFunction", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.json index 28ac1150..61a033d5 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.json @@ -5,6 +5,7 @@ "abiName" : "bjs_Greeter_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -28,6 +29,7 @@ "abiName" : "bjs_Greeter_greet", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "greet", @@ -44,6 +46,7 @@ "abiName" : "bjs_Greeter_changeName", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "changeName", @@ -92,6 +95,7 @@ "abiName" : "bjs_OptionalPropertyHolder_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -154,6 +158,7 @@ "abiName" : "bjs_roundTripOptionalClass", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalClass", @@ -186,6 +191,7 @@ "abiName" : "bjs_testOptionalPropertyRoundtrip", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "testOptionalPropertyRoundtrip", @@ -218,6 +224,7 @@ "abiName" : "bjs_roundTripString", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripString", @@ -250,6 +257,7 @@ "abiName" : "bjs_roundTripInt", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripInt", @@ -282,6 +290,7 @@ "abiName" : "bjs_roundTripBool", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripBool", @@ -314,6 +323,7 @@ "abiName" : "bjs_roundTripFloat", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripFloat", @@ -346,6 +356,7 @@ "abiName" : "bjs_roundTripDouble", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripDouble", @@ -378,6 +389,7 @@ "abiName" : "bjs_roundTripSyntax", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripSyntax", @@ -410,6 +422,7 @@ "abiName" : "bjs_roundTripMixSyntax", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripMixSyntax", @@ -442,6 +455,7 @@ "abiName" : "bjs_roundTripSwiftSyntax", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripSwiftSyntax", @@ -474,6 +488,7 @@ "abiName" : "bjs_roundTripMixedSwiftSyntax", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripMixedSwiftSyntax", @@ -506,6 +521,7 @@ "abiName" : "bjs_roundTripWithSpaces", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripWithSpaces", @@ -538,6 +554,7 @@ "abiName" : "bjs_roundTripAlias", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripAlias", @@ -570,6 +587,7 @@ "abiName" : "bjs_roundTripOptionalAlias", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalAlias", @@ -602,6 +620,7 @@ "abiName" : "bjs_testMixedOptionals", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "testMixedOptionals", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveParameters.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveParameters.json index c58f3c8e..10638ebf 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveParameters.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveParameters.json @@ -10,6 +10,7 @@ "abiName" : "bjs_check", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "check", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveReturn.json index ee29313b..b1f89765 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveReturn.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveReturn.json @@ -10,6 +10,7 @@ "abiName" : "bjs_checkInt", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "checkInt", @@ -26,6 +27,7 @@ "abiName" : "bjs_checkFloat", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "checkFloat", @@ -42,6 +44,7 @@ "abiName" : "bjs_checkDouble", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "checkDouble", @@ -58,6 +61,7 @@ "abiName" : "bjs_checkBool", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "checkBool", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.json index e0c5e812..a728578e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.json @@ -5,6 +5,7 @@ "abiName" : "bjs_PropertyHolder_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -69,6 +70,7 @@ "abiName" : "bjs_PropertyHolder_getAllValues", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getAllValues", @@ -240,6 +242,7 @@ "abiName" : "bjs_createPropertyHolder", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "createPropertyHolder", @@ -309,6 +312,7 @@ "abiName" : "bjs_testPropertyHolder", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "testPropertyHolder", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.json new file mode 100644 index 00000000..b4c44418 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.json @@ -0,0 +1,299 @@ +{ + "classes" : [ + { + "constructor" : { + "abiName" : "bjs_MathUtils_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "methods" : [ + { + "abiName" : "bjs_MathUtils_static_subtract", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "subtract", + "parameters" : [ + { + "label" : "a", + "name" : "a", + "type" : { + "int" : { + + } + } + }, + { + "label" : "b", + "name" : "b", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + }, + "staticContext" : { + "className" : { + "_0" : "MathUtils" + } + } + }, + { + "abiName" : "bjs_MathUtils_static_add", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "add", + "parameters" : [ + { + "label" : "a", + "name" : "a", + "type" : { + "int" : { + + } + } + }, + { + "label" : "b", + "name" : "b", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + }, + "staticContext" : { + "className" : { + "_0" : "MathUtils" + } + } + }, + { + "abiName" : "bjs_MathUtils_multiply", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "multiply", + "parameters" : [ + { + "label" : "x", + "name" : "x", + "type" : { + "int" : { + + } + } + }, + { + "label" : "y", + "name" : "y", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + } + } + ], + "name" : "MathUtils", + "properties" : [ + + ], + "swiftCallName" : "MathUtils" + } + ], + "enums" : [ + { + "cases" : [ + { + "associatedValues" : [ + + ], + "name" : "scientific" + }, + { + "associatedValues" : [ + + ], + "name" : "basic" + } + ], + "emitStyle" : "const", + "name" : "Calculator", + "staticMethods" : [ + { + "abiName" : "bjs_Calculator_static_square", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "square", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + }, + "staticContext" : { + "enumName" : { + "_0" : "Calculator" + } + } + } + ], + "swiftCallName" : "Calculator" + }, + { + "cases" : [ + { + "associatedValues" : [ + { + "type" : { + "string" : { + + } + } + } + ], + "name" : "success" + }, + { + "associatedValues" : [ + { + "type" : { + "int" : { + + } + } + } + ], + "name" : "failure" + } + ], + "emitStyle" : "const", + "name" : "APIResult", + "staticMethods" : [ + { + "abiName" : "bjs_APIResult_static_roundtrip", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "roundtrip", + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + } + } + ], + "returnType" : { + "associatedValueEnum" : { + "_0" : "APIResult" + } + }, + "staticContext" : { + "enumName" : { + "_0" : "APIResult" + } + } + } + ], + "swiftCallName" : "APIResult" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "String", + "namespace" : [ + "Utils" + ], + "staticMethods" : [ + { + "abiName" : "bjs_Utils_String_uppercase", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "uppercase", + "parameters" : [ + { + "label" : "_", + "name" : "text", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "string" : { + + } + }, + "staticContext" : { + "namespaceEnum" : { + "_0" : "Utils.String" + } + } + } + ], + "swiftCallName" : "Utils.String" + } + ], + "functions" : [ + + ], + "moduleName" : "TestModule" +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.swift new file mode 100644 index 00000000..36923e4a --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.swift @@ -0,0 +1,166 @@ +// 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`. + +@_spi(BridgeJS) import JavaScriptKit + +extension Calculator: _BridgedSwiftCaseEnum { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { + return bridgeJSRawValue + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> Calculator { + return Calculator(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> Calculator { + return Calculator(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 { + return bridgeJSRawValue + } + + private init?(bridgeJSRawValue: Int32) { + switch bridgeJSRawValue { + case 0: + self = .scientific + case 1: + self = .basic + default: + return nil + } + } + + private var bridgeJSRawValue: Int32 { + switch self { + case .scientific: + return 0 + case .basic: + return 1 + } + } +} + +@_expose(wasm, "bjs_Calculator_static_square") +@_cdecl("bjs_Calculator_static_square") +public func _bjs_Calculator_static_square(value: Int32) -> Int32 { + #if arch(wasm32) + let ret = Calculator.square(value: Int.bridgeJSLiftParameter(value)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +extension APIResult: _BridgedSwiftAssociatedValueEnum { + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ caseId: Int32) -> APIResult { + switch caseId { + case 0: + return .success(String.bridgeJSLiftParameter(_swift_js_pop_param_int32(), _swift_js_pop_param_int32())) + case 1: + return .failure(Int.bridgeJSLiftParameter(_swift_js_pop_param_int32())) + default: + fatalError("Unknown APIResult case ID: \(caseId)") + } + } + + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() { + switch self { + case .success(let param0): + _swift_js_push_tag(Int32(0)) + var __bjs_param0 = param0 + __bjs_param0.withUTF8 { ptr in + _swift_js_push_string(ptr.baseAddress, Int32(ptr.count)) + } + case .failure(let param0): + _swift_js_push_tag(Int32(1)) + _swift_js_push_int(Int32(param0)) + } + } +} + +@_expose(wasm, "bjs_APIResult_static_roundtrip") +@_cdecl("bjs_APIResult_static_roundtrip") +public func _bjs_APIResult_static_roundtrip(value: Int32) -> Void { + #if arch(wasm32) + let ret = APIResult.roundtrip(value: APIResult.bridgeJSLiftParameter(value)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_Utils_String_uppercase") +@_cdecl("bjs_Utils_String_uppercase") +public func _bjs_Utils_String_uppercase(textBytes: Int32, textLength: Int32) -> Void { + #if arch(wasm32) + let ret = Utils.String.uppercase(_: String.bridgeJSLiftParameter(textBytes, textLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_MathUtils_init") +@_cdecl("bjs_MathUtils_init") +public func _bjs_MathUtils_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = MathUtils() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_MathUtils_static_subtract") +@_cdecl("bjs_MathUtils_static_subtract") +public func _bjs_MathUtils_static_subtract(a: Int32, b: Int32) -> Int32 { + #if arch(wasm32) + let ret = MathUtils.subtract(a: Int.bridgeJSLiftParameter(a), b: Int.bridgeJSLiftParameter(b)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_MathUtils_static_add") +@_cdecl("bjs_MathUtils_static_add") +public func _bjs_MathUtils_static_add(a: Int32, b: Int32) -> Int32 { + #if arch(wasm32) + let ret = MathUtils.add(a: Int.bridgeJSLiftParameter(a), b: Int.bridgeJSLiftParameter(b)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_MathUtils_multiply") +@_cdecl("bjs_MathUtils_multiply") +public func _bjs_MathUtils_multiply(_self: UnsafeMutableRawPointer, x: Int32, y: Int32) -> Int32 { + #if arch(wasm32) + let ret = MathUtils.bridgeJSLiftParameter(_self).multiply(x: Int.bridgeJSLiftParameter(x), y: Int.bridgeJSLiftParameter(y)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_MathUtils_deinit") +@_cdecl("bjs_MathUtils_deinit") +public func _bjs_MathUtils_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension MathUtils: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "TestModule", name: "bjs_MathUtils_wrap") + func _bjs_MathUtils_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_MathUtils_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_MathUtils_wrap(Unmanaged.passRetained(self).toOpaque())))) + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.json index 24d7641c..55972aa1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.json @@ -10,6 +10,7 @@ "abiName" : "bjs_checkString", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "checkString", @@ -34,6 +35,7 @@ "abiName" : "bjs_roundtripString", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtripString", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringReturn.json index 75439e36..5d727c85 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringReturn.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringReturn.json @@ -10,6 +10,7 @@ "abiName" : "bjs_checkString", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "checkString", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json index ce506f51..e838f59a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json @@ -5,6 +5,7 @@ "abiName" : "bjs_Greeter_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -24,6 +25,7 @@ "abiName" : "bjs_Greeter_greet", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "greet", @@ -40,6 +42,7 @@ "abiName" : "bjs_Greeter_changeName", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "changeName", @@ -106,6 +109,7 @@ "abiName" : "bjs_takeGreeter", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "takeGreeter", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Throws.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Throws.json index cc3184fb..51d548e6 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Throws.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Throws.json @@ -10,6 +10,7 @@ "abiName" : "bjs_throwsSomething", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : true }, "name" : "throwsSomething", diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/VoidParameterVoidReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/VoidParameterVoidReturn.json index 413fe084..f3f7abaa 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/VoidParameterVoidReturn.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/VoidParameterVoidReturn.json @@ -10,6 +10,7 @@ "abiName" : "bjs_check", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "check", diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md index c19b8c52..10e4d89c 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md @@ -67,4 +67,5 @@ This command will: - - - +- - diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md index d91d0616..a9a23a59 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md @@ -89,6 +89,6 @@ export type Exports = { | Computed properties: `var x: X { get set }` | ✅ | | Computed properties with effects: `var x: X { get async throws }` | 🚧 | | Methods: `func` | ✅ (See ) | -| Static/class methods: `static func`, `class func` | 🚧 | +| Static/class methods: `static func`, `class func` | ✅ (See ) | | Subscripts: `subscript()` | ❌ | | Generics | ❌ | diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Functions.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Functions.md new file mode 100644 index 00000000..46ee6102 --- /dev/null +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Functions.md @@ -0,0 +1,189 @@ +# Exporting Swift Static Functions to JS + +Learn how to export Swift static and class functions as JavaScript static methods using BridgeJS. + +## Overview + +> Tip: You can quickly preview what interfaces will be exposed on the Swift/TypeScript sides using the [BridgeJS Playground](https://swiftwasm.org/JavaScriptKit/PlayBridgeJS/). + +BridgeJS supports exporting Swift `static func` and `class func` to JavaScript static methods. Both generate identical JavaScript output but differ in Swift inheritance behavior. + +> Important: Static function support is **Swift → JavaScript only**. JavaScript → Swift static function imports are not currently supported. + +## Class Static Functions + +Classes can export both `static` and `class` functions: + +```swift +@JS class MathUtils { + @JS init() {} + + @JS static func add(a: Int, b: Int) -> Int { + return a + b + } + + @JS class func subtract(a: Int, b: Int) -> Int { + return a - b + } + + @JS func multiply(x: Int, y: Int) -> Int { + return x * y + } +} +``` + +JavaScript usage: + +```javascript +// Static methods - no instance needed +const sum = MathUtils.add(5, 3); +const difference = MathUtils.subtract(10, 4); + +// Instance method - requires instance +const utils = new MathUtils(); +const product = utils.multiply(4, 7); +``` + +Generated TypeScript definitions: + +```typescript +export type Exports = { + MathUtils: { + new(): MathUtils; + subtract(a: number, b: number): number; + add(a: number, b: number): number; + } +} + +export interface MathUtils extends SwiftHeapObject { + multiply(x: number, y: number): number; +} +``` + +## Enum Static Functions + +Enums can contain static functions that are exported as properties: + +```swift +@JS enum Calculator { + case scientific + case basic + + @JS static func square(value: Int) -> Int { + return value * value + } + + @JS static func cube(value: Int) -> Int { + return value * value * value + } +} +``` + +Generated JavaScript: + +```javascript +export const Calculator = { + Scientific: 0, + Basic: 1, + square: null, + cube: null, +}; + +// Later assigned: +Calculator.square = function(value) { + const ret = instance.exports.bjs_Calculator_static_square(value); + return ret; +} +Calculator.cube = function(value) { + const ret = instance.exports.bjs_Calculator_static_cube(value); + return ret; +} +``` + +JavaScript usage: + +```javascript +const squared = Calculator.square(5); +const cubed = Calculator.cube(3); +``` + +Generated TypeScript definitions: + +```typescript +export const Calculator: { + readonly Scientific: 0; + readonly Basic: 1; + square(value: number): number; + cube(value: number): number; +}; +export type Calculator = typeof Calculator[keyof typeof Calculator]; +``` + +## Namespace Enum Static Functions + +Namespace enums organize related utility functions and are assigned to `globalThis`: + +```swift +@JS enum Utils { + @JS enum String { + @JS static func uppercase(_ text: String) -> String { + return text.uppercased() + } + } +} +``` + +Generated JavaScript: + +```javascript +// Function exported in exports object +const exports = { + uppercase: function bjs_Utils_String_uppercase(text) { + const textBytes = textEncoder.encode(text); + const textId = swift.memory.retain(textBytes); + instance.exports.bjs_Utils_String_uppercase(textId, textBytes.length); + const ret = tmpRetString; + tmpRetString = undefined; + swift.memory.release(textId); + return ret; + }, +}; + +// Then assigned to globalThis +if (typeof globalThis.Utils === 'undefined') { + globalThis.Utils = {}; +} +if (typeof globalThis.Utils.String === 'undefined') { + globalThis.Utils.String = {}; +} +globalThis.Utils.String.uppercase = exports.uppercase; +``` + +JavaScript usage: + +```javascript +const upper = Utils.String.uppercase("hello"); +``` + +Generated TypeScript definitions: + +```typescript +declare global { + namespace Utils { + namespace String { + uppercase(text: string): string; + } + } +} +``` + +## Supported Features + +| Swift Static Function Feature | Status | +|:------------------------------|:-------| +| Class `static func` | ✅ | +| Class `class func` | ✅ | +| Enum `static func` | ✅ | +| Namespace enum `static func` | ✅ | +| Generic static functions | ❌ | +| Protocol static requirements | ❌ | \ No newline at end of file diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index 7e2b586e..e985b7a2 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -672,6 +672,34 @@ enum APIOptionalResult { return "willSet:\(PropertyHolder.willSetCallCount),didSet:\(PropertyHolder.didSetCallCount),willSetOld:\(PropertyHolder.lastWillSetOldValue),willSetNew:\(PropertyHolder.lastWillSetNewValue),didSetOld:\(PropertyHolder.lastDidSetOldValue),didSetNew:\(PropertyHolder.lastDidSetNewValue)" } + +// MARK: - Static Functions +@JS class MathUtils { + @JS static func add(a: Int, b: Int) -> Int { + return a + b + } + @JS class func substract(a: Int, b: Int) -> Int { + return a - b + } +} + +@JS enum StaticCalculator { + case scientific + case basic + + @JS static func roundtrip(_ value: Int) -> Int { + return value + } +} + +@JS enum StaticUtils { + @JS enum Nested { + @JS static func roundtrip(_ value: String) -> String { + return value + } + } +} + class ExportAPITests: XCTestCase { func testAll() { var hasDeinitGreeter = false diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift index b629cda4..59ca3830 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift @@ -488,6 +488,63 @@ extension APIOptionalResult: _BridgedSwiftAssociatedValueEnum { } } +extension StaticCalculator: _BridgedSwiftCaseEnum { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { + return bridgeJSRawValue + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> StaticCalculator { + return StaticCalculator(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> StaticCalculator { + return StaticCalculator(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 { + return bridgeJSRawValue + } + + private init?(bridgeJSRawValue: Int32) { + switch bridgeJSRawValue { + case 0: + self = .scientific + case 1: + self = .basic + default: + return nil + } + } + + private var bridgeJSRawValue: Int32 { + switch self { + case .scientific: + return 0 + case .basic: + return 1 + } + } +} + +@_expose(wasm, "bjs_StaticCalculator_static_roundtrip") +@_cdecl("bjs_StaticCalculator_static_roundtrip") +public func _bjs_StaticCalculator_static_roundtrip(value: Int32) -> Int32 { + #if arch(wasm32) + let ret = StaticCalculator.roundtrip(_: Int.bridgeJSLiftParameter(value)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticUtils_Nested_roundtrip") +@_cdecl("bjs_StaticUtils_Nested_roundtrip") +public func _bjs_StaticUtils_Nested_roundtrip(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + let ret = StaticUtils.Nested.roundtrip(_: String.bridgeJSLiftParameter(valueBytes, valueLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_roundTripVoid") @_cdecl("bjs_roundTripVoid") public func _bjs_roundTripVoid() -> Void { @@ -2407,4 +2464,46 @@ extension PropertyHolder: ConvertibleToJSValue, _BridgedSwiftHeapObject { #endif return .object(JSObject(id: UInt32(bitPattern: _bjs_PropertyHolder_wrap(Unmanaged.passRetained(self).toOpaque())))) } +} + +@_expose(wasm, "bjs_MathUtils_static_add") +@_cdecl("bjs_MathUtils_static_add") +public func _bjs_MathUtils_static_add(a: Int32, b: Int32) -> Int32 { + #if arch(wasm32) + let ret = MathUtils.add(a: Int.bridgeJSLiftParameter(a), b: Int.bridgeJSLiftParameter(b)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_MathUtils_static_substract") +@_cdecl("bjs_MathUtils_static_substract") +public func _bjs_MathUtils_static_substract(a: Int32, b: Int32) -> Int32 { + #if arch(wasm32) + let ret = MathUtils.substract(a: Int.bridgeJSLiftParameter(a), b: Int.bridgeJSLiftParameter(b)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_MathUtils_deinit") +@_cdecl("bjs_MathUtils_deinit") +public func _bjs_MathUtils_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension MathUtils: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_MathUtils_wrap") + func _bjs_MathUtils_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_MathUtils_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_MathUtils_wrap(Unmanaged.passRetained(self).toOpaque())))) + } } \ No newline at end of file diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json index def3e68d..9fa074c3 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -5,6 +5,7 @@ "abiName" : "bjs_Greeter_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -24,6 +25,7 @@ "abiName" : "bjs_Greeter_greet", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "greet", @@ -40,6 +42,7 @@ "abiName" : "bjs_Greeter_changeName", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "changeName", @@ -90,6 +93,7 @@ "abiName" : "bjs_Calculator_square", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "square", @@ -114,6 +118,7 @@ "abiName" : "bjs_Calculator_add", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "add", @@ -188,6 +193,7 @@ "abiName" : "bjs_Converter_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -199,6 +205,7 @@ "abiName" : "bjs_Converter_toString", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "toString", @@ -234,6 +241,7 @@ "abiName" : "bjs_HTTPServer_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -245,6 +253,7 @@ "abiName" : "bjs_HTTPServer_call", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "call", @@ -281,6 +290,7 @@ "abiName" : "bjs_TestServer_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -292,6 +302,7 @@ "abiName" : "bjs_TestServer_call", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "call", @@ -329,6 +340,7 @@ "abiName" : "bjs_OptionalPropertyHolder_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -399,6 +411,7 @@ "abiName" : "bjs_SimplePropertyHolder_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -435,6 +448,7 @@ "abiName" : "bjs_PropertyHolder_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -508,6 +522,7 @@ "abiName" : "bjs_PropertyHolder_getAllValues", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getAllValues", @@ -669,6 +684,93 @@ } ], "swiftCallName" : "PropertyHolder" + }, + { + "methods" : [ + { + "abiName" : "bjs_MathUtils_static_add", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "add", + "parameters" : [ + { + "label" : "a", + "name" : "a", + "type" : { + "int" : { + + } + } + }, + { + "label" : "b", + "name" : "b", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + }, + "staticContext" : { + "className" : { + "_0" : "MathUtils" + } + } + }, + { + "abiName" : "bjs_MathUtils_static_substract", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "substract", + "parameters" : [ + { + "label" : "a", + "name" : "a", + "type" : { + "int" : { + + } + } + }, + { + "label" : "b", + "name" : "b", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + }, + "staticContext" : { + "className" : { + "_0" : "MathUtils" + } + } + } + ], + "name" : "MathUtils", + "properties" : [ + + ], + "swiftCallName" : "MathUtils" } ], "enums" : [ @@ -701,6 +803,9 @@ ], "emitStyle" : "const", "name" : "Direction", + "staticMethods" : [ + + ], "swiftCallName" : "Direction" }, { @@ -726,6 +831,9 @@ ], "emitStyle" : "const", "name" : "Status", + "staticMethods" : [ + + ], "swiftCallName" : "Status" }, { @@ -755,6 +863,9 @@ "emitStyle" : "const", "name" : "Theme", "rawType" : "String", + "staticMethods" : [ + + ], "swiftCallName" : "Theme" }, { @@ -784,6 +895,9 @@ "emitStyle" : "const", "name" : "HttpStatus", "rawType" : "Int", + "staticMethods" : [ + + ], "swiftCallName" : "HttpStatus" }, { @@ -815,6 +929,9 @@ ], "emitStyle" : "tsEnum", "name" : "TSDirection", + "staticMethods" : [ + + ], "swiftCallName" : "TSDirection" }, { @@ -844,6 +961,9 @@ "emitStyle" : "tsEnum", "name" : "TSTheme", "rawType" : "String", + "staticMethods" : [ + + ], "swiftCallName" : "TSTheme" }, { @@ -852,6 +972,9 @@ ], "emitStyle" : "const", "name" : "Utils", + "staticMethods" : [ + + ], "swiftCallName" : "Utils" }, { @@ -886,6 +1009,9 @@ "namespace" : [ "Networking", "API" + ], + "staticMethods" : [ + ], "swiftCallName" : "Networking.API.Method" }, @@ -926,6 +1052,9 @@ "Configuration" ], "rawType" : "String", + "staticMethods" : [ + + ], "swiftCallName" : "Configuration.LogLevel" }, { @@ -958,6 +1087,9 @@ "Configuration" ], "rawType" : "Int", + "staticMethods" : [ + + ], "swiftCallName" : "Configuration.Port" }, { @@ -981,6 +1113,9 @@ "Networking", "APIV2", "Internal" + ], + "staticMethods" : [ + ], "swiftCallName" : "Internal.SupportedMethod" }, @@ -1055,6 +1190,9 @@ ], "emitStyle" : "const", "name" : "APIResult", + "staticMethods" : [ + + ], "swiftCallName" : "APIResult" }, { @@ -1245,6 +1383,9 @@ ], "emitStyle" : "const", "name" : "ComplexResult", + "staticMethods" : [ + + ], "swiftCallName" : "ComplexResult" }, { @@ -1311,6 +1452,9 @@ "name" : "Result", "namespace" : [ "Utilities" + ], + "staticMethods" : [ + ], "swiftCallName" : "Utilities.Result" }, @@ -1352,6 +1496,9 @@ "name" : "NetworkingResult", "namespace" : [ "API" + ], + "staticMethods" : [ + ], "swiftCallName" : "API.NetworkingResult" }, @@ -1441,7 +1588,104 @@ ], "emitStyle" : "const", "name" : "APIOptionalResult", + "staticMethods" : [ + + ], "swiftCallName" : "APIOptionalResult" + }, + { + "cases" : [ + { + "associatedValues" : [ + + ], + "name" : "scientific" + }, + { + "associatedValues" : [ + + ], + "name" : "basic" + } + ], + "emitStyle" : "const", + "name" : "StaticCalculator", + "staticMethods" : [ + { + "abiName" : "bjs_StaticCalculator_static_roundtrip", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "roundtrip", + "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + }, + "staticContext" : { + "enumName" : { + "_0" : "StaticCalculator" + } + } + } + ], + "swiftCallName" : "StaticCalculator" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Nested", + "namespace" : [ + "StaticUtils" + ], + "staticMethods" : [ + { + "abiName" : "bjs_StaticUtils_Nested_roundtrip", + "effects" : { + "isAsync" : false, + "isStatic" : true, + "isThrows" : false + }, + "name" : "roundtrip", + "parameters" : [ + { + "label" : "_", + "name" : "value", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "string" : { + + } + }, + "staticContext" : { + "namespaceEnum" : { + "_0" : "StaticUtils.Nested" + } + } + } + ], + "swiftCallName" : "StaticUtils.Nested" } ], "functions" : [ @@ -1449,6 +1693,7 @@ "abiName" : "bjs_roundTripVoid", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripVoid", @@ -1465,6 +1710,7 @@ "abiName" : "bjs_roundTripInt", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripInt", @@ -1489,6 +1735,7 @@ "abiName" : "bjs_roundTripFloat", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripFloat", @@ -1513,6 +1760,7 @@ "abiName" : "bjs_roundTripDouble", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripDouble", @@ -1537,6 +1785,7 @@ "abiName" : "bjs_roundTripBool", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripBool", @@ -1561,6 +1810,7 @@ "abiName" : "bjs_roundTripString", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripString", @@ -1585,6 +1835,7 @@ "abiName" : "bjs_roundTripSwiftHeapObject", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripSwiftHeapObject", @@ -1609,6 +1860,7 @@ "abiName" : "bjs_roundTripJSObject", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripJSObject", @@ -1633,6 +1885,7 @@ "abiName" : "bjs_throwsSwiftError", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : true }, "name" : "throwsSwiftError", @@ -1657,6 +1910,7 @@ "abiName" : "bjs_throwsWithIntResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : true }, "name" : "throwsWithIntResult", @@ -1673,6 +1927,7 @@ "abiName" : "bjs_throwsWithStringResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : true }, "name" : "throwsWithStringResult", @@ -1689,6 +1944,7 @@ "abiName" : "bjs_throwsWithBoolResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : true }, "name" : "throwsWithBoolResult", @@ -1705,6 +1961,7 @@ "abiName" : "bjs_throwsWithFloatResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : true }, "name" : "throwsWithFloatResult", @@ -1721,6 +1978,7 @@ "abiName" : "bjs_throwsWithDoubleResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : true }, "name" : "throwsWithDoubleResult", @@ -1737,6 +1995,7 @@ "abiName" : "bjs_throwsWithSwiftHeapObjectResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : true }, "name" : "throwsWithSwiftHeapObjectResult", @@ -1753,6 +2012,7 @@ "abiName" : "bjs_throwsWithJSObjectResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : true }, "name" : "throwsWithJSObjectResult", @@ -1769,6 +2029,7 @@ "abiName" : "bjs_asyncRoundTripVoid", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripVoid", @@ -1785,6 +2046,7 @@ "abiName" : "bjs_asyncRoundTripInt", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripInt", @@ -1809,6 +2071,7 @@ "abiName" : "bjs_asyncRoundTripFloat", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripFloat", @@ -1833,6 +2096,7 @@ "abiName" : "bjs_asyncRoundTripDouble", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripDouble", @@ -1857,6 +2121,7 @@ "abiName" : "bjs_asyncRoundTripBool", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripBool", @@ -1881,6 +2146,7 @@ "abiName" : "bjs_asyncRoundTripString", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripString", @@ -1905,6 +2171,7 @@ "abiName" : "bjs_asyncRoundTripSwiftHeapObject", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripSwiftHeapObject", @@ -1929,6 +2196,7 @@ "abiName" : "bjs_asyncRoundTripJSObject", "effects" : { "isAsync" : true, + "isStatic" : false, "isThrows" : false }, "name" : "asyncRoundTripJSObject", @@ -1953,6 +2221,7 @@ "abiName" : "bjs_takeGreeter", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "takeGreeter", @@ -1986,6 +2255,7 @@ "abiName" : "bjs_createCalculator", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "createCalculator", @@ -2002,6 +2272,7 @@ "abiName" : "bjs_useCalculator", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "useCalculator", @@ -2044,6 +2315,7 @@ "abiName" : "bjs_testGreeterToJSValue", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "testGreeterToJSValue", @@ -2060,6 +2332,7 @@ "abiName" : "bjs_testCalculatorToJSValue", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "testCalculatorToJSValue", @@ -2076,6 +2349,7 @@ "abiName" : "bjs_testSwiftClassAsJSValue", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "testSwiftClassAsJSValue", @@ -2100,6 +2374,7 @@ "abiName" : "bjs_setDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setDirection", @@ -2124,6 +2399,7 @@ "abiName" : "bjs_getDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getDirection", @@ -2140,6 +2416,7 @@ "abiName" : "bjs_processDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "processDirection", @@ -2164,6 +2441,7 @@ "abiName" : "bjs_setTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setTheme", @@ -2190,6 +2468,7 @@ "abiName" : "bjs_getTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getTheme", @@ -2207,6 +2486,7 @@ "abiName" : "bjs_setHttpStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setHttpStatus", @@ -2233,6 +2513,7 @@ "abiName" : "bjs_getHttpStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getHttpStatus", @@ -2250,6 +2531,7 @@ "abiName" : "bjs_processTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "processTheme", @@ -2276,6 +2558,7 @@ "abiName" : "bjs_setTSDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setTSDirection", @@ -2300,6 +2583,7 @@ "abiName" : "bjs_getTSDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getTSDirection", @@ -2316,6 +2600,7 @@ "abiName" : "bjs_setTSTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "setTSTheme", @@ -2342,6 +2627,7 @@ "abiName" : "bjs_getTSTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getTSTheme", @@ -2359,6 +2645,7 @@ "abiName" : "bjs_roundtripNetworkingAPIMethod", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtripNetworkingAPIMethod", @@ -2383,6 +2670,7 @@ "abiName" : "bjs_roundtripConfigurationLogLevel", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtripConfigurationLogLevel", @@ -2409,6 +2697,7 @@ "abiName" : "bjs_roundtripConfigurationPort", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtripConfigurationPort", @@ -2435,6 +2724,7 @@ "abiName" : "bjs_processConfigurationLogLevel", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "processConfigurationLogLevel", @@ -2461,6 +2751,7 @@ "abiName" : "bjs_roundtripInternalSupportedMethod", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtripInternalSupportedMethod", @@ -2485,6 +2776,7 @@ "abiName" : "bjs_roundtripAPIResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtripAPIResult", @@ -2509,6 +2801,7 @@ "abiName" : "bjs_makeAPIResultSuccess", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeAPIResultSuccess", @@ -2533,6 +2826,7 @@ "abiName" : "bjs_makeAPIResultFailure", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeAPIResultFailure", @@ -2557,6 +2851,7 @@ "abiName" : "bjs_makeAPIResultInfo", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeAPIResultInfo", @@ -2573,6 +2868,7 @@ "abiName" : "bjs_makeAPIResultFlag", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeAPIResultFlag", @@ -2597,6 +2893,7 @@ "abiName" : "bjs_makeAPIResultRate", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeAPIResultRate", @@ -2621,6 +2918,7 @@ "abiName" : "bjs_makeAPIResultPrecise", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeAPIResultPrecise", @@ -2645,6 +2943,7 @@ "abiName" : "bjs_roundtripComplexResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtripComplexResult", @@ -2669,6 +2968,7 @@ "abiName" : "bjs_makeComplexResultSuccess", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeComplexResultSuccess", @@ -2693,6 +2993,7 @@ "abiName" : "bjs_makeComplexResultError", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeComplexResultError", @@ -2726,6 +3027,7 @@ "abiName" : "bjs_makeComplexResultLocation", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeComplexResultLocation", @@ -2768,6 +3070,7 @@ "abiName" : "bjs_makeComplexResultStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeComplexResultStatus", @@ -2810,6 +3113,7 @@ "abiName" : "bjs_makeComplexResultCoordinates", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeComplexResultCoordinates", @@ -2852,6 +3156,7 @@ "abiName" : "bjs_makeComplexResultComprehensive", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeComplexResultComprehensive", @@ -2948,6 +3253,7 @@ "abiName" : "bjs_makeComplexResultInfo", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeComplexResultInfo", @@ -2964,6 +3270,7 @@ "abiName" : "bjs_makeUtilitiesResultSuccess", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeUtilitiesResultSuccess", @@ -2988,6 +3295,7 @@ "abiName" : "bjs_makeUtilitiesResultFailure", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeUtilitiesResultFailure", @@ -3021,6 +3329,7 @@ "abiName" : "bjs_makeUtilitiesResultStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeUtilitiesResultStatus", @@ -3063,6 +3372,7 @@ "abiName" : "bjs_makeAPINetworkingResultSuccess", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeAPINetworkingResultSuccess", @@ -3087,6 +3397,7 @@ "abiName" : "bjs_makeAPINetworkingResultFailure", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeAPINetworkingResultFailure", @@ -3120,6 +3431,7 @@ "abiName" : "bjs_roundtripUtilitiesResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtripUtilitiesResult", @@ -3144,6 +3456,7 @@ "abiName" : "bjs_roundtripAPINetworkingResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtripAPINetworkingResult", @@ -3168,6 +3481,7 @@ "abiName" : "bjs_roundTripOptionalString", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalString", @@ -3200,6 +3514,7 @@ "abiName" : "bjs_roundTripOptionalInt", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalInt", @@ -3232,6 +3547,7 @@ "abiName" : "bjs_roundTripOptionalBool", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalBool", @@ -3264,6 +3580,7 @@ "abiName" : "bjs_roundTripOptionalFloat", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalFloat", @@ -3296,6 +3613,7 @@ "abiName" : "bjs_roundTripOptionalDouble", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalDouble", @@ -3328,6 +3646,7 @@ "abiName" : "bjs_roundTripOptionalSyntax", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalSyntax", @@ -3360,6 +3679,7 @@ "abiName" : "bjs_roundTripOptionalMixSyntax", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalMixSyntax", @@ -3392,6 +3712,7 @@ "abiName" : "bjs_roundTripOptionalSwiftSyntax", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalSwiftSyntax", @@ -3424,6 +3745,7 @@ "abiName" : "bjs_roundTripOptionalWithSpaces", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalWithSpaces", @@ -3456,6 +3778,7 @@ "abiName" : "bjs_roundTripOptionalTypeAlias", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalTypeAlias", @@ -3488,6 +3811,7 @@ "abiName" : "bjs_roundTripOptionalStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalStatus", @@ -3520,6 +3844,7 @@ "abiName" : "bjs_roundTripOptionalTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalTheme", @@ -3554,6 +3879,7 @@ "abiName" : "bjs_roundTripOptionalHttpStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalHttpStatus", @@ -3588,6 +3914,7 @@ "abiName" : "bjs_roundTripOptionalTSDirection", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalTSDirection", @@ -3620,6 +3947,7 @@ "abiName" : "bjs_roundTripOptionalTSTheme", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalTSTheme", @@ -3654,6 +3982,7 @@ "abiName" : "bjs_roundTripOptionalNetworkingAPIMethod", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalNetworkingAPIMethod", @@ -3686,6 +4015,7 @@ "abiName" : "bjs_roundTripOptionalAPIResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalAPIResult", @@ -3718,6 +4048,7 @@ "abiName" : "bjs_roundTripOptionalComplexResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalComplexResult", @@ -3750,6 +4081,7 @@ "abiName" : "bjs_roundTripOptionalClass", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalClass", @@ -3782,6 +4114,7 @@ "abiName" : "bjs_roundTripOptionalAPIOptionalResult", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundTripOptionalAPIOptionalResult", @@ -3814,6 +4147,7 @@ "abiName" : "bjs_createPropertyHolder", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "createPropertyHolder", @@ -3883,6 +4217,7 @@ "abiName" : "bjs_testPropertyHolder", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "testPropertyHolder", @@ -3907,6 +4242,7 @@ "abiName" : "bjs_resetObserverCounts", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "resetObserverCounts", @@ -3923,6 +4259,7 @@ "abiName" : "bjs_getObserverStats", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "getObserverStats", diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 039c721e..e8a86582 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -1,7 +1,7 @@ // @ts-check import { - Direction, Status, Theme, HttpStatus, TSDirection, TSTheme, APIResult, ComplexResult, APIOptionalResult + Direction, Status, Theme, HttpStatus, TSDirection, TSTheme, APIResult, ComplexResult, APIOptionalResult, StaticCalculator } from '../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.js'; /** @type {import('../.build/plugins/PackageToJS/outputs/PackageTests/test.d.ts').SetupOptionsFn} */ @@ -551,6 +551,13 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor9), aor9); assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor10), aor10); assert.equal(exports.roundTripOptionalAPIOptionalResult(null), null); + + assert.equal(exports.MathUtils.add(2147483647, 0), 2147483647); + assert.equal(StaticCalculator.roundtrip(42), 42); + assert.equal(StaticCalculator.Scientific, 0); + assert.equal(StaticCalculator.Basic, 1); + + assert.equal(globalThis.StaticUtils.Nested.roundtrip("hello world"), "hello world"); } /** @param {import('./../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */ From 7fe9b5b86fc90ff64f80d0ee1015ca6d5f770c06 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Wed, 17 Sep 2025 05:52:13 -0600 Subject: [PATCH 2/7] BridgeJS: Basic properties setup --- .../Sources/BridgeJSCore/ExportSwift.swift | 256 ++++++-- .../Sources/BridgeJSLink/BridgeJSLink.swift | 479 +++++++++++--- .../Sources/BridgeJSLink/JSGlueGen.swift | 26 +- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 55 +- .../Inputs/StaticProperties.swift | 42 ++ .../EnumAssociatedValue.Export.js | 19 +- .../BridgeJSLinkTests/EnumNamespace.Export.js | 13 +- .../BridgeJSLinkTests/Namespaces.Export.js | 34 +- .../StaticFunctions.Export.js | 18 +- .../StaticProperties.Export.d.ts | 59 ++ .../StaticProperties.Export.js | 332 ++++++++++ .../ExportSwiftTests/EnumAssociatedValue.json | 29 + .../ExportSwiftTests/EnumCase.json | 12 + .../ExportSwiftTests/EnumNamespace.json | 78 +++ .../ExportSwiftTests/EnumRawType.json | 36 ++ .../ExportSwiftTests/Optionals.json | 4 + .../ExportSwiftTests/PropertyTypes.json | 16 + .../ExportSwiftTests/StaticFunctions.json | 23 + .../ExportSwiftTests/StaticProperties.json | 312 +++++++++ .../ExportSwiftTests/StaticProperties.swift | 338 ++++++++++ .../ExportSwiftTests/SwiftClass.json | 1 + .../BridgeJS/Exporting-Swift-to-JavaScript.md | 1 + .../Exporting-Swift-Static-Properties.md | 189 ++++++ .../BridgeJSRuntimeTests/ExportAPITests.swift | 76 +++ .../Generated/BridgeJS.ExportSwift.swift | 479 ++++++++++++++ .../JavaScript/BridgeJS.ExportSwift.json | 606 ++++++++++++++++++ Tests/prelude.mjs | 84 ++- 27 files changed, 3429 insertions(+), 188 deletions(-) create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticProperties.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.d.ts create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.json create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.swift create mode 100644 Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Properties.md diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 4b049341..b6642fed 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -94,8 +94,39 @@ public class ExportSwift { var cases: [EnumCase] = [] var rawType: String? var staticMethods: [ExportedFunction] = [] + var staticProperties: [ExportedProperty] = [] } - var currentEnum = CurrentEnum() + + struct EnumStack { + private var stack: [CurrentEnum] = [] + + var current: CurrentEnum { + get { + return stack.last ?? CurrentEnum() + } + set { + if stack.isEmpty { + stack.append(newValue) + } else { + stack[stack.count - 1] = newValue + } + } + } + + mutating func push(_ enumData: CurrentEnum) { + stack.append(enumData) + } + + mutating func pop() -> CurrentEnum? { + return stack.popLast() + } + + var isEmpty: Bool { + return stack.isEmpty + } + } + + var enumStack = EnumStack() enum State { case topLevel @@ -188,7 +219,9 @@ public class ExportSwift { return .skipChildren } if let exportedFunction = visitFunction(node: node, isStatic: isStatic, enumName: enumName) { - currentEnum.staticMethods.append(exportedFunction) + var current = enumStack.current + current.staticMethods.append(exportedFunction) + enumStack.current = current } return .skipChildren } @@ -281,7 +314,7 @@ public class ExportSwift { return nil } - let isNamespaceEnum = currentEnum.cases.isEmpty + let isNamespaceEnum = enumStack.current.cases.isEmpty if isNamespaceEnum { // For namespace enums, compute the full Swift call path manually @@ -429,8 +462,49 @@ public class ExportSwift { override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind { guard node.attributes.hasJSAttribute() else { return .skipChildren } - guard case .classBody(_, let classKey) = state else { - diagnose(node: node, message: "@JS var must be inside a @JS class") + + let isStatic = node.modifiers.contains { modifier in + modifier.name.tokenKind == .keyword(.static) || + modifier.name.tokenKind == .keyword(.class) + } + + let staticContext: StaticContext? + let classKey: String + + switch state { + case .classBody(let className, let key): + classKey = key + if isStatic { + staticContext = .className(className) + } else { + staticContext = nil + } + case .enumBody(let enumName): + if !isStatic { + diagnose(node: node, message: "Only static properties are supported in enums") + return .skipChildren + } + classKey = enumName // We'll handle enum properties differently + + let isNamespaceEnum = enumStack.current.cases.isEmpty + if isNamespaceEnum { + var swiftPath: [String] = [] + var currentNode: Syntax? = node.parent + while let parent = currentNode { + if let enumDecl = parent.as(EnumDeclSyntax.self), + enumDecl.attributes.hasJSAttribute() + { + swiftPath.insert(enumDecl.name.text, at: 0) + } + currentNode = parent.parent + } + let fullEnumCallName = swiftPath.joined(separator: ".") + staticContext = .namespaceEnum(fullEnumCallName) + } else { + staticContext = .enumName(enumName) + } + case .topLevel: + diagnose(node: node, message: "@JS var must be inside a @JS class or enum") return .skipChildren } @@ -487,10 +561,18 @@ public class ExportSwift { let exportedProperty = ExportedProperty( name: propertyName, type: propertyType, - isReadonly: isReadonly + isReadonly: isReadonly, + isStatic: isStatic, + staticContext: staticContext ) - exportedClassByName[classKey]?.properties.append(exportedProperty) + if case .enumBody(_) = state { + var current = enumStack.current + current.staticProperties.append(exportedProperty) + enumStack.current = current + } else { + exportedClassByName[classKey]?.properties.append(exportedProperty) + } } return .skipChildren @@ -574,9 +656,14 @@ public class ExportSwift { return .skipChildren } - currentEnum.name = name - currentEnum.cases = [] - currentEnum.rawType = rawType + let newEnum = CurrentEnum() + enumStack.push(newEnum) + + var current = enumStack.current + current.name = name + current.cases = [] + current.rawType = rawType + enumStack.current = current stateStack.push(state: .enumBody(name: name)) @@ -585,11 +672,12 @@ public class ExportSwift { override func visitPost(_ node: EnumDeclSyntax) { guard let jsAttribute = node.attributes.firstJSAttribute, - let enumName = currentEnum.name + let enumName = enumStack.current.name else { // Only pop if we have a valid enum that was processed if case .enumBody(_) = stateStack.current { stateStack.pop() + _ = enumStack.pop() // Also pop the enum stack } return } @@ -607,7 +695,7 @@ public class ExportSwift { let emitStyle = extractEnumStyle(from: jsAttribute) ?? .const if case .tsEnum = emitStyle { - if let raw = currentEnum.rawType, + if let raw = enumStack.current.rawType, let rawEnum = SwiftEnumRawType.from(raw), rawEnum == .bool { diagnose( @@ -617,7 +705,8 @@ public class ExportSwift { ) } - if !currentEnum.staticMethods.isEmpty { + if !enumStack.current.staticMethods.isEmpty { + diagnose( node: jsAttribute, message: "TypeScript enum style does not support static functions", @@ -626,7 +715,7 @@ public class ExportSwift { } } - if currentEnum.cases.contains(where: { !$0.associatedValues.isEmpty }) { + if enumStack.current.cases.contains(where: { !$0.associatedValues.isEmpty }) { if case .tsEnum = emitStyle { diagnose( node: jsAttribute, @@ -634,7 +723,7 @@ public class ExportSwift { hint: "Use enumStyle: .const in order to map associated-value enums" ) } - for enumCase in currentEnum.cases { + for enumCase in enumStack.current.cases { for associatedValue in enumCase.associatedValues { switch associatedValue.type { case .string, .int, .float, .double, .bool: @@ -668,20 +757,22 @@ public class ExportSwift { for: node, message: "Enum visibility must be at least internal" ) + let currentEnumData = enumStack.current let exportedEnum = ExportedEnum( name: enumName, swiftCallName: swiftCallName, explicitAccessControl: explicitAccessControl, - cases: currentEnum.cases, - rawType: currentEnum.rawType, + cases: currentEnumData.cases, + rawType: currentEnumData.rawType, namespace: effectiveNamespace, emitStyle: emitStyle, - staticMethods: currentEnum.staticMethods + staticMethods: currentEnumData.staticMethods, + staticProperties: currentEnumData.staticProperties ) exportedEnumByName[enumName] = exportedEnum exportedEnumNames.append(enumName) - currentEnum = CurrentEnum() + _ = enumStack.pop() // Pop the completed enum from stack stateStack.pop() } @@ -691,7 +782,7 @@ public class ExportSwift { let rawValue: String? var associatedValues: [AssociatedValue] = [] - if currentEnum.rawType != nil { + if enumStack.current.rawType != nil { if let stringLiteral = element.rawValue?.value.as(StringLiteralExprSyntax.self) { rawValue = stringLiteral.segments.first?.as(StringSegmentSyntax.self)?.content.text } else if let boolLiteral = element.rawValue?.value.as(BooleanLiteralExprSyntax.self) { @@ -727,7 +818,9 @@ public class ExportSwift { associatedValues: associatedValues ) - currentEnum.cases.append(enumCase) + var current = enumStack.current + current.cases.append(enumCase) + enumStack.current = current } return .visitChildren @@ -945,6 +1038,43 @@ public class ExportSwift { for staticMethod in enumDef.staticMethods { decls.append(try renderSingleExportedFunction(function: staticMethod)) } + + for staticProperty in enumDef.staticProperties { + let getterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false, isStatic: true)) + + let staticCallName: String + if let staticContext = staticProperty.staticContext { + switch staticContext { + case .className(let className): + staticCallName = "\(className).\(staticProperty.name)" + case .enumName(let enumName): + staticCallName = "\(enumName).\(staticProperty.name)" + case .namespaceEnum(let enumName): + staticCallName = "\(enumName).\(staticProperty.name)" + case .explicitNamespace(let namespace): + staticCallName = "\(namespace.joined(separator: ".")).\(staticProperty.name)" + } + } else { + staticCallName = "\(enumDef.swiftCallName).\(staticProperty.name)" + } + + getterBuilder.callStaticProperty(name: staticCallName, returnType: staticProperty.type) + try getterBuilder.lowerReturnValue(returnType: staticProperty.type) + decls.append(getterBuilder.render(abiName: staticProperty.getterAbiName(className: enumDef.name))) + + if !staticProperty.isReadonly { + let setterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false, isStatic: true)) + try setterBuilder.liftParameter( + param: Parameter(label: "value", name: "value", type: staticProperty.type) + ) + setterBuilder.callStaticPropertySetter( + klassName: staticCallName.components(separatedBy: ".").dropLast().joined(separator: "."), + propertyName: staticProperty.name + ) + try setterBuilder.lowerReturnValue(returnType: .void) + decls.append(setterBuilder.render(abiName: staticProperty.setterAbiName(className: enumDef.name))) + } + } } for function in exportedFunctions { @@ -1048,6 +1178,14 @@ public class ExportSwift { append(item) } + func callStaticProperty(name: String, returnType: BridgeType) { + if returnType == .void { + append("let ret = \(raw: name)") + } else { + append("let ret = \(raw: name)") + } + } + func callMethod(klassName: String, methodName: String, returnType: BridgeType) { let (_, selfExpr) = removeFirstLiftedParameter() let item = renderCallStatement( @@ -1072,6 +1210,11 @@ public class ExportSwift { append("\(raw: selfExpr).\(raw: propertyName) = \(raw: newValueExpr)") } + func callStaticPropertySetter(klassName: String, propertyName: String) { + let (_, newValueExpr) = removeFirstLiftedParameter() + append("\(raw: klassName).\(raw: propertyName) = \(raw: newValueExpr)") + } + func lowerReturnValue(returnType: BridgeType) throws { if effects.isAsync { // Async functions always return a Promise, which is a JSObject @@ -1460,34 +1603,57 @@ public class ExportSwift { // Generate property getters and setters for property in klass.properties { - // Generate getter - let getterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) - try getterBuilder.liftParameter( - param: Parameter(label: nil, name: "_self", type: .swiftHeapObject(klass.name)) - ) - getterBuilder.callPropertyGetter( - klassName: klass.name, - propertyName: property.name, - returnType: property.type - ) - try getterBuilder.lowerReturnValue(returnType: property.type) - decls.append(getterBuilder.render(abiName: property.getterAbiName(className: klass.name))) - - // Generate setter if property is not readonly - if !property.isReadonly { - let setterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) - try setterBuilder.liftParameter( + if property.isStatic { + // Generate static property getter + let getterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false, isStatic: true)) + let staticCallName = "\(klass.swiftCallName).\(property.name)" + getterBuilder.callStaticProperty(name: staticCallName, returnType: property.type) + try getterBuilder.lowerReturnValue(returnType: property.type) + decls.append(getterBuilder.render(abiName: property.getterAbiName(className: klass.name))) + + // Generate static property setter if not readonly + if !property.isReadonly { + let setterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false, isStatic: true)) + try setterBuilder.liftParameter( + param: Parameter(label: "value", name: "value", type: property.type) + ) + setterBuilder.callStaticPropertySetter( + klassName: klass.swiftCallName, + propertyName: property.name + ) + try setterBuilder.lowerReturnValue(returnType: .void) + decls.append(setterBuilder.render(abiName: property.setterAbiName(className: klass.name))) + } + } else { + // Generate instance property getter + let getterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) + try getterBuilder.liftParameter( param: Parameter(label: nil, name: "_self", type: .swiftHeapObject(klass.name)) ) - try setterBuilder.liftParameter( - param: Parameter(label: "value", name: "value", type: property.type) - ) - setterBuilder.callPropertySetter( + getterBuilder.callPropertyGetter( klassName: klass.name, - propertyName: property.name + propertyName: property.name, + returnType: property.type ) - try setterBuilder.lowerReturnValue(returnType: .void) - decls.append(setterBuilder.render(abiName: property.setterAbiName(className: klass.name))) + try getterBuilder.lowerReturnValue(returnType: property.type) + decls.append(getterBuilder.render(abiName: property.getterAbiName(className: klass.name))) + + // Generate instance property setter if not readonly + if !property.isReadonly { + let setterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) + try setterBuilder.liftParameter( + param: Parameter(label: nil, name: "_self", type: .swiftHeapObject(klass.name)) + ) + try setterBuilder.liftParameter( + param: Parameter(label: "value", name: "value", type: property.type) + ) + setterBuilder.callPropertySetter( + klassName: klass.name, + propertyName: property.name + ) + try setterBuilder.lowerReturnValue(returnType: .void) + decls.append(setterBuilder.render(abiName: property.setterAbiName(className: klass.name))) + } } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 741f6158..789188a1 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -182,25 +182,24 @@ struct BridgeJSLink { data.exportsLines.append(contentsOf: js) data.dtsExportLines.append(contentsOf: dts) } + + for property in enumDefinition.staticProperties { + let fullNamespace = (enumDefinition.namespace ?? []) + [enumDefinition.name] + let namespacePath = fullNamespace.joined(separator: ".") + let (propJs, _) = try renderNamespaceStaticProperty(property: property, namespacePath: namespacePath) + data.enumStaticAssignments.append(contentsOf: propJs) + } } for enumDefinition in skeleton.enums where enumDefinition.enumType != .namespace { for function in enumDefinition.staticMethods { - let (funcJs, _) = try renderEnumStaticFunction(function: function, enumName: enumDefinition.name) - var assignmentJs = funcJs - if !assignmentJs.isEmpty && assignmentJs.last?.hasSuffix(",") == true { - assignmentJs[assignmentJs.count - 1] = String(assignmentJs.last?.dropLast() ?? "") - } - if !assignmentJs.isEmpty { - let firstLine = assignmentJs[0] - if firstLine.contains("(") { - assignmentJs[0] = - "\(enumDefinition.name).\(function.name) = function" - + firstLine.dropFirst(function.name.count) - } - } + let assignmentJs = try renderEnumStaticFunctionAssignment(function: function, enumName: enumDefinition.name) data.enumStaticAssignments.append(contentsOf: assignmentJs) } + for property in enumDefinition.staticProperties { + let (propJs, _) = try renderEnumStaticProperty(property: property, enumName: enumDefinition.name) + data.enumStaticAssignments.append(contentsOf: propJs) + } } } @@ -558,7 +557,8 @@ struct BridgeJSLink { // Namespace assignments section let namespaceBuilder = NamespaceBuilder() let topLevelNamespaceCode = namespaceBuilder.renderTopLevelEnumNamespaceAssignments( - namespacedEnums: data.namespacedEnums + namespacedEnums: data.namespacedEnums, + exportedSkeletons: exportedSkeletons ) printer.write(lines: topLevelNamespaceCode) @@ -620,21 +620,79 @@ struct BridgeJSLink { // Exports / return section let hasNamespacedItems = !data.namespacedFunctions.isEmpty || !data.namespacedClasses.isEmpty + let hasNamespacedEnums = !data.namespacedEnums.isEmpty + let hasAnyNamespacedItems = hasNamespacedItems || hasNamespacedEnums printer.write(lines: data.classLines) - printer.write(lines: data.enumStaticAssignments) - if hasNamespacedItems { + // CRITICAL: Initialize ALL namespaces BEFORE any property assignments + if hasAnyNamespacedItems { + var allUniqueNamespaces: [String] = [] + var seen = Set() + + let functionNamespacePaths: Set<[String]> = Set( + data.namespacedFunctions.compactMap { $0.namespace } + ) + let classNamespacePaths: Set<[String]> = Set( + data.namespacedClasses.compactMap { $0.namespace } + ) + let allRegularNamespacePaths = functionNamespacePaths.union(classNamespacePaths) + + let enumNamespacePaths: Set<[String]> = Set( + data.namespacedEnums.compactMap { $0.namespace } + ) + + var namespaceEnumPropertyPaths: Set<[String]> = Set() + for skeleton in exportedSkeletons { + for enumDefinition in skeleton.enums { + for property in enumDefinition.staticProperties { + if case .namespaceEnum(let fullNamespacePath) = property.staticContext { + let namespaceParts = fullNamespacePath.split(separator: ".").map(String.init) + namespaceEnumPropertyPaths.insert(namespaceParts) + } + } + } + } + + let allNamespacePaths = allRegularNamespacePaths.union(enumNamespacePaths).union(namespaceEnumPropertyPaths) + + allNamespacePaths.forEach { namespacePath in + namespacePath.enumerated().forEach { (index, _) in + let path = namespacePath[0...index].joined(separator: ".") + if seen.insert(path).inserted { + allUniqueNamespaces.append(path) + } + } + } + + allUniqueNamespaces.sorted().forEach { namespace in + printer.write("if (typeof globalThis.\(namespace) === 'undefined') {") + printer.indent { + printer.write("globalThis.\(namespace) = {};") + } + printer.write("}") + } + } + + // NOW assign enum static function/property implementations (namespaces are ready) + printer.write(lines: data.enumStaticAssignments) + + if hasAnyNamespacedItems { printer.write("const exports = {") printer.indent { printer.write(lines: data.exportsLines) } printer.write("};") - let namespaceSetupCode = namespaceBuilder.renderGlobalNamespace( - namespacedFunctions: data.namespacedFunctions, - namespacedClasses: data.namespacedClasses - ) - printer.write(lines: namespaceSetupCode) + + data.namespacedClasses.forEach { klass in + let namespacePath: String = klass.namespace?.joined(separator: ".") ?? "" + printer.write("globalThis.\(namespacePath).\(klass.name) = exports.\(klass.name);") + } + + data.namespacedFunctions.forEach { function in + let namespacePath: String = function.namespace?.joined(separator: ".") ?? "" + printer.write("globalThis.\(namespacePath).\(function.name) = exports.\(function.name);") + } printer.write("return exports;") } else { @@ -872,9 +930,7 @@ struct BridgeJSLink { case .simple: let fragment = IntrinsicJSFragment.simpleEnumHelper(enumDefinition: enumDefinition) _ = fragment.printCode([enumDefinition.name], scope, printer, cleanup) - jsLines.append(contentsOf: printer.lines) - try addStaticFunctionsToEnum(enumDefinition: enumDefinition, jsLines: &jsLines, dtsLines: &dtsLines) case .rawValue: guard enumDefinition.rawType != nil else { throw BridgeJSLinkError(message: "Raw value enum \(enumDefinition.name) is missing rawType") @@ -882,15 +938,11 @@ struct BridgeJSLink { let fragment = IntrinsicJSFragment.rawValueEnumHelper(enumDefinition: enumDefinition) _ = fragment.printCode([enumDefinition.name], scope, printer, cleanup) - jsLines.append(contentsOf: printer.lines) - try addStaticFunctionsToEnum(enumDefinition: enumDefinition, jsLines: &jsLines, dtsLines: &dtsLines) case .associatedValue: let fragment = IntrinsicJSFragment.associatedValueEnumHelper(enumDefinition: enumDefinition) _ = fragment.printCode([enumDefinition.name], scope, printer, cleanup) - jsLines.append(contentsOf: printer.lines) - try addStaticFunctionsToEnum(enumDefinition: enumDefinition, jsLines: &jsLines, dtsLines: &dtsLines) case .namespace: break } @@ -902,42 +954,6 @@ struct BridgeJSLink { return (jsLines, dtsLines) } - private func addStaticFunctionsToEnum( - enumDefinition: ExportedEnum, - jsLines: inout [String], - dtsLines: inout [String] - ) throws { - let enumStaticFunctions = enumDefinition.staticMethods - - guard !enumStaticFunctions.isEmpty else { return } - - let enumName = enumDefinition.name - - if !jsLines.isEmpty { - for i in 0.. [String] { let printer = CodeFragmentPrinter() @@ -991,6 +1007,10 @@ struct BridgeJSLink { ) printer.write("\(function.name)\(signature);") } + for property in enumDefinition.staticProperties { + let readonly = property.isReadonly ? "readonly " : "" + printer.write("\(readonly)\(property.name): \(property.type.tsType);") + } } printer.write("};") printer.write( @@ -1016,6 +1036,10 @@ struct BridgeJSLink { ) printer.write("\(function.name)\(signature);") } + for property in enumDefinition.staticProperties { + let readonly = property.isReadonly ? "readonly " : "" + printer.write("\(readonly)\(property.name): \(property.type.tsType);") + } } printer.write("};") printer.nextLine() @@ -1178,6 +1202,172 @@ extension BridgeJSLink { return (funcLines, dtsLines) } + private func renderEnumStaticFunctionAssignment( + function: ExportedFunction, + enumName: String + ) throws -> [String] { + let thunkBuilder = ExportedThunkBuilder(effects: function.effects) + for param in function.parameters { + try thunkBuilder.lowerParameter(param: param) + } + let returnExpr = try thunkBuilder.call(abiName: function.abiName, returnType: function.returnType) + + let printer = CodeFragmentPrinter() + printer.write("\(enumName).\(function.name) = function(\(function.parameters.map { $0.name }.joined(separator: ", "))) {") + printer.indent { + printer.write(contentsOf: thunkBuilder.body) + printer.write(contentsOf: thunkBuilder.cleanupCode) + printer.write(lines: thunkBuilder.checkExceptionLines()) + if let returnExpr = returnExpr { + printer.write("return \(returnExpr);") + } + } + printer.write("};") + + return printer.lines + } + + /// Renders an enum static property as getter/setter assignments on the enum object + private func renderEnumStaticProperty( + property: ExportedProperty, + enumName: String + ) throws -> (js: [String], dts: [String]) { + var jsLines: [String] = [] + + // Generate getter assignment + let getterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) + let getterReturnExpr = try getterThunkBuilder.call( + abiName: property.getterAbiName(className: enumName), + returnType: property.type + ) + + let getterLines = getterThunkBuilder.renderFunction( + name: property.name, + parameters: [], + returnExpr: getterReturnExpr, + declarationPrefixKeyword: nil + ) + + // Build Object.defineProperty call + var definePropertyLines: [String] = [] + definePropertyLines.append("Object.defineProperty(\(enumName), '\(property.name)', { get: function() {") + + // Add getter body (skip function declaration and closing brace) + if getterLines.count > 2 { + let bodyLines = Array(getterLines[1.. 2 { + let bodyLines = Array(setterLines[1.. (js: [String], dts: [String]) { + var jsLines: [String] = [] + + // Generate getter assignment + let getterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) + // Use the last component of namespace as the className for ABI name generation + let className = namespacePath.components(separatedBy: ".").last ?? namespacePath + let getterReturnExpr = try getterThunkBuilder.call( + abiName: property.getterAbiName(className: className), + returnType: property.type + ) + + let getterLines = getterThunkBuilder.renderFunction( + name: property.name, + parameters: [], + returnExpr: getterReturnExpr, + declarationPrefixKeyword: nil + ) + + // Build Object.defineProperty call for namespace + var definePropertyLines: [String] = [] + definePropertyLines.append("Object.defineProperty(globalThis.\(namespacePath), '\(property.name)', { get: function() {") + + // Add getter body (skip function declaration and closing brace) + if getterLines.count > 2 { + let bodyLines = Array(getterLines[1.. 2 { + let bodyLines = Array(setterLines[1.. (js: [String], dtsType: [String], dtsExportEntry: [String]) { @@ -1279,53 +1469,102 @@ extension BridgeJSLink { // Generate property getters and setters for property in klass.properties { - // Generate getter - let getterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) - getterThunkBuilder.lowerSelf() - let getterReturnExpr = try getterThunkBuilder.call( - abiName: property.getterAbiName(className: klass.name), - returnType: property.type - ) + if property.isStatic { + // Generate static property getter + let getterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) + let getterReturnExpr = try getterThunkBuilder.call( + abiName: property.getterAbiName(className: klass.name), + returnType: property.type + ) - jsPrinter.indent { - jsPrinter.write( - lines: getterThunkBuilder.renderFunction( - name: property.name, - parameters: [], - returnExpr: getterReturnExpr, - declarationPrefixKeyword: "get" + jsPrinter.indent { + jsPrinter.write( + lines: getterThunkBuilder.renderFunction( + name: property.name, + parameters: [], + returnExpr: getterReturnExpr, + declarationPrefixKeyword: "static get" + ) ) - ) - } + } - // Generate setter if not readonly - if !property.isReadonly { - let setterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) - setterThunkBuilder.lowerSelf() - try setterThunkBuilder.lowerParameter( - param: Parameter(label: "value", name: "value", type: property.type) - ) - _ = try setterThunkBuilder.call( - abiName: property.setterAbiName(className: klass.name), - returnType: .void + // Generate static property setter if not readonly + if !property.isReadonly { + let setterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) + try setterThunkBuilder.lowerParameter( + param: Parameter(label: "value", name: "value", type: property.type) + ) + _ = try setterThunkBuilder.call( + abiName: property.setterAbiName(className: klass.name), + returnType: .void + ) + + jsPrinter.indent { + jsPrinter.write( + lines: setterThunkBuilder.renderFunction( + name: property.name, + parameters: [.init(label: nil, name: "value", type: property.type)], + returnExpr: nil, + declarationPrefixKeyword: "static set" + ) + ) + } + } + + // Add static property to TypeScript exports definition (not instance interface) + let readonly = property.isReadonly ? "readonly " : "" + dtsExportEntryPrinter.indent { + dtsExportEntryPrinter.write("\(readonly)\(property.name): \(property.type.tsType);") + } + } else { + // Generate instance property getter + let getterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) + getterThunkBuilder.lowerSelf() + let getterReturnExpr = try getterThunkBuilder.call( + abiName: property.getterAbiName(className: klass.name), + returnType: property.type ) jsPrinter.indent { jsPrinter.write( - lines: setterThunkBuilder.renderFunction( + lines: getterThunkBuilder.renderFunction( name: property.name, - parameters: [.init(label: nil, name: "value", type: property.type)], - returnExpr: nil, - declarationPrefixKeyword: "set" + parameters: [], + returnExpr: getterReturnExpr, + declarationPrefixKeyword: "get" ) ) } - } - // Add TypeScript property definition - let readonly = property.isReadonly ? "readonly " : "" - dtsTypePrinter.indent { - dtsTypePrinter.write("\(readonly)\(property.name): \(property.type.tsType);") + // Generate instance property setter if not readonly + if !property.isReadonly { + let setterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) + setterThunkBuilder.lowerSelf() + try setterThunkBuilder.lowerParameter( + param: Parameter(label: "value", name: "value", type: property.type) + ) + _ = try setterThunkBuilder.call( + abiName: property.setterAbiName(className: klass.name), + returnType: .void + ) + + jsPrinter.indent { + jsPrinter.write( + lines: setterThunkBuilder.renderFunction( + name: property.name, + parameters: [.init(label: nil, name: "value", type: property.type)], + returnExpr: nil, + declarationPrefixKeyword: "set" + ) + ) + } + } + + // Add instance property to TypeScript interface definition + let readonly = property.isReadonly ? "readonly " : "" + dtsTypePrinter.indent { + dtsTypePrinter.write("\(readonly)\(property.name): \(property.type.tsType);") + } } } @@ -1564,7 +1803,10 @@ extension BridgeJSLink { return printer.lines } - func renderTopLevelEnumNamespaceAssignments(namespacedEnums: [ExportedEnum]) -> [String] { + func renderTopLevelEnumNamespaceAssignments( + namespacedEnums: [ExportedEnum], + exportedSkeletons: [ExportedSkeleton] + ) -> [String] { guard !namespacedEnums.isEmpty else { return [] } let printer = CodeFragmentPrinter() @@ -1582,6 +1824,23 @@ extension BridgeJSLink { } } + for skeleton in exportedSkeletons { + for enumDefinition in skeleton.enums { + for property in enumDefinition.staticProperties { + if case .namespaceEnum(let fullNamespacePath) = property.staticContext { + let namespaceParts = fullNamespacePath.split(separator: ".").map(String.init) + namespaceParts.enumerated().forEach { (index, _) in + let path = namespaceParts[0...index].joined(separator: ".") + if !seen.contains(path) { + seen.insert(path) + uniqueNamespaces.append(path) + } + } + } + } + } + } + for namespace in uniqueNamespaces { printer.write("if (typeof globalThis.\(namespace) === 'undefined') {") printer.indent { @@ -1595,6 +1854,10 @@ extension BridgeJSLink { } for enumDef in namespacedEnums { + if enumDef.enumType == .namespace { + continue + } + let namespacePath = enumDef.namespace?.joined(separator: ".") ?? "" printer.write("globalThis.\(namespacePath).\(enumDef.name) = \(enumDef.name);") } @@ -1606,6 +1869,7 @@ extension BridgeJSLink { var functions: [ExportedFunction] = [] var classes: [ExportedClass] = [] var enums: [ExportedEnum] = [] + var staticProperties: [ExportedProperty] = [] } private final class NamespaceNode { @@ -1693,6 +1957,18 @@ extension BridgeJSLink { } currentNode.content.functions.append(function) } + + // Add static properties to namespace content for TypeScript declarations + for property in enumDefinition.staticProperties { + var currentNode = rootNode + let fullNamespace = (enumDefinition.namespace ?? []) + [enumDefinition.name] + for part in fullNamespace { + currentNode = currentNode.addChild(part) + } + if !currentNode.content.staticProperties.contains(where: { $0.name == property.name }) { + currentNode.content.staticProperties.append(property) + } + } } } } @@ -1835,6 +2111,11 @@ extension BridgeJSLink { "\(function.name)\(renderTSSignatureCallback(function.parameters, function.returnType, function.effects));" printer.write(signature) } + let sortedProperties = childNode.content.staticProperties.sorted { $0.name < $1.name } + for property in sortedProperties { + let readonly = property.isReadonly ? "var " : "let " + printer.write("\(readonly)\(property.name): \(property.type.tsType);") + } generateNamespaceDeclarations(node: childNode, depth: depth + 1) diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index ad07774d..921ee92f 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -536,7 +536,15 @@ struct IntrinsicJSFragment: Sendable { printer.write("\(caseName): \(index),") } } - printer.write("}") + printer.write("},") + + for function in enumDefinition.staticMethods { + printer.write("\(function.name): null,") + } + + for property in enumDefinition.staticProperties { + printer.write("\(property.name): null,") + } } printer.write("};") printer.nextLine() @@ -625,6 +633,14 @@ struct IntrinsicJSFragment: Sendable { let caseName = enumCase.name.capitalizedFirstLetter printer.write("\(caseName): \(index),") } + + for function in enumDefinition.staticMethods { + printer.write("\(function.name): null,") + } + + for property in enumDefinition.staticProperties { + printer.write("\(property.name): null,") + } } printer.write("};") printer.nextLine() @@ -651,6 +667,14 @@ struct IntrinsicJSFragment: Sendable { printer.write("\(caseName): \(formattedValue),") } + + for function in enumDefinition.staticMethods { + printer.write("\(function.name): null,") + } + + for property in enumDefinition.staticProperties { + printer.write("\(property.name): null,") + } } printer.write("};") printer.nextLine() diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index c7f7d128..86fa6998 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -136,6 +136,7 @@ public struct ExportedEnum: Codable, Equatable, Sendable { public let namespace: [String]? public let emitStyle: EnumEmitStyle public var staticMethods: [ExportedFunction] + public var staticProperties: [ExportedProperty] = [] public var enumType: EnumType { if cases.isEmpty { return .namespace @@ -154,7 +155,8 @@ public struct ExportedEnum: Codable, Equatable, Sendable { rawType: String?, namespace: [String]?, emitStyle: EnumEmitStyle, - staticMethods: [ExportedFunction] = [] + staticMethods: [ExportedFunction] = [], + staticProperties: [ExportedProperty] = [] ) { self.name = name self.swiftCallName = swiftCallName @@ -164,6 +166,7 @@ public struct ExportedEnum: Codable, Equatable, Sendable { self.namespace = namespace self.emitStyle = emitStyle self.staticMethods = staticMethods + self.staticProperties = staticProperties } } @@ -246,23 +249,65 @@ public struct ExportedConstructor: Codable { } } -public struct ExportedProperty: Codable { +public struct ExportedProperty: Codable, Equatable, Sendable { public var name: String public var type: BridgeType public var isReadonly: Bool + public var isStatic: Bool = false + public var staticContext: StaticContext? - public init(name: String, type: BridgeType, isReadonly: Bool = false) { + public init(name: String, type: BridgeType, isReadonly: Bool = false, isStatic: Bool = false, staticContext: StaticContext? = nil) { self.name = name self.type = type self.isReadonly = isReadonly + self.isStatic = isStatic + self.staticContext = staticContext } public func getterAbiName(className: String) -> String { - return "bjs_\(className)_\(name)_get" + if isStatic, let staticContext = staticContext { + // Generate context-aware ABI names for static properties + switch staticContext { + case .className(let className): + return "bjs_\(className)_static_\(name)_get" + case .enumName(let enumName): + return "bjs_\(enumName)_static_\(name)_get" + case .namespaceEnum(let enumName): + // Convert dots to underscores for namespace enums + let abiEnumName = enumName.split(separator: ".").joined(separator: "_") + return "bjs_\(abiEnumName)_static_\(name)_get" + case .explicitNamespace(let namespace): + let abiNamespace = namespace.joined(separator: "_") + return "bjs_\(abiNamespace)_static_\(name)_get" + } + } else if isStatic { + return "bjs_\(className)_static_\(name)_get" + } else { + return "bjs_\(className)_\(name)_get" + } } public func setterAbiName(className: String) -> String { - return "bjs_\(className)_\(name)_set" + if isStatic, let staticContext = staticContext { + // Generate context-aware ABI names for static properties + switch staticContext { + case .className(let className): + return "bjs_\(className)_static_\(name)_set" + case .enumName(let enumName): + return "bjs_\(enumName)_static_\(name)_set" + case .namespaceEnum(let enumName): + // Convert dots to underscores for namespace enums + let abiEnumName = enumName.split(separator: ".").joined(separator: "_") + return "bjs_\(abiEnumName)_static_\(name)_set" + case .explicitNamespace(let namespace): + let abiNamespace = namespace.joined(separator: "_") + return "bjs_\(abiNamespace)_static_\(name)_set" + } + } else if isStatic { + return "bjs_\(className)_static_\(name)_set" + } else { + return "bjs_\(className)_\(name)_set" + } } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticProperties.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticProperties.swift new file mode 100644 index 00000000..656b4da0 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticProperties.swift @@ -0,0 +1,42 @@ +@JS class PropertyClass { + @JS init() {} + + @JS static let staticConstant: String = "constant" + @JS static var staticVariable: Int = 42 + @JS static var jsObjectProperty: JSObject = JSObject() + @JS class var classVariable: String = "overridable" + + @JS static var computedProperty: String { + get { return "\(staticVariable) computed" } + set { staticVariable = newValue + 5 } + } + + @JS static var readOnlyComputed: Int { + return 100 + } + + @JS static var optionalProperty: String? = nil +} + +@JS enum PropertyEnum { + case value1 + case value2 + + @JS static var enumProperty: String = "enum value" + @JS static let enumConstant: Int = 42 + @JS static var computedEnum: String { + get { return enumProperty + " computed" } + set { enumProperty = newValue + " computed" } + } +} + +@JS enum PropertyNamespace { + @JS static var namespaceProperty: String = "namespace" + @JS static let namespaceConstant: String = "constant" + + @JS enum Nested { + @JS nonisolated(unsafe) static var nestedProperty: Int = 999 + @JS static let nestedConstant: String = "nested" + @JS nonisolated(unsafe) static var nestedDouble: Double = 1.414 + } +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js index f7ca2dd8..6a690945 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js @@ -12,7 +12,7 @@ export const APIResult = { Rate: 3, Precise: 4, Info: 5, - } + }, }; const __bjs_createAPIResultHelpers = () => { @@ -94,7 +94,7 @@ export const ComplexResult = { Coordinates: 3, Comprehensive: 4, Info: 5, - } + }, }; const __bjs_createComplexResultHelpers = () => { @@ -222,7 +222,7 @@ export const Result = { Success: 0, Failure: 1, Status: 2, - } + }, }; const __bjs_createResultHelpers = () => { @@ -293,7 +293,7 @@ export const NetworkingResult = { Tag: { Success: 0, Failure: 1, - } + }, }; const __bjs_createNetworkingResultHelpers = () => { @@ -347,7 +347,7 @@ export const APIOptionalResult = { Success: 0, Failure: 1, Status: 2, - } + }, }; const __bjs_createAPIOptionalResultHelpers = () => { @@ -651,7 +651,13 @@ export async function createInstantiator(options, swift) { /** @param {WebAssembly.Instance} instance */ createExports: (instance) => { const js = swift.memory.heap; - return { + if (typeof globalThis.API === 'undefined') { + globalThis.API = {}; + } + if (typeof globalThis.Utilities === 'undefined') { + globalThis.Utilities = {}; + } + const exports = { handle: function bjs_handle(result) { const { caseId: resultCaseId, cleanup: resultCleanup } = enumHelpers.APIResult.lower(result); instance.exports.bjs_handle(resultCaseId); @@ -782,6 +788,7 @@ export async function createInstantiator(options, swift) { return optResult; }, }; + return exports; }, } } \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js index 0aff338b..224fcf88 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js @@ -273,11 +273,9 @@ export async function createInstantiator(options, swift) { instance.exports.bjs_TestServer_call(this.pointer, method); } } - const exports = { - Converter, - HTTPServer, - TestServer, - }; + if (typeof globalThis.Configuration === 'undefined') { + globalThis.Configuration = {}; + } if (typeof globalThis.Networking === 'undefined') { globalThis.Networking = {}; } @@ -293,6 +291,11 @@ export async function createInstantiator(options, swift) { if (typeof globalThis.Utils === 'undefined') { globalThis.Utils = {}; } + const exports = { + Converter, + HTTPServer, + TestServer, + }; globalThis.Utils.Converter = exports.Converter; globalThis.Networking.API.HTTPServer = exports.HTTPServer; globalThis.Networking.APIV2.Internal.TestServer = exports.TestServer; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js index 4631fb24..ebc55d03 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js @@ -232,23 +232,6 @@ export async function createInstantiator(options, swift) { return ret; } } - const exports = { - Greeter, - Converter, - UUID, - plainFunction: function bjs_plainFunction() { - instance.exports.bjs_plainFunction(); - const ret = tmpRetString; - tmpRetString = undefined; - return ret; - }, - namespacedFunction: function bjs_namespacedFunction() { - instance.exports.bjs_namespacedFunction(); - const ret = tmpRetString; - tmpRetString = undefined; - return ret; - }, - }; if (typeof globalThis.MyModule === 'undefined') { globalThis.MyModule = {}; } @@ -267,6 +250,23 @@ export async function createInstantiator(options, swift) { if (typeof globalThis.__Swift.Foundation === 'undefined') { globalThis.__Swift.Foundation = {}; } + const exports = { + Greeter, + Converter, + UUID, + plainFunction: function bjs_plainFunction() { + instance.exports.bjs_plainFunction(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + }, + namespacedFunction: function bjs_namespacedFunction() { + instance.exports.bjs_namespacedFunction(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + }, + }; globalThis.__Swift.Foundation.Greeter = exports.Greeter; globalThis.Utils.Converters.Converter = exports.Converter; globalThis.__Swift.Foundation.UUID = exports.UUID; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js index 280332dc..f294faa5 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js @@ -14,7 +14,7 @@ export const APIResult = { Tag: { Success: 0, Failure: 1, - } + }, roundtrip: null, }; @@ -256,17 +256,23 @@ export async function createInstantiator(options, swift) { return ret; } } + if (typeof globalThis.Utils === 'undefined') { + globalThis.Utils = {}; + } + if (typeof globalThis.Utils.String === 'undefined') { + globalThis.Utils.String = {}; + } Calculator.square = function(value) { const ret = instance.exports.bjs_Calculator_static_square(value); return ret; - } + }; APIResult.roundtrip = function(value) { const { caseId: valueCaseId, cleanup: valueCleanup } = enumHelpers.APIResult.lower(value); instance.exports.bjs_APIResult_static_roundtrip(valueCaseId); const ret = enumHelpers.APIResult.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s); if (valueCleanup) { valueCleanup(); } return ret; - } + }; const exports = { MathUtils, uppercase: function bjs_Utils_String_uppercase(text) { @@ -279,12 +285,6 @@ export async function createInstantiator(options, swift) { return ret; }, }; - if (typeof globalThis.Utils === 'undefined') { - globalThis.Utils = {}; - } - if (typeof globalThis.Utils.String === 'undefined') { - globalThis.Utils.String = {}; - } globalThis.Utils.String.uppercase = exports.uppercase; return exports; }, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.d.ts new file mode 100644 index 00000000..59c30ca9 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.d.ts @@ -0,0 +1,59 @@ +// 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 const PropertyEnum: { + readonly Value1: 0; + readonly Value2: 1; + enumProperty: string; + readonly enumConstant: number; + computedEnum: string; +}; +export type PropertyEnum = typeof PropertyEnum[keyof typeof PropertyEnum]; + +export {}; + +declare global { + namespace PropertyNamespace { + var namespaceConstant: string; + let namespaceProperty: string; + namespace Nested { + var nestedConstant: string; + let nestedDouble: number; + let nestedProperty: number; + } + } +} + +/// Represents a Swift heap object like a class instance or an actor instance. +export interface SwiftHeapObject { + /// Release the heap object. + /// + /// Note: Calling this method will release the heap object and it will no longer be accessible. + release(): void; +} +export interface PropertyClass extends SwiftHeapObject { +} +export type Exports = { + PropertyClass: { + new(): PropertyClass; + readonly staticConstant: string; + staticVariable: number; + jsObjectProperty: any; + classVariable: string; + computedProperty: string; + readonly readOnlyComputed: number; + optionalProperty: string | null; + } +} +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/StaticProperties.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js new file mode 100644 index 00000000..f02ca0a2 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js @@ -0,0 +1,332 @@ +// 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 const PropertyEnum = { + Value1: 0, + Value2: 1, + enumProperty: null, + enumConstant: null, + computedEnum: null, +}; + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + 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 tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpParamInts = []; + let tmpParamF32s = []; + let tmpParamF64s = []; + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + const bjs = {}; + importObject["bjs"] = bjs; + const imports = options.getImports(importsContext); + bjs["swift_js_return_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + return swift.memory.retain(textDecoder.decode(bytes)); + } + 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_tag"] = function(tag) { + tmpRetTag = tag; + } + bjs["swift_js_push_int"] = function(v) { + tmpRetInts.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + tmpRetF32s.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + const value = textDecoder.decode(bytes); + tmpRetStrings.push(value); + } + bjs["swift_js_pop_param_int32"] = function() { + return tmpParamInts.pop(); + } + bjs["swift_js_pop_param_f32"] = function() { + return tmpParamF32s.pop(); + } + bjs["swift_js_pop_param_f64"] = function() { + return tmpParamF64s.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 { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + 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; + } + } + // Wrapper functions for module: TestModule + if (!importObject["TestModule"]) { + importObject["TestModule"] = {}; + } + importObject["TestModule"]["bjs_PropertyClass_wrap"] = function(pointer) { + const obj = PropertyClass.__construct(pointer); + return swift.memory.retain(obj); + }; + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + /// Represents a Swift heap object like a class instance or an actor instance. + class SwiftHeapObject { + static __wrap(pointer, deinit, prototype) { + const obj = Object.create(prototype); + obj.pointer = pointer; + obj.hasReleased = false; + obj.deinit = deinit; + obj.registry = new FinalizationRegistry((pointer) => { + deinit(pointer); + }); + obj.registry.register(this, obj.pointer); + return obj; + } + + release() { + this.registry.unregister(this); + this.deinit(this.pointer); + } + } + class PropertyClass extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PropertyClass_deinit, PropertyClass.prototype); + } + + constructor() { + const ret = instance.exports.bjs_PropertyClass_init(); + return PropertyClass.__construct(ret); + } + static get staticConstant() { + instance.exports.bjs_PropertyClass_static_staticConstant_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + static get staticVariable() { + const ret = instance.exports.bjs_PropertyClass_static_staticVariable_get(); + return ret; + } + static set staticVariable(value) { + instance.exports.bjs_PropertyClass_static_staticVariable_set(value); + } + static get jsObjectProperty() { + const ret = instance.exports.bjs_PropertyClass_static_jsObjectProperty_get(); + const ret1 = swift.memory.getObject(ret); + swift.memory.release(ret); + return ret1; + } + static set jsObjectProperty(value) { + instance.exports.bjs_PropertyClass_static_jsObjectProperty_set(swift.memory.retain(value)); + } + static get classVariable() { + instance.exports.bjs_PropertyClass_static_classVariable_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + static set classVariable(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_PropertyClass_static_classVariable_set(valueId, valueBytes.length); + swift.memory.release(valueId); + } + static get computedProperty() { + instance.exports.bjs_PropertyClass_static_computedProperty_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + static set computedProperty(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_PropertyClass_static_computedProperty_set(valueId, valueBytes.length); + swift.memory.release(valueId); + } + static get readOnlyComputed() { + const ret = instance.exports.bjs_PropertyClass_static_readOnlyComputed_get(); + return ret; + } + static get optionalProperty() { + instance.exports.bjs_PropertyClass_static_optionalProperty_get(); + const optResult = tmpRetString; + tmpRetString = undefined; + return optResult; + } + static set optionalProperty(value) { + const isSome = value != null; + let valueId, valueBytes; + if (isSome) { + valueBytes = textEncoder.encode(value); + valueId = swift.memory.retain(valueBytes); + } + instance.exports.bjs_PropertyClass_static_optionalProperty_set(+isSome, isSome ? valueId : 0, isSome ? valueBytes.length : 0); + if (valueId != undefined) { + swift.memory.release(valueId); + } + } + } + Object.defineProperty(globalThis.PropertyNamespace.Nested, 'nestedProperty', { get: function() { + const ret = instance.exports.bjs_PropertyNamespace_Nested_static_nestedProperty_get(); + return ret; + }, set: function(value) { + instance.exports.bjs_PropertyNamespace_Nested_static_nestedProperty_set(value); + } }); + Object.defineProperty(globalThis.PropertyNamespace.Nested, 'nestedConstant', { get: function() { + instance.exports.bjs_PropertyNamespace_Nested_static_nestedConstant_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } }); + Object.defineProperty(globalThis.PropertyNamespace.Nested, 'nestedDouble', { get: function() { + const ret = instance.exports.bjs_PropertyNamespace_Nested_static_nestedDouble_get(); + return ret; + }, set: function(value) { + instance.exports.bjs_PropertyNamespace_Nested_static_nestedDouble_set(value); + } }); + Object.defineProperty(globalThis.PropertyNamespace, 'namespaceProperty', { get: function() { + instance.exports.bjs_PropertyNamespace_static_namespaceProperty_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + }, set: function(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_PropertyNamespace_static_namespaceProperty_set(valueId, valueBytes.length); + swift.memory.release(valueId); + } }); + Object.defineProperty(globalThis.PropertyNamespace, 'namespaceConstant', { get: function() { + instance.exports.bjs_PropertyNamespace_static_namespaceConstant_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } }); + Object.defineProperty(PropertyEnum, 'enumProperty', { get: function() { + instance.exports.bjs_PropertyEnum_static_enumProperty_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + }, set: function(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_PropertyEnum_static_enumProperty_set(valueId, valueBytes.length); + swift.memory.release(valueId); + } }); + Object.defineProperty(PropertyEnum, 'enumConstant', { get: function() { + const ret = instance.exports.bjs_PropertyEnum_static_enumConstant_get(); + return ret; + } }); + Object.defineProperty(PropertyEnum, 'computedEnum', { get: function() { + instance.exports.bjs_PropertyEnum_static_computedEnum_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + }, set: function(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_PropertyEnum_static_computedEnum_set(valueId, valueBytes.length); + swift.memory.release(valueId); + } }); + return { + PropertyClass, + }; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json index a5620206..105cd31d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json @@ -76,6 +76,9 @@ "name" : "APIResult", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "APIResult" }, @@ -243,6 +246,9 @@ "name" : "ComplexResult", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "ComplexResult" }, @@ -313,9 +319,26 @@ ], "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Utilities.Result" }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Utilities", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Utilities" + }, { "cases" : [ { @@ -357,6 +380,9 @@ ], "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "NetworkingResult" }, @@ -448,6 +474,9 @@ "name" : "APIOptionalResult", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "APIOptionalResult" } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.json index 3f8ae035..1184b4b0 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumCase.json @@ -34,6 +34,9 @@ "name" : "Direction", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Direction" }, @@ -62,6 +65,9 @@ "name" : "Status", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Status" }, @@ -96,6 +102,9 @@ "name" : "TSDirection", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "TSDirection" }, @@ -113,6 +122,9 @@ "name" : "PublicStatus", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "PublicStatus" } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json index 0ac995ec..8dc3431e 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json @@ -157,6 +157,9 @@ "name" : "Utils", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Utils" }, @@ -195,9 +198,43 @@ ], "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Networking.API.Method" }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "API", + "namespace" : [ + "Networking" + ], + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Networking.API" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Networking", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Networking" + }, { "cases" : [ { @@ -237,6 +274,9 @@ "rawType" : "String", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Configuration.LogLevel" }, @@ -272,9 +312,26 @@ "rawType" : "Int", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Configuration.Port" }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Configuration", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Configuration" + }, { "cases" : [ { @@ -299,8 +356,29 @@ ], "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Internal.SupportedMethod" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Internal", + "namespace" : [ + "Networking", + "APIV2" + ], + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Internal" } ], "functions" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.json index 0711cd29..207fd11a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumRawType.json @@ -32,6 +32,9 @@ "rawType" : "String", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Theme" }, @@ -64,6 +67,9 @@ "rawType" : "String", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "TSTheme" }, @@ -89,6 +95,9 @@ "rawType" : "Bool", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "FeatureFlag" }, @@ -121,6 +130,9 @@ "rawType" : "Int", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "HttpStatus" }, @@ -153,6 +165,9 @@ "rawType" : "Int", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "TSHttpStatus" }, @@ -199,6 +214,9 @@ "rawType" : "Int32", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Priority" }, @@ -238,6 +256,9 @@ "rawType" : "Int64", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "FileSize" }, @@ -270,6 +291,9 @@ "rawType" : "UInt", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "UserId" }, @@ -302,6 +326,9 @@ "rawType" : "UInt32", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "TokenId" }, @@ -334,6 +361,9 @@ "rawType" : "UInt64", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "SessionId" }, @@ -366,6 +396,9 @@ "rawType" : "Float", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Precision" }, @@ -405,6 +438,9 @@ "rawType" : "Double", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Ratio" } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.json index 61a033d5..aef4e890 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Optionals.json @@ -76,6 +76,7 @@ "properties" : [ { "isReadonly" : false, + "isStatic" : false, "name" : "name", "type" : { "optional" : { @@ -109,6 +110,7 @@ "properties" : [ { "isReadonly" : false, + "isStatic" : false, "name" : "optionalName", "type" : { "optional" : { @@ -122,6 +124,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "optionalAge", "type" : { "optional" : { @@ -135,6 +138,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "optionalGreeter", "type" : { "optional" : { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.json index a728578e..8de3a12d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.json @@ -88,6 +88,7 @@ "properties" : [ { "isReadonly" : false, + "isStatic" : false, "name" : "intValue", "type" : { "int" : { @@ -97,6 +98,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "floatValue", "type" : { "float" : { @@ -106,6 +108,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "doubleValue", "type" : { "double" : { @@ -115,6 +118,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "boolValue", "type" : { "bool" : { @@ -124,6 +128,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "stringValue", "type" : { "string" : { @@ -133,6 +138,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "readonlyInt", "type" : { "int" : { @@ -142,6 +148,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "readonlyFloat", "type" : { "float" : { @@ -151,6 +158,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "readonlyDouble", "type" : { "double" : { @@ -160,6 +168,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "readonlyBool", "type" : { "bool" : { @@ -169,6 +178,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "readonlyString", "type" : { "string" : { @@ -178,6 +188,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "jsObject", "type" : { "jsObject" : { @@ -187,6 +198,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "sibling", "type" : { "swiftHeapObject" : { @@ -196,6 +208,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "lazyValue", "type" : { "string" : { @@ -205,6 +218,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "computedReadonly", "type" : { "int" : { @@ -214,6 +228,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "computedReadWrite", "type" : { "string" : { @@ -223,6 +238,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "observedProperty", "type" : { "int" : { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.json index b4c44418..6277494a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.json @@ -182,6 +182,9 @@ } } } + ], + "staticProperties" : [ + ], "swiftCallName" : "Calculator" }, @@ -245,6 +248,9 @@ } } } + ], + "staticProperties" : [ + ], "swiftCallName" : "APIResult" }, @@ -288,8 +294,25 @@ } } } + ], + "staticProperties" : [ + ], "swiftCallName" : "Utils.String" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Utils", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Utils" } ], "functions" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.json new file mode 100644 index 00000000..4b00c7e9 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.json @@ -0,0 +1,312 @@ +{ + "classes" : [ + { + "constructor" : { + "abiName" : "bjs_PropertyClass_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "methods" : [ + + ], + "name" : "PropertyClass", + "properties" : [ + { + "isReadonly" : true, + "isStatic" : true, + "name" : "staticConstant", + "staticContext" : { + "className" : { + "_0" : "PropertyClass" + } + }, + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "staticVariable", + "staticContext" : { + "className" : { + "_0" : "PropertyClass" + } + }, + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "jsObjectProperty", + "staticContext" : { + "className" : { + "_0" : "PropertyClass" + } + }, + "type" : { + "jsObject" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "classVariable", + "staticContext" : { + "className" : { + "_0" : "PropertyClass" + } + }, + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "computedProperty", + "staticContext" : { + "className" : { + "_0" : "PropertyClass" + } + }, + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : true, + "isStatic" : true, + "name" : "readOnlyComputed", + "staticContext" : { + "className" : { + "_0" : "PropertyClass" + } + }, + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "optionalProperty", + "staticContext" : { + "className" : { + "_0" : "PropertyClass" + } + }, + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "swiftCallName" : "PropertyClass" + } + ], + "enums" : [ + { + "cases" : [ + { + "associatedValues" : [ + + ], + "name" : "value1" + }, + { + "associatedValues" : [ + + ], + "name" : "value2" + } + ], + "emitStyle" : "const", + "name" : "PropertyEnum", + "staticMethods" : [ + + ], + "staticProperties" : [ + { + "isReadonly" : false, + "isStatic" : true, + "name" : "enumProperty", + "staticContext" : { + "enumName" : { + "_0" : "PropertyEnum" + } + }, + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : true, + "isStatic" : true, + "name" : "enumConstant", + "staticContext" : { + "enumName" : { + "_0" : "PropertyEnum" + } + }, + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "computedEnum", + "staticContext" : { + "enumName" : { + "_0" : "PropertyEnum" + } + }, + "type" : { + "string" : { + + } + } + } + ], + "swiftCallName" : "PropertyEnum" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Nested", + "namespace" : [ + "PropertyNamespace" + ], + "staticMethods" : [ + + ], + "staticProperties" : [ + { + "isReadonly" : false, + "isStatic" : true, + "name" : "nestedProperty", + "staticContext" : { + "namespaceEnum" : { + "_0" : "PropertyNamespace.Nested" + } + }, + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : true, + "isStatic" : true, + "name" : "nestedConstant", + "staticContext" : { + "namespaceEnum" : { + "_0" : "PropertyNamespace.Nested" + } + }, + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "nestedDouble", + "staticContext" : { + "namespaceEnum" : { + "_0" : "PropertyNamespace.Nested" + } + }, + "type" : { + "double" : { + + } + } + } + ], + "swiftCallName" : "PropertyNamespace.Nested" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "PropertyNamespace", + "staticMethods" : [ + + ], + "staticProperties" : [ + { + "isReadonly" : false, + "isStatic" : true, + "name" : "namespaceProperty", + "staticContext" : { + "namespaceEnum" : { + "_0" : "PropertyNamespace" + } + }, + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : true, + "isStatic" : true, + "name" : "namespaceConstant", + "staticContext" : { + "namespaceEnum" : { + "_0" : "PropertyNamespace" + } + }, + "type" : { + "string" : { + + } + } + } + ], + "swiftCallName" : "PropertyNamespace" + } + ], + "functions" : [ + + ], + "moduleName" : "TestModule" +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.swift new file mode 100644 index 00000000..ba45244e --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.swift @@ -0,0 +1,338 @@ +// 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`. + +@_spi(BridgeJS) import JavaScriptKit + +extension PropertyEnum: _BridgedSwiftCaseEnum { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { + return bridgeJSRawValue + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> PropertyEnum { + return PropertyEnum(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> PropertyEnum { + return PropertyEnum(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 { + return bridgeJSRawValue + } + + private init?(bridgeJSRawValue: Int32) { + switch bridgeJSRawValue { + case 0: + self = .value1 + case 1: + self = .value2 + default: + return nil + } + } + + private var bridgeJSRawValue: Int32 { + switch self { + case .value1: + return 0 + case .value2: + return 1 + } + } +} + +@_expose(wasm, "bjs_PropertyEnum_static_enumProperty_get") +@_cdecl("bjs_PropertyEnum_static_enumProperty_get") +public func _bjs_PropertyEnum_static_enumProperty_get() -> Void { + #if arch(wasm32) + let ret = PropertyEnum.enumProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyEnum_static_enumProperty_set") +@_cdecl("bjs_PropertyEnum_static_enumProperty_set") +public func _bjs_PropertyEnum_static_enumProperty_set(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + PropertyEnum.enumProperty = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyEnum_static_enumConstant_get") +@_cdecl("bjs_PropertyEnum_static_enumConstant_get") +public func _bjs_PropertyEnum_static_enumConstant_get() -> Int32 { + #if arch(wasm32) + let ret = PropertyEnum.enumConstant + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyEnum_static_computedEnum_get") +@_cdecl("bjs_PropertyEnum_static_computedEnum_get") +public func _bjs_PropertyEnum_static_computedEnum_get() -> Void { + #if arch(wasm32) + let ret = PropertyEnum.computedEnum + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyEnum_static_computedEnum_set") +@_cdecl("bjs_PropertyEnum_static_computedEnum_set") +public func _bjs_PropertyEnum_static_computedEnum_set(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + PropertyEnum.computedEnum = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyNamespace_Nested_static_nestedProperty_get") +@_cdecl("bjs_PropertyNamespace_Nested_static_nestedProperty_get") +public func _bjs_PropertyNamespace_Nested_static_nestedProperty_get() -> Int32 { + #if arch(wasm32) + let ret = PropertyNamespace.Nested.nestedProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyNamespace_Nested_static_nestedProperty_set") +@_cdecl("bjs_PropertyNamespace_Nested_static_nestedProperty_set") +public func _bjs_PropertyNamespace_Nested_static_nestedProperty_set(value: Int32) -> Void { + #if arch(wasm32) + PropertyNamespace.Nested.nestedProperty = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyNamespace_Nested_static_nestedConstant_get") +@_cdecl("bjs_PropertyNamespace_Nested_static_nestedConstant_get") +public func _bjs_PropertyNamespace_Nested_static_nestedConstant_get() -> Void { + #if arch(wasm32) + let ret = PropertyNamespace.Nested.nestedConstant + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyNamespace_Nested_static_nestedDouble_get") +@_cdecl("bjs_PropertyNamespace_Nested_static_nestedDouble_get") +public func _bjs_PropertyNamespace_Nested_static_nestedDouble_get() -> Float64 { + #if arch(wasm32) + let ret = PropertyNamespace.Nested.nestedDouble + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyNamespace_Nested_static_nestedDouble_set") +@_cdecl("bjs_PropertyNamespace_Nested_static_nestedDouble_set") +public func _bjs_PropertyNamespace_Nested_static_nestedDouble_set(value: Float64) -> Void { + #if arch(wasm32) + PropertyNamespace.Nested.nestedDouble = Double.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyNamespace_static_namespaceProperty_get") +@_cdecl("bjs_PropertyNamespace_static_namespaceProperty_get") +public func _bjs_PropertyNamespace_static_namespaceProperty_get() -> Void { + #if arch(wasm32) + let ret = PropertyNamespace.namespaceProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyNamespace_static_namespaceProperty_set") +@_cdecl("bjs_PropertyNamespace_static_namespaceProperty_set") +public func _bjs_PropertyNamespace_static_namespaceProperty_set(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + PropertyNamespace.namespaceProperty = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyNamespace_static_namespaceConstant_get") +@_cdecl("bjs_PropertyNamespace_static_namespaceConstant_get") +public func _bjs_PropertyNamespace_static_namespaceConstant_get() -> Void { + #if arch(wasm32) + let ret = PropertyNamespace.namespaceConstant + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_init") +@_cdecl("bjs_PropertyClass_init") +public func _bjs_PropertyClass_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = PropertyClass() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_static_staticConstant_get") +@_cdecl("bjs_PropertyClass_static_staticConstant_get") +public func _bjs_PropertyClass_static_staticConstant_get() -> Void { + #if arch(wasm32) + let ret = PropertyClass.staticConstant + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_static_staticVariable_get") +@_cdecl("bjs_PropertyClass_static_staticVariable_get") +public func _bjs_PropertyClass_static_staticVariable_get() -> Int32 { + #if arch(wasm32) + let ret = PropertyClass.staticVariable + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_static_staticVariable_set") +@_cdecl("bjs_PropertyClass_static_staticVariable_set") +public func _bjs_PropertyClass_static_staticVariable_set(value: Int32) -> Void { + #if arch(wasm32) + PropertyClass.staticVariable = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_static_jsObjectProperty_get") +@_cdecl("bjs_PropertyClass_static_jsObjectProperty_get") +public func _bjs_PropertyClass_static_jsObjectProperty_get() -> Int32 { + #if arch(wasm32) + let ret = PropertyClass.jsObjectProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_static_jsObjectProperty_set") +@_cdecl("bjs_PropertyClass_static_jsObjectProperty_set") +public func _bjs_PropertyClass_static_jsObjectProperty_set(value: Int32) -> Void { + #if arch(wasm32) + PropertyClass.jsObjectProperty = JSObject.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_static_classVariable_get") +@_cdecl("bjs_PropertyClass_static_classVariable_get") +public func _bjs_PropertyClass_static_classVariable_get() -> Void { + #if arch(wasm32) + let ret = PropertyClass.classVariable + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_static_classVariable_set") +@_cdecl("bjs_PropertyClass_static_classVariable_set") +public func _bjs_PropertyClass_static_classVariable_set(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + PropertyClass.classVariable = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_static_computedProperty_get") +@_cdecl("bjs_PropertyClass_static_computedProperty_get") +public func _bjs_PropertyClass_static_computedProperty_get() -> Void { + #if arch(wasm32) + let ret = PropertyClass.computedProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_static_computedProperty_set") +@_cdecl("bjs_PropertyClass_static_computedProperty_set") +public func _bjs_PropertyClass_static_computedProperty_set(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + PropertyClass.computedProperty = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_static_readOnlyComputed_get") +@_cdecl("bjs_PropertyClass_static_readOnlyComputed_get") +public func _bjs_PropertyClass_static_readOnlyComputed_get() -> Int32 { + #if arch(wasm32) + let ret = PropertyClass.readOnlyComputed + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_static_optionalProperty_get") +@_cdecl("bjs_PropertyClass_static_optionalProperty_get") +public func _bjs_PropertyClass_static_optionalProperty_get() -> Void { + #if arch(wasm32) + let ret = PropertyClass.optionalProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_static_optionalProperty_set") +@_cdecl("bjs_PropertyClass_static_optionalProperty_set") +public func _bjs_PropertyClass_static_optionalProperty_set(valueIsSome: Int32, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + PropertyClass.optionalProperty = Optional.bridgeJSLiftParameter(valueIsSome, valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyClass_deinit") +@_cdecl("bjs_PropertyClass_deinit") +public func _bjs_PropertyClass_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension PropertyClass: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "TestModule", name: "bjs_PropertyClass_wrap") + func _bjs_PropertyClass_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_PropertyClass_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_PropertyClass_wrap(Unmanaged.passRetained(self).toOpaque())))) + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json index e838f59a..59b7f9ef 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json @@ -68,6 +68,7 @@ "properties" : [ { "isReadonly" : false, + "isStatic" : false, "name" : "name", "type" : { "string" : { diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md index 10e4d89c..41315708 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md @@ -68,4 +68,5 @@ This command will: - - - +- - diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Properties.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Properties.md new file mode 100644 index 00000000..80b6f580 --- /dev/null +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Properties.md @@ -0,0 +1,189 @@ +# Exporting Swift Static Properties to JS + +Learn how to export Swift static and class properties as JavaScript static properties using BridgeJS. + +## Overview + +> Tip: You can quickly preview what interfaces will be exposed on the Swift/TypeScript sides using the [BridgeJS Playground](https://swiftwasm.org/JavaScriptKit/PlayBridgeJS/). + +BridgeJS supports exporting Swift `static var`, `static let`, and `class var` properties to JavaScript static properties. Both stored and computed properties are supported. + +## Class Static Properties + +Classes can export both stored and computed static properties: + +```swift +@JS class Configuration { + @JS init() {} + + @JS static let version = "1.0.0" + @JS static var debugMode = false + @JS class var defaultTimeout = 30 + + @JS static var timestamp: Double { + get { return Date().timeIntervalSince1970 } + set { /* custom setter logic */ } + } + + @JS static var buildNumber: Int { + return 12345 + } +} +``` + +JavaScript usage: + +```javascript +console.log(Configuration.version); // "1.0.0" +console.log(Configuration.debugMode); // false +console.log(Configuration.timestamp); // current timestamp + +Configuration.debugMode = true; +Configuration.timestamp = Date.now() / 1000; +``` + +Generated TypeScript definitions: + +```typescript +export type Exports = { + Configuration: { + new(): Configuration; + readonly version: string; + debugMode: boolean; + defaultTimeout: number; + timestamp: number; + readonly buildNumber: number; + } +} + +export interface Configuration extends SwiftHeapObject { + // instance methods here +} +``` + +## Enum Static Properties + +Enums can contain static properties that are exported alongside enum cases: + +```swift +@JS enum NetworkStatus { + case connected + case disconnected + case connecting + + @JS static var retryCount = 3 + @JS static let maxRetries = 10 +} +``` + +Generated JavaScript: + +```javascript +export const NetworkStatus = { + Connected: 0, + Disconnected: 1, + Connecting: 2 +}; + +// Properties added via Object.defineProperty +Object.defineProperty(NetworkStatus, 'retryCount', { + get: function() { return wasmCall('bjs_NetworkStatus_static_retryCount_get'); }, + set: function(value) { wasmCall('bjs_NetworkStatus_static_retryCount_set', value); } +}); + +Object.defineProperty(NetworkStatus, 'maxRetries', { + get: function() { return wasmCall('bjs_NetworkStatus_static_maxRetries_get'); } +}); +``` + +JavaScript usage: + +```javascript +console.log(NetworkStatus.retryCount); // 3 +console.log(NetworkStatus.maxRetries); // 10 +NetworkStatus.retryCount = 5; +``` + +Generated TypeScript definitions: + +```typescript +export const NetworkStatus: { + readonly Connected: 0; + readonly Disconnected: 1; + readonly Connecting: 2; + retryCount: number; + readonly maxRetries: number; +}; +export type NetworkStatus = typeof NetworkStatus[keyof typeof NetworkStatus]; +``` + +## Namespace Enum Static Properties + +Namespace enums organize related static properties and are assigned to `globalThis`: + +```swift +@JS enum Config { + @JS enum API { + @JS static var baseURL = "https://api.example.com" + @JS static let version = "v1" + } +} +``` + +Generated JavaScript: + +```javascript +if (typeof globalThis.Config === 'undefined') { + globalThis.Config = {}; +} +if (typeof globalThis.Config.API === 'undefined') { + globalThis.Config.API = {}; +} + +Object.defineProperty(globalThis.Config.API, 'baseURL', { + get: function() { return wasmCall('bjs_Config_API_baseURL_get'); }, + set: function(value) { wasmCall('bjs_Config_API_baseURL_set', value); } +}); + +Object.defineProperty(globalThis.Config.API, 'version', { + get: function() { return wasmCall('bjs_Config_API_version_get'); } +}); +``` + +JavaScript usage: + +```javascript +console.log(Config.API.baseURL); // "https://api.example.com" +console.log(Config.API.version); // "v1" + +Config.API.baseURL = "https://staging.api.example.com"; +``` + +Generated TypeScript definitions: + +```typescript +declare global { + namespace Config { + namespace API { + baseURL: string; + readonly version: string; + } + } +} +``` + +## Supported Features + +| Swift Static Property Feature | Status | +|:------------------------------|:-------| +| Class `static let` | ✅ | +| Class `static var` | ✅ | +| Class `class var` | ✅ | +| Enum static properties | ✅ | +| Namespace enum static properties | ✅ | +| Computed properties (get/set) | ✅ | +| Read-only computed properties | ✅ | +| All property types (primitives, objects, optionals) | ✅ | +| Property observers (`willSet`/`didSet`) | ❌ | +| Generic static properties | ❌ | +| Protocol static property requirements | ❌ | \ No newline at end of file diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index e985b7a2..ab1b7f82 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -700,6 +700,82 @@ enum APIOptionalResult { } } +// MARK: - Static Properties + +@JS class StaticPropertyHolder { + @JS static let staticConstant: String = "constant" + @JS nonisolated(unsafe) static var staticVariable: Int = 42 + @JS nonisolated(unsafe) static var staticString: String = "initial" + @JS nonisolated(unsafe) static var staticBool: Bool = true + @JS nonisolated(unsafe) static var staticFloat: Float = 3.14 + @JS nonisolated(unsafe) static var staticDouble: Double = 2.718 + + @JS static var computedProperty: String { + get { return "computed: \(staticVariable)" } + set { + if let number = Int(newValue.replacingOccurrences(of: "computed: ", with: "")) { + staticVariable = number + } + } + } + + @JS static var readOnlyComputed: Int { + return staticVariable * 2 + } + + @JS nonisolated(unsafe) static var optionalString: String? = nil + @JS nonisolated(unsafe) static var optionalInt: Int? = nil + + @JS nonisolated(unsafe) static var jsObjectProperty: JSObject = JSObject() + + @JS init() {} + +} + +@JS enum StaticPropertyEnum { + case option1 + case option2 + + @JS nonisolated(unsafe) static var enumProperty: String = "enum value" + @JS static let enumConstant: Int = 42 + @JS nonisolated(unsafe) static var enumBool: Bool = false + + @JS nonisolated(unsafe) static var enumVariable: Int = 200 + + @JS static var computedReadonly: Int { + return enumVariable * 2 + } + + @JS static var computedReadWrite: String { + get { + return "Value: \(enumVariable)" + } + set { + if let range = newValue.range(of: "Value: "), + let number = Int(String(newValue[range.upperBound...])) + { + enumVariable = number + } + } + } +} + +@JS enum StaticPropertyNamespace { + @JS nonisolated(unsafe) static var namespaceProperty: String = "namespace" + @JS static let namespaceConstant: String = "constant" + + @JS enum NestedProperties { + @JS nonisolated(unsafe) static var nestedProperty: Int = 999 + @JS static let nestedConstant: String = "nested" + @JS nonisolated(unsafe) static var nestedDouble: Double = 1.414 + } +} + +// Test functions for static properties +@JS func getAllStaticPropertyValues() -> String { + return "const:\(StaticPropertyHolder.staticConstant),var:\(StaticPropertyHolder.staticVariable),computed:\(StaticPropertyHolder.computedProperty),readonly:\(StaticPropertyHolder.readOnlyComputed)" +} + class ExportAPITests: XCTestCase { func testAll() { var hasDeinitGreeter = false diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift index 59ca3830..56bd9a13 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift @@ -545,6 +545,232 @@ public func _bjs_StaticUtils_Nested_roundtrip(valueBytes: Int32, valueLength: In #endif } +extension StaticPropertyEnum: _BridgedSwiftCaseEnum { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { + return bridgeJSRawValue + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> StaticPropertyEnum { + return StaticPropertyEnum(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> StaticPropertyEnum { + return StaticPropertyEnum(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 { + return bridgeJSRawValue + } + + private init?(bridgeJSRawValue: Int32) { + switch bridgeJSRawValue { + case 0: + self = .option1 + case 1: + self = .option2 + default: + return nil + } + } + + private var bridgeJSRawValue: Int32 { + switch self { + case .option1: + return 0 + case .option2: + return 1 + } + } +} + +@_expose(wasm, "bjs_StaticPropertyEnum_static_enumProperty_get") +@_cdecl("bjs_StaticPropertyEnum_static_enumProperty_get") +public func _bjs_StaticPropertyEnum_static_enumProperty_get() -> Void { + #if arch(wasm32) + let ret = StaticPropertyEnum.enumProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyEnum_static_enumProperty_set") +@_cdecl("bjs_StaticPropertyEnum_static_enumProperty_set") +public func _bjs_StaticPropertyEnum_static_enumProperty_set(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + StaticPropertyEnum.enumProperty = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyEnum_static_enumConstant_get") +@_cdecl("bjs_StaticPropertyEnum_static_enumConstant_get") +public func _bjs_StaticPropertyEnum_static_enumConstant_get() -> Int32 { + #if arch(wasm32) + let ret = StaticPropertyEnum.enumConstant + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyEnum_static_enumBool_get") +@_cdecl("bjs_StaticPropertyEnum_static_enumBool_get") +public func _bjs_StaticPropertyEnum_static_enumBool_get() -> Int32 { + #if arch(wasm32) + let ret = StaticPropertyEnum.enumBool + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyEnum_static_enumBool_set") +@_cdecl("bjs_StaticPropertyEnum_static_enumBool_set") +public func _bjs_StaticPropertyEnum_static_enumBool_set(value: Int32) -> Void { + #if arch(wasm32) + StaticPropertyEnum.enumBool = Bool.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyEnum_static_enumVariable_get") +@_cdecl("bjs_StaticPropertyEnum_static_enumVariable_get") +public func _bjs_StaticPropertyEnum_static_enumVariable_get() -> Int32 { + #if arch(wasm32) + let ret = StaticPropertyEnum.enumVariable + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyEnum_static_enumVariable_set") +@_cdecl("bjs_StaticPropertyEnum_static_enumVariable_set") +public func _bjs_StaticPropertyEnum_static_enumVariable_set(value: Int32) -> Void { + #if arch(wasm32) + StaticPropertyEnum.enumVariable = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyEnum_static_computedReadonly_get") +@_cdecl("bjs_StaticPropertyEnum_static_computedReadonly_get") +public func _bjs_StaticPropertyEnum_static_computedReadonly_get() -> Int32 { + #if arch(wasm32) + let ret = StaticPropertyEnum.computedReadonly + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyEnum_static_computedReadWrite_get") +@_cdecl("bjs_StaticPropertyEnum_static_computedReadWrite_get") +public func _bjs_StaticPropertyEnum_static_computedReadWrite_get() -> Void { + #if arch(wasm32) + let ret = StaticPropertyEnum.computedReadWrite + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyEnum_static_computedReadWrite_set") +@_cdecl("bjs_StaticPropertyEnum_static_computedReadWrite_set") +public func _bjs_StaticPropertyEnum_static_computedReadWrite_set(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + StaticPropertyEnum.computedReadWrite = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyNamespace_NestedProperties_static_nestedProperty_get") +@_cdecl("bjs_StaticPropertyNamespace_NestedProperties_static_nestedProperty_get") +public func _bjs_StaticPropertyNamespace_NestedProperties_static_nestedProperty_get() -> Int32 { + #if arch(wasm32) + let ret = StaticPropertyNamespace.NestedProperties.nestedProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyNamespace_NestedProperties_static_nestedProperty_set") +@_cdecl("bjs_StaticPropertyNamespace_NestedProperties_static_nestedProperty_set") +public func _bjs_StaticPropertyNamespace_NestedProperties_static_nestedProperty_set(value: Int32) -> Void { + #if arch(wasm32) + StaticPropertyNamespace.NestedProperties.nestedProperty = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyNamespace_NestedProperties_static_nestedConstant_get") +@_cdecl("bjs_StaticPropertyNamespace_NestedProperties_static_nestedConstant_get") +public func _bjs_StaticPropertyNamespace_NestedProperties_static_nestedConstant_get() -> Void { + #if arch(wasm32) + let ret = StaticPropertyNamespace.NestedProperties.nestedConstant + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyNamespace_NestedProperties_static_nestedDouble_get") +@_cdecl("bjs_StaticPropertyNamespace_NestedProperties_static_nestedDouble_get") +public func _bjs_StaticPropertyNamespace_NestedProperties_static_nestedDouble_get() -> Float64 { + #if arch(wasm32) + let ret = StaticPropertyNamespace.NestedProperties.nestedDouble + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyNamespace_NestedProperties_static_nestedDouble_set") +@_cdecl("bjs_StaticPropertyNamespace_NestedProperties_static_nestedDouble_set") +public func _bjs_StaticPropertyNamespace_NestedProperties_static_nestedDouble_set(value: Float64) -> Void { + #if arch(wasm32) + StaticPropertyNamespace.NestedProperties.nestedDouble = Double.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyNamespace_static_namespaceProperty_get") +@_cdecl("bjs_StaticPropertyNamespace_static_namespaceProperty_get") +public func _bjs_StaticPropertyNamespace_static_namespaceProperty_get() -> Void { + #if arch(wasm32) + let ret = StaticPropertyNamespace.namespaceProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyNamespace_static_namespaceProperty_set") +@_cdecl("bjs_StaticPropertyNamespace_static_namespaceProperty_set") +public func _bjs_StaticPropertyNamespace_static_namespaceProperty_set(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + StaticPropertyNamespace.namespaceProperty = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyNamespace_static_namespaceConstant_get") +@_cdecl("bjs_StaticPropertyNamespace_static_namespaceConstant_get") +public func _bjs_StaticPropertyNamespace_static_namespaceConstant_get() -> Void { + #if arch(wasm32) + let ret = StaticPropertyNamespace.namespaceConstant + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_roundTripVoid") @_cdecl("bjs_roundTripVoid") public func _bjs_roundTripVoid() -> Void { @@ -1692,6 +1918,17 @@ public func _bjs_getObserverStats() -> Void { #endif } +@_expose(wasm, "bjs_getAllStaticPropertyValues") +@_cdecl("bjs_getAllStaticPropertyValues") +public func _bjs_getAllStaticPropertyValues() -> Void { + #if arch(wasm32) + let ret = getAllStaticPropertyValues() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_Greeter_init") @_cdecl("bjs_Greeter_init") public func _bjs_Greeter_init(nameBytes: Int32, nameLength: Int32) -> UnsafeMutableRawPointer { @@ -2506,4 +2743,246 @@ extension MathUtils: ConvertibleToJSValue, _BridgedSwiftHeapObject { #endif return .object(JSObject(id: UInt32(bitPattern: _bjs_MathUtils_wrap(Unmanaged.passRetained(self).toOpaque())))) } +} + +@_expose(wasm, "bjs_StaticPropertyHolder_init") +@_cdecl("bjs_StaticPropertyHolder_init") +public func _bjs_StaticPropertyHolder_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = StaticPropertyHolder() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_staticConstant_get") +@_cdecl("bjs_StaticPropertyHolder_static_staticConstant_get") +public func _bjs_StaticPropertyHolder_static_staticConstant_get() -> Void { + #if arch(wasm32) + let ret = StaticPropertyHolder.staticConstant + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_staticVariable_get") +@_cdecl("bjs_StaticPropertyHolder_static_staticVariable_get") +public func _bjs_StaticPropertyHolder_static_staticVariable_get() -> Int32 { + #if arch(wasm32) + let ret = StaticPropertyHolder.staticVariable + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_staticVariable_set") +@_cdecl("bjs_StaticPropertyHolder_static_staticVariable_set") +public func _bjs_StaticPropertyHolder_static_staticVariable_set(value: Int32) -> Void { + #if arch(wasm32) + StaticPropertyHolder.staticVariable = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_staticString_get") +@_cdecl("bjs_StaticPropertyHolder_static_staticString_get") +public func _bjs_StaticPropertyHolder_static_staticString_get() -> Void { + #if arch(wasm32) + let ret = StaticPropertyHolder.staticString + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_staticString_set") +@_cdecl("bjs_StaticPropertyHolder_static_staticString_set") +public func _bjs_StaticPropertyHolder_static_staticString_set(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + StaticPropertyHolder.staticString = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_staticBool_get") +@_cdecl("bjs_StaticPropertyHolder_static_staticBool_get") +public func _bjs_StaticPropertyHolder_static_staticBool_get() -> Int32 { + #if arch(wasm32) + let ret = StaticPropertyHolder.staticBool + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_staticBool_set") +@_cdecl("bjs_StaticPropertyHolder_static_staticBool_set") +public func _bjs_StaticPropertyHolder_static_staticBool_set(value: Int32) -> Void { + #if arch(wasm32) + StaticPropertyHolder.staticBool = Bool.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_staticFloat_get") +@_cdecl("bjs_StaticPropertyHolder_static_staticFloat_get") +public func _bjs_StaticPropertyHolder_static_staticFloat_get() -> Float32 { + #if arch(wasm32) + let ret = StaticPropertyHolder.staticFloat + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_staticFloat_set") +@_cdecl("bjs_StaticPropertyHolder_static_staticFloat_set") +public func _bjs_StaticPropertyHolder_static_staticFloat_set(value: Float32) -> Void { + #if arch(wasm32) + StaticPropertyHolder.staticFloat = Float.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_staticDouble_get") +@_cdecl("bjs_StaticPropertyHolder_static_staticDouble_get") +public func _bjs_StaticPropertyHolder_static_staticDouble_get() -> Float64 { + #if arch(wasm32) + let ret = StaticPropertyHolder.staticDouble + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_staticDouble_set") +@_cdecl("bjs_StaticPropertyHolder_static_staticDouble_set") +public func _bjs_StaticPropertyHolder_static_staticDouble_set(value: Float64) -> Void { + #if arch(wasm32) + StaticPropertyHolder.staticDouble = Double.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_computedProperty_get") +@_cdecl("bjs_StaticPropertyHolder_static_computedProperty_get") +public func _bjs_StaticPropertyHolder_static_computedProperty_get() -> Void { + #if arch(wasm32) + let ret = StaticPropertyHolder.computedProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_computedProperty_set") +@_cdecl("bjs_StaticPropertyHolder_static_computedProperty_set") +public func _bjs_StaticPropertyHolder_static_computedProperty_set(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + StaticPropertyHolder.computedProperty = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_readOnlyComputed_get") +@_cdecl("bjs_StaticPropertyHolder_static_readOnlyComputed_get") +public func _bjs_StaticPropertyHolder_static_readOnlyComputed_get() -> Int32 { + #if arch(wasm32) + let ret = StaticPropertyHolder.readOnlyComputed + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_optionalString_get") +@_cdecl("bjs_StaticPropertyHolder_static_optionalString_get") +public func _bjs_StaticPropertyHolder_static_optionalString_get() -> Void { + #if arch(wasm32) + let ret = StaticPropertyHolder.optionalString + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_optionalString_set") +@_cdecl("bjs_StaticPropertyHolder_static_optionalString_set") +public func _bjs_StaticPropertyHolder_static_optionalString_set(valueIsSome: Int32, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + StaticPropertyHolder.optionalString = Optional.bridgeJSLiftParameter(valueIsSome, valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_optionalInt_get") +@_cdecl("bjs_StaticPropertyHolder_static_optionalInt_get") +public func _bjs_StaticPropertyHolder_static_optionalInt_get() -> Void { + #if arch(wasm32) + let ret = StaticPropertyHolder.optionalInt + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_optionalInt_set") +@_cdecl("bjs_StaticPropertyHolder_static_optionalInt_set") +public func _bjs_StaticPropertyHolder_static_optionalInt_set(valueIsSome: Int32, valueValue: Int32) -> Void { + #if arch(wasm32) + StaticPropertyHolder.optionalInt = Optional.bridgeJSLiftParameter(valueIsSome, valueValue) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_jsObjectProperty_get") +@_cdecl("bjs_StaticPropertyHolder_static_jsObjectProperty_get") +public func _bjs_StaticPropertyHolder_static_jsObjectProperty_get() -> Int32 { + #if arch(wasm32) + let ret = StaticPropertyHolder.jsObjectProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_static_jsObjectProperty_set") +@_cdecl("bjs_StaticPropertyHolder_static_jsObjectProperty_set") +public func _bjs_StaticPropertyHolder_static_jsObjectProperty_set(value: Int32) -> Void { + #if arch(wasm32) + StaticPropertyHolder.jsObjectProperty = JSObject.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyHolder_deinit") +@_cdecl("bjs_StaticPropertyHolder_deinit") +public func _bjs_StaticPropertyHolder_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension StaticPropertyHolder: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_StaticPropertyHolder_wrap") + func _bjs_StaticPropertyHolder_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_StaticPropertyHolder_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_StaticPropertyHolder_wrap(Unmanaged.passRetained(self).toOpaque())))) + } } \ No newline at end of file diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json index 9fa074c3..c4dc5d23 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -68,6 +68,7 @@ "properties" : [ { "isReadonly" : false, + "isStatic" : false, "name" : "name", "type" : { "string" : { @@ -77,6 +78,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "prefix", "type" : { "string" : { @@ -366,6 +368,7 @@ "properties" : [ { "isReadonly" : false, + "isStatic" : false, "name" : "optionalName", "type" : { "optional" : { @@ -379,6 +382,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "optionalAge", "type" : { "optional" : { @@ -392,6 +396,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "optionalGreeter", "type" : { "optional" : { @@ -433,6 +438,7 @@ "properties" : [ { "isReadonly" : false, + "isStatic" : false, "name" : "value", "type" : { "int" : { @@ -540,6 +546,7 @@ "properties" : [ { "isReadonly" : false, + "isStatic" : false, "name" : "intValue", "type" : { "int" : { @@ -549,6 +556,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "floatValue", "type" : { "float" : { @@ -558,6 +566,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "doubleValue", "type" : { "double" : { @@ -567,6 +576,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "boolValue", "type" : { "bool" : { @@ -576,6 +586,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "stringValue", "type" : { "string" : { @@ -585,6 +596,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "readonlyInt", "type" : { "int" : { @@ -594,6 +606,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "readonlyFloat", "type" : { "float" : { @@ -603,6 +616,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "readonlyDouble", "type" : { "double" : { @@ -612,6 +626,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "readonlyBool", "type" : { "bool" : { @@ -621,6 +636,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "readonlyString", "type" : { "string" : { @@ -630,6 +646,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "jsObject", "type" : { "jsObject" : { @@ -639,6 +656,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "sibling", "type" : { "swiftHeapObject" : { @@ -648,6 +666,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "lazyValue", "type" : { "string" : { @@ -657,6 +676,7 @@ }, { "isReadonly" : true, + "isStatic" : false, "name" : "computedReadonly", "type" : { "int" : { @@ -666,6 +686,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "computedReadWrite", "type" : { "string" : { @@ -675,6 +696,7 @@ }, { "isReadonly" : false, + "isStatic" : false, "name" : "observedProperty", "type" : { "int" : { @@ -771,6 +793,199 @@ ], "swiftCallName" : "MathUtils" + }, + { + "constructor" : { + "abiName" : "bjs_StaticPropertyHolder_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "methods" : [ + + ], + "name" : "StaticPropertyHolder", + "properties" : [ + { + "isReadonly" : true, + "isStatic" : true, + "name" : "staticConstant", + "staticContext" : { + "className" : { + "_0" : "StaticPropertyHolder" + } + }, + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "staticVariable", + "staticContext" : { + "className" : { + "_0" : "StaticPropertyHolder" + } + }, + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "staticString", + "staticContext" : { + "className" : { + "_0" : "StaticPropertyHolder" + } + }, + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "staticBool", + "staticContext" : { + "className" : { + "_0" : "StaticPropertyHolder" + } + }, + "type" : { + "bool" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "staticFloat", + "staticContext" : { + "className" : { + "_0" : "StaticPropertyHolder" + } + }, + "type" : { + "float" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "staticDouble", + "staticContext" : { + "className" : { + "_0" : "StaticPropertyHolder" + } + }, + "type" : { + "double" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "computedProperty", + "staticContext" : { + "className" : { + "_0" : "StaticPropertyHolder" + } + }, + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : true, + "isStatic" : true, + "name" : "readOnlyComputed", + "staticContext" : { + "className" : { + "_0" : "StaticPropertyHolder" + } + }, + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "optionalString", + "staticContext" : { + "className" : { + "_0" : "StaticPropertyHolder" + } + }, + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "optionalInt", + "staticContext" : { + "className" : { + "_0" : "StaticPropertyHolder" + } + }, + "type" : { + "optional" : { + "_0" : { + "int" : { + + } + } + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "jsObjectProperty", + "staticContext" : { + "className" : { + "_0" : "StaticPropertyHolder" + } + }, + "type" : { + "jsObject" : { + + } + } + } + ], + "swiftCallName" : "StaticPropertyHolder" } ], "enums" : [ @@ -805,6 +1020,9 @@ "name" : "Direction", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Direction" }, @@ -833,6 +1051,9 @@ "name" : "Status", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Status" }, @@ -865,6 +1086,9 @@ "rawType" : "String", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Theme" }, @@ -897,6 +1121,9 @@ "rawType" : "Int", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "HttpStatus" }, @@ -931,6 +1158,9 @@ "name" : "TSDirection", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "TSDirection" }, @@ -963,6 +1193,9 @@ "rawType" : "String", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "TSTheme" }, @@ -974,6 +1207,9 @@ "name" : "Utils", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Utils" }, @@ -1012,9 +1248,40 @@ ], "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Networking.API.Method" }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "API", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "API" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Networking", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Networking" + }, { "cases" : [ { @@ -1054,6 +1321,9 @@ "rawType" : "String", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Configuration.LogLevel" }, @@ -1089,9 +1359,26 @@ "rawType" : "Int", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Configuration.Port" }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Configuration", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Configuration" + }, { "cases" : [ { @@ -1116,9 +1403,30 @@ ], "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Internal.SupportedMethod" }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Internal", + "namespace" : [ + "Networking", + "APIV2" + ], + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Internal" + }, { "cases" : [ { @@ -1192,6 +1500,9 @@ "name" : "APIResult", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "APIResult" }, @@ -1385,6 +1696,9 @@ "name" : "ComplexResult", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "ComplexResult" }, @@ -1455,9 +1769,26 @@ ], "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "Utilities.Result" }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Utilities", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Utilities" + }, { "cases" : [ { @@ -1499,9 +1830,26 @@ ], "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "API.NetworkingResult" }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "API", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "API" + }, { "cases" : [ { @@ -1590,6 +1938,9 @@ "name" : "APIOptionalResult", "staticMethods" : [ + ], + "staticProperties" : [ + ], "swiftCallName" : "APIOptionalResult" }, @@ -1641,6 +1992,9 @@ } } } + ], + "staticProperties" : [ + ], "swiftCallName" : "StaticCalculator" }, @@ -1684,8 +2038,243 @@ } } } + ], + "staticProperties" : [ + ], "swiftCallName" : "StaticUtils.Nested" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "StaticUtils", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "StaticUtils" + }, + { + "cases" : [ + { + "associatedValues" : [ + + ], + "name" : "option1" + }, + { + "associatedValues" : [ + + ], + "name" : "option2" + } + ], + "emitStyle" : "const", + "name" : "StaticPropertyEnum", + "staticMethods" : [ + + ], + "staticProperties" : [ + { + "isReadonly" : false, + "isStatic" : true, + "name" : "enumProperty", + "staticContext" : { + "enumName" : { + "_0" : "StaticPropertyEnum" + } + }, + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : true, + "isStatic" : true, + "name" : "enumConstant", + "staticContext" : { + "enumName" : { + "_0" : "StaticPropertyEnum" + } + }, + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "enumBool", + "staticContext" : { + "enumName" : { + "_0" : "StaticPropertyEnum" + } + }, + "type" : { + "bool" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "enumVariable", + "staticContext" : { + "enumName" : { + "_0" : "StaticPropertyEnum" + } + }, + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : true, + "isStatic" : true, + "name" : "computedReadonly", + "staticContext" : { + "enumName" : { + "_0" : "StaticPropertyEnum" + } + }, + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "computedReadWrite", + "staticContext" : { + "enumName" : { + "_0" : "StaticPropertyEnum" + } + }, + "type" : { + "string" : { + + } + } + } + ], + "swiftCallName" : "StaticPropertyEnum" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "NestedProperties", + "namespace" : [ + "StaticPropertyNamespace" + ], + "staticMethods" : [ + + ], + "staticProperties" : [ + { + "isReadonly" : false, + "isStatic" : true, + "name" : "nestedProperty", + "staticContext" : { + "namespaceEnum" : { + "_0" : "StaticPropertyNamespace.NestedProperties" + } + }, + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : true, + "isStatic" : true, + "name" : "nestedConstant", + "staticContext" : { + "namespaceEnum" : { + "_0" : "StaticPropertyNamespace.NestedProperties" + } + }, + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "nestedDouble", + "staticContext" : { + "namespaceEnum" : { + "_0" : "StaticPropertyNamespace.NestedProperties" + } + }, + "type" : { + "double" : { + + } + } + } + ], + "swiftCallName" : "StaticPropertyNamespace.NestedProperties" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "StaticPropertyNamespace", + "staticMethods" : [ + + ], + "staticProperties" : [ + { + "isReadonly" : false, + "isStatic" : true, + "name" : "namespaceProperty", + "staticContext" : { + "namespaceEnum" : { + "_0" : "StaticPropertyNamespace" + } + }, + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : true, + "isStatic" : true, + "name" : "namespaceConstant", + "staticContext" : { + "namespaceEnum" : { + "_0" : "StaticPropertyNamespace" + } + }, + "type" : { + "string" : { + + } + } + } + ], + "swiftCallName" : "StaticPropertyNamespace" } ], "functions" : [ @@ -4265,6 +4854,23 @@ "name" : "getObserverStats", "parameters" : [ + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_getAllStaticPropertyValues", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "getAllStaticPropertyValues", + "parameters" : [ + ], "returnType" : { "string" : { diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index e8a86582..0c74ec4f 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -1,7 +1,7 @@ // @ts-check import { - Direction, Status, Theme, HttpStatus, TSDirection, TSTheme, APIResult, ComplexResult, APIOptionalResult, StaticCalculator + Direction, Status, Theme, HttpStatus, TSDirection, TSTheme, APIResult, ComplexResult, APIOptionalResult, StaticCalculator, StaticPropertyEnum } from '../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.js'; /** @type {import('../.build/plugins/PackageToJS/outputs/PackageTests/test.d.ts').SetupOptionsFn} */ @@ -274,6 +274,88 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { ph.release(); ph2.release(); + // Test static properties + assert.equal(exports.StaticPropertyHolder.staticConstant, "constant"); + assert.equal(exports.StaticPropertyHolder.staticVariable, 42); + assert.equal(exports.StaticPropertyHolder.staticString, "initial"); + assert.equal(exports.StaticPropertyHolder.staticBool, true); + assert.equal(exports.StaticPropertyHolder.staticFloat, 3.140000104904175); + assert.equal(exports.StaticPropertyHolder.staticDouble, 2.718); + assert.equal(exports.StaticPropertyHolder.readOnlyComputed, 84); // staticVariable * 2 = 42 * 2 + + // Test static primitive property setters + exports.StaticPropertyHolder.staticVariable = 200; + exports.StaticPropertyHolder.staticString = "updated"; + exports.StaticPropertyHolder.staticBool = false; + exports.StaticPropertyHolder.staticFloat = 6.28; + exports.StaticPropertyHolder.staticDouble = 1.414; + + assert.equal(exports.StaticPropertyHolder.staticVariable, 200); + assert.equal(exports.StaticPropertyHolder.staticString, "updated"); + assert.equal(exports.StaticPropertyHolder.staticBool, false); + assert.equal(exports.StaticPropertyHolder.staticFloat, 6.280000209808350); + assert.equal(exports.StaticPropertyHolder.staticDouble, 1.414); + + // Test static JSObject + const testStaticObj = { staticTest: "object" }; + exports.StaticPropertyHolder.jsObjectProperty = testStaticObj; + assert.equal(exports.StaticPropertyHolder.jsObjectProperty, testStaticObj); + + const newStaticObj = { newStaticProp: "new" }; + exports.StaticPropertyHolder.jsObjectProperty = newStaticObj; + assert.equal(exports.StaticPropertyHolder.jsObjectProperty, newStaticObj); + + // Test static optional properties + assert.equal(exports.StaticPropertyHolder.optionalString, null); + assert.equal(exports.StaticPropertyHolder.optionalInt, null); + exports.StaticPropertyHolder.optionalString = "optional value"; + exports.StaticPropertyHolder.optionalInt = 42; + assert.equal(exports.StaticPropertyHolder.optionalString, "optional value"); + assert.equal(exports.StaticPropertyHolder.optionalInt, 42); + exports.StaticPropertyHolder.optionalString = null; + exports.StaticPropertyHolder.optionalInt = null; + assert.equal(exports.StaticPropertyHolder.optionalString, null); + assert.equal(exports.StaticPropertyHolder.optionalInt, null); + + // Test static computed properties + assert.equal(exports.StaticPropertyHolder.computedProperty, "computed: 200"); + exports.StaticPropertyHolder.computedProperty = "computed: 300"; // Should have parsed and set staticVariable + assert.equal(exports.StaticPropertyHolder.staticVariable, 300); + assert.equal(exports.StaticPropertyHolder.computedProperty, "computed: 300"); + assert.equal(exports.StaticPropertyHolder.readOnlyComputed, 600); // staticVariable * 2 = 300 * 2 + + // Test static properties in enums + assert.equal(StaticPropertyEnum.enumProperty, "enum value"); + assert.equal(StaticPropertyEnum.enumConstant, 42); + assert.equal(StaticPropertyEnum.enumBool, false); + + StaticPropertyEnum.enumProperty = "modified enum"; + StaticPropertyEnum.enumBool = true; + assert.equal(StaticPropertyEnum.enumProperty, "modified enum"); + assert.equal(StaticPropertyEnum.enumBool, true); + + assert.equal(StaticPropertyEnum.enumVariable, 200); + assert.equal(StaticPropertyEnum.computedReadonly, 400); + assert.equal(StaticPropertyEnum.computedReadWrite, "Value: 200"); + StaticPropertyEnum.computedReadWrite = "Value: 500"; + assert.equal(StaticPropertyEnum.enumVariable, 500); + + // Namespace enum static properties + assert.equal(globalThis.StaticPropertyNamespace.namespaceProperty, "namespace"); + assert.equal(globalThis.StaticPropertyNamespace.namespaceConstant, "constant"); + + globalThis.StaticPropertyNamespace.namespaceProperty = "modified namespace"; + assert.equal(globalThis.StaticPropertyNamespace.namespaceProperty, "modified namespace"); + + assert.equal(globalThis.StaticPropertyNamespace.NestedProperties.nestedProperty, 999); + assert.equal(globalThis.StaticPropertyNamespace.NestedProperties.nestedConstant, "nested"); + assert.equal(globalThis.StaticPropertyNamespace.NestedProperties.nestedDouble, 1.414); + + globalThis.StaticPropertyNamespace.NestedProperties.nestedProperty = 1000; + globalThis.StaticPropertyNamespace.NestedProperties.nestedDouble = 2.828; + assert.equal(globalThis.StaticPropertyNamespace.NestedProperties.nestedProperty, 1000); + assert.equal(globalThis.StaticPropertyNamespace.NestedProperties.nestedDouble, 2.828); + // Test class without @JS init constructor const calc = exports.createCalculator(); assert.equal(calc.square(5), 25); From fac9cbd82ff3e37855b428b0dea6c67d70164b09 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Wed, 17 Sep 2025 08:12:05 -0600 Subject: [PATCH 3/7] BridgeJS: Support for static / class properties improvements --- .../Sources/BridgeJSCore/ExportSwift.swift | 349 ++++++++---------- .../Sources/BridgeJSLink/BridgeJSLink.swift | 137 +++---- .../Sources/BridgeJSLink/JSGlueGen.swift | 12 +- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 138 ++++++- .../Inputs/StaticFunctions.swift | 4 +- .../Inputs/StaticProperties.swift | 12 +- .../StaticFunctions.Export.js | 9 +- .../StaticProperties.Export.js | 43 ++- .../ExportSwiftTests/EnumAssociatedValue.json | 28 +- .../ExportSwiftTests/EnumNamespace.json | 90 +++-- .../ExportSwiftTests/StaticFunctions.json | 36 +- .../ExportSwiftTests/StaticFunctions.swift | 6 +- .../ExportSwiftTests/StaticProperties.json | 89 +++-- .../ExportSwiftTests/StaticProperties.swift | 35 ++ .../Exporting-Swift-Static-Functions.md | 2 +- .../BridgeJSRuntimeTests/ExportAPITests.swift | 19 +- .../Generated/BridgeJS.ExportSwift.swift | 70 ++-- .../JavaScript/BridgeJS.ExportSwift.json | 237 +++++++----- 18 files changed, 758 insertions(+), 558 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index b6642fed..12908f9f 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -88,50 +88,19 @@ public class ExportSwift { } } - /// Temporary storage for enum data during visitor traversal since EnumCaseDeclSyntax lacks parent context - struct CurrentEnum { - var name: String? - var cases: [EnumCase] = [] - var rawType: String? - var staticMethods: [ExportedFunction] = [] - var staticProperties: [ExportedProperty] = [] - } - - struct EnumStack { - private var stack: [CurrentEnum] = [] - - var current: CurrentEnum { - get { - return stack.last ?? CurrentEnum() - } - set { - if stack.isEmpty { - stack.append(newValue) - } else { - stack[stack.count - 1] = newValue - } - } - } - - mutating func push(_ enumData: CurrentEnum) { - stack.append(enumData) - } - - mutating func pop() -> CurrentEnum? { - return stack.popLast() - } - - var isEmpty: Bool { - return stack.isEmpty + /// Creates a unique key for an enum by combining name and namespace + private func enumKey(name: String, namespace: [String]?) -> String { + if let namespace = namespace, !namespace.isEmpty { + return "\(namespace.joined(separator: ".")).\(name)" + } else { + return name } } - - var enumStack = EnumStack() enum State { case topLevel case classBody(name: String, key: String) - case enumBody(name: String) + case enumBody(name: String, key: String) } struct StateStack { @@ -189,8 +158,7 @@ public class ExportSwift { } let isStatic = node.modifiers.contains { modifier in - modifier.name.tokenKind == .keyword(.static) || - modifier.name.tokenKind == .keyword(.class) + modifier.name.tokenKind == .keyword(.static) || modifier.name.tokenKind == .keyword(.class) } switch state { @@ -213,15 +181,16 @@ public class ExportSwift { exportedClassByName[classKey]?.methods.append(exportedFunction) } return .skipChildren - case .enumBody(let enumName): + case .enumBody(let enumName, let enumKey): if !isStatic { diagnose(node: node, message: "Only static functions are supported in enums") return .skipChildren } if let exportedFunction = visitFunction(node: node, isStatic: isStatic, enumName: enumName) { - var current = enumStack.current - current.staticMethods.append(exportedFunction) - enumStack.current = current + if var currentEnum = exportedEnumByName[enumKey] { + currentEnum.staticMethods.append(exportedFunction) + exportedEnumByName[enumKey] = currentEnum + } } return .skipChildren } @@ -239,9 +208,19 @@ public class ExportSwift { } let name = node.name.text - let namespace = extractNamespace(from: jsAttribute) - if namespace != nil, case .classBody = state { + let attributeNamespace = extractNamespace(from: jsAttribute) + let computedNamespace = computeNamespace(for: node) + + let finalNamespace: [String]? + + if let computed = computedNamespace, !computed.isEmpty { + finalNamespace = computed + } else { + finalNamespace = attributeNamespace + } + + if attributeNamespace != nil, case .classBody = state { diagnose( node: jsAttribute, message: "Namespace is only needed in top-level declaration", @@ -249,7 +228,7 @@ public class ExportSwift { ) } - if namespace != nil, case .enumBody = state { + if attributeNamespace != nil, case .enumBody = state { diagnose( node: jsAttribute, message: "Namespace is not supported for enum static functions", @@ -308,34 +287,22 @@ public class ExportSwift { abiName = "bjs_\(className)_\(name)" staticContext = nil } - case .enumBody(let enumName): + case .enumBody(let enumName, let enumKey): if !isStatic { diagnose(node: node, message: "Only static functions are supported in enums") return nil } - let isNamespaceEnum = enumStack.current.cases.isEmpty + let isNamespaceEnum = exportedEnumByName[enumKey]?.cases.isEmpty ?? true + staticContext = isNamespaceEnum ? .namespaceEnum : .enumName(enumName) - if isNamespaceEnum { - // For namespace enums, compute the full Swift call path manually - var swiftPath: [String] = [] - var currentNode: Syntax? = node.parent - while let parent = currentNode { - if let enumDecl = parent.as(EnumDeclSyntax.self), - enumDecl.attributes.hasJSAttribute() - { - swiftPath.insert(enumDecl.name.text, at: 0) - } - currentNode = parent.parent - } - let fullEnumCallName = swiftPath.joined(separator: ".") - - // ABI name should include full namespace path to avoid conflicts - abiName = "bjs_\(swiftPath.joined(separator: "_"))_\(name)" - staticContext = .namespaceEnum(fullEnumCallName) + if isNamespaceEnum, let namespace = finalNamespace, !namespace.isEmpty { + // For namespace enums, use ONLY the resolved namespace to avoid duplication + // The finalNamespace already contains the correct namespace path + let abiNamespace = namespace.joined(separator: "_") + abiName = "bjs_\(abiNamespace)_static_\(name)" } else { abiName = "bjs_\(enumName)_static_\(name)" - staticContext = .enumName(enumName) } } @@ -349,7 +316,7 @@ public class ExportSwift { parameters: parameters, returnType: returnType, effects: effects, - namespace: namespace, + namespace: finalNamespace, // UPDATED: Use resolved namespace staticContext: staticContext ) } @@ -414,9 +381,9 @@ public class ExportSwift { } override func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind { - guard node.attributes.hasJSAttribute() else { return .skipChildren } + guard let jsAttribute = node.attributes.firstJSAttribute else { return .skipChildren } guard case .classBody(let className, _) = state else { - if case .enumBody(_) = state { + if case .enumBody(_, _) = state { diagnose(node: node, message: "Initializers are not supported inside enums") } else { diagnose(node: node, message: "@JS init must be inside a @JS class") @@ -424,9 +391,7 @@ public class ExportSwift { return .skipChildren } - if let jsAttribute = node.attributes.firstJSAttribute, - extractNamespace(from: jsAttribute) != nil - { + if extractNamespace(from: jsAttribute) != nil { diagnose( node: jsAttribute, message: "Namespace is not supported for initializer declarations", @@ -461,63 +426,54 @@ public class ExportSwift { } override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind { - guard node.attributes.hasJSAttribute() else { return .skipChildren } - + guard let jsAttribute = node.attributes.firstJSAttribute else { return .skipChildren } + let isStatic = node.modifiers.contains { modifier in - modifier.name.tokenKind == .keyword(.static) || - modifier.name.tokenKind == .keyword(.class) + modifier.name.tokenKind == .keyword(.static) || modifier.name.tokenKind == .keyword(.class) + } + + let attributeNamespace = extractNamespace(from: jsAttribute) + if attributeNamespace != nil { + diagnose( + node: node.attributes.firstJSAttribute!, + message: "Namespace parameter within @JS attribute is not supported for property declarations", + hint: + "Remove the namespace from @JS attribute. If you need dedicated namespace, consider using a nested enum or class instead." + ) } - + + let computedNamespace = computeNamespace(for: node) + let finalNamespace: [String]? + + if let computed = computedNamespace, !computed.isEmpty { + finalNamespace = computed + } else { + finalNamespace = nil + } + + // Determine static context and validate placement let staticContext: StaticContext? let classKey: String - + switch state { case .classBody(let className, let key): classKey = key - if isStatic { - staticContext = .className(className) - } else { - staticContext = nil - } - case .enumBody(let enumName): + staticContext = isStatic ? .className(className) : nil + case .enumBody(let enumName, let enumKey): if !isStatic { diagnose(node: node, message: "Only static properties are supported in enums") return .skipChildren } - classKey = enumName // We'll handle enum properties differently - - let isNamespaceEnum = enumStack.current.cases.isEmpty - if isNamespaceEnum { - var swiftPath: [String] = [] - var currentNode: Syntax? = node.parent - while let parent = currentNode { - if let enumDecl = parent.as(EnumDeclSyntax.self), - enumDecl.attributes.hasJSAttribute() - { - swiftPath.insert(enumDecl.name.text, at: 0) - } - currentNode = parent.parent - } - let fullEnumCallName = swiftPath.joined(separator: ".") - staticContext = .namespaceEnum(fullEnumCallName) - } else { - staticContext = .enumName(enumName) - } + classKey = enumKey + + let isNamespaceEnum = exportedEnumByName[enumKey]?.cases.isEmpty ?? true + staticContext = isStatic ? (isNamespaceEnum ? .namespaceEnum : .enumName(enumName)) : nil + case .topLevel: diagnose(node: node, message: "@JS var must be inside a @JS class or enum") return .skipChildren } - if let jsAttribute = node.attributes.firstJSAttribute, - extractNamespace(from: jsAttribute) != nil - { - diagnose( - node: jsAttribute, - message: "Namespace is not supported for property declarations", - hint: "Remove the namespace from @JS attribute" - ) - } - // Process each binding (variable declaration) for binding in node.bindings { guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self) else { @@ -563,13 +519,15 @@ public class ExportSwift { type: propertyType, isReadonly: isReadonly, isStatic: isStatic, + namespace: finalNamespace, staticContext: staticContext ) - if case .enumBody(_) = state { - var current = enumStack.current - current.staticProperties.append(exportedProperty) - enumStack.current = current + if case .enumBody(_, let enumKey) = state { + if var currentEnum = exportedEnumByName[enumKey] { + currentEnum.staticProperties.append(exportedProperty) + exportedEnumByName[enumKey] = currentEnum + } } else { exportedClassByName[classKey]?.properties.append(exportedProperty) } @@ -656,46 +614,59 @@ public class ExportSwift { return .skipChildren } - let newEnum = CurrentEnum() - enumStack.push(newEnum) - - var current = enumStack.current - current.name = name - current.cases = [] - current.rawType = rawType - enumStack.current = current + let effectiveNamespace = computedNamespace ?? attributeNamespace + let emitStyle = extractEnumStyle(from: jsAttribute) ?? .const + let swiftCallName = ExportSwift.computeSwiftCallName(for: node, itemName: name) + let explicitAccessControl = computeExplicitAtLeastInternalAccessControl( + for: node, + message: "Enum visibility must be at least internal" + ) + + // Create enum directly in dictionary + let exportedEnum = ExportedEnum( + name: name, + swiftCallName: swiftCallName, + explicitAccessControl: explicitAccessControl, + cases: [], // Will be populated in visit(EnumCaseDeclSyntax) + rawType: rawType, + namespace: effectiveNamespace, + emitStyle: emitStyle, + staticMethods: [], + staticProperties: [] + ) + + let enumUniqueKey = enumKey(name: name, namespace: effectiveNamespace) + exportedEnumByName[enumUniqueKey] = exportedEnum + exportedEnumNames.append(enumUniqueKey) - stateStack.push(state: .enumBody(name: name)) + stateStack.push(state: .enumBody(name: name, key: enumUniqueKey)) return .visitChildren } override func visitPost(_ node: EnumDeclSyntax) { - guard let jsAttribute = node.attributes.firstJSAttribute, - let enumName = enumStack.current.name - else { + guard let jsAttribute = node.attributes.firstJSAttribute else { // Only pop if we have a valid enum that was processed - if case .enumBody(_) = stateStack.current { + if case .enumBody(_, _) = stateStack.current { stateStack.pop() - _ = enumStack.pop() // Also pop the enum stack } return } - let attributeNamespace = extractNamespace(from: jsAttribute) - let computedNamespace = computeNamespace(for: node) + guard case .enumBody(_, let enumKey) = stateStack.current else { + return + } - let effectiveNamespace: [String]? - if computedNamespace == nil && attributeNamespace != nil { - effectiveNamespace = attributeNamespace - } else { - effectiveNamespace = computedNamespace + guard let exportedEnum = exportedEnumByName[enumKey] else { + stateStack.pop() + return } - let emitStyle = extractEnumStyle(from: jsAttribute) ?? .const + let emitStyle = exportedEnum.emitStyle if case .tsEnum = emitStyle { - if let raw = enumStack.current.rawType, + // Check for Bool raw type limitation + if let raw = exportedEnum.rawType, let rawEnum = SwiftEnumRawType.from(raw), rawEnum == .bool { diagnose( @@ -705,8 +676,8 @@ public class ExportSwift { ) } - if !enumStack.current.staticMethods.isEmpty { - + // Check for static functions limitation + if !exportedEnum.staticMethods.isEmpty { diagnose( node: jsAttribute, message: "TypeScript enum style does not support static functions", @@ -715,7 +686,7 @@ public class ExportSwift { } } - if enumStack.current.cases.contains(where: { !$0.associatedValues.isEmpty }) { + if exportedEnum.cases.contains(where: { !$0.associatedValues.isEmpty }) { if case .tsEnum = emitStyle { diagnose( node: jsAttribute, @@ -723,7 +694,7 @@ public class ExportSwift { hint: "Use enumStyle: .const in order to map associated-value enums" ) } - for enumCase in enumStack.current.cases { + for enumCase in exportedEnum.cases { for associatedValue in enumCase.associatedValues { switch associatedValue.type { case .string, .int, .float, .double, .bool: @@ -752,37 +723,20 @@ 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 currentEnumData = enumStack.current - let exportedEnum = ExportedEnum( - name: enumName, - swiftCallName: swiftCallName, - explicitAccessControl: explicitAccessControl, - cases: currentEnumData.cases, - rawType: currentEnumData.rawType, - namespace: effectiveNamespace, - emitStyle: emitStyle, - staticMethods: currentEnumData.staticMethods, - staticProperties: currentEnumData.staticProperties - ) - exportedEnumByName[enumName] = exportedEnum - exportedEnumNames.append(enumName) - - _ = enumStack.pop() // Pop the completed enum from stack stateStack.pop() } override func visit(_ node: EnumCaseDeclSyntax) -> SyntaxVisitorContinueKind { + guard case .enumBody(_, let enumKey) = stateStack.current else { + return .visitChildren + } + for element in node.elements { let caseName = element.name.text let rawValue: String? var associatedValues: [AssociatedValue] = [] - if enumStack.current.rawType != nil { + if exportedEnumByName[enumKey]?.rawType != nil { if let stringLiteral = element.rawValue?.value.as(StringLiteralExprSyntax.self) { rawValue = stringLiteral.segments.first?.as(StringSegmentSyntax.self)?.content.text } else if let boolLiteral = element.rawValue?.value.as(BooleanLiteralExprSyntax.self) { @@ -817,10 +771,7 @@ public class ExportSwift { rawValue: rawValue, associatedValues: associatedValues ) - - var current = enumStack.current - current.cases.append(enumCase) - enumStack.current = current + exportedEnumByName[enumKey]?.cases.append(enumCase) } return .visitChildren @@ -1034,14 +985,17 @@ public class ExportSwift { case .namespace: () } - + for staticMethod in enumDef.staticMethods { decls.append(try renderSingleExportedFunction(function: staticMethod)) } - + for staticProperty in enumDef.staticProperties { - let getterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false, isStatic: true)) - + let getterBuilder = ExportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: false, isStatic: true) + ) + + // Use context-aware call name based on static context and namespace let staticCallName: String if let staticContext = staticProperty.staticContext { switch staticContext { @@ -1049,21 +1003,28 @@ public class ExportSwift { staticCallName = "\(className).\(staticProperty.name)" case .enumName(let enumName): staticCallName = "\(enumName).\(staticProperty.name)" - case .namespaceEnum(let enumName): - staticCallName = "\(enumName).\(staticProperty.name)" - case .explicitNamespace(let namespace): - staticCallName = "\(namespace.joined(separator: ".")).\(staticProperty.name)" + case .namespaceEnum: + // For namespace enums and explicit namespace, use the namespace property + if let namespace = staticProperty.namespace, !namespace.isEmpty { + let namespacePath = namespace.joined(separator: ".") + staticCallName = "\(namespacePath).\(staticProperty.name)" + } else { + // Fallback to using the enum's swift call name + staticCallName = "\(enumDef.swiftCallName).\(staticProperty.name)" + } } } else { staticCallName = "\(enumDef.swiftCallName).\(staticProperty.name)" } - + getterBuilder.callStaticProperty(name: staticCallName, returnType: staticProperty.type) try getterBuilder.lowerReturnValue(returnType: staticProperty.type) decls.append(getterBuilder.render(abiName: staticProperty.getterAbiName(className: enumDef.name))) if !staticProperty.isReadonly { - let setterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false, isStatic: true)) + let setterBuilder = ExportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: false, isStatic: true) + ) try setterBuilder.liftParameter( param: Parameter(label: "value", name: "value", type: staticProperty.type) ) @@ -1495,7 +1456,7 @@ public class ExportSwift { for param in function.parameters { try builder.liftParameter(param: param) } - + if function.effects.isStatic, let staticContext = function.staticContext { let callName: String switch staticContext { @@ -1503,16 +1464,20 @@ public class ExportSwift { callName = "\(className).\(function.name)" case .enumName(let enumName): callName = "\(enumName).\(function.name)" - case .namespaceEnum(let enumName): - callName = "\(enumName).\(function.name)" - case .explicitNamespace(let namespace): - callName = "\(namespace.joined(separator: ".")).\(function.name)" + case .namespaceEnum: + // For namespace enums and explicit namespace, use the namespace property + if let namespace = function.namespace, !namespace.isEmpty { + callName = "\(namespace.joined(separator: ".")).\(function.name)" + } else { + // Fallback to just the function name for functions without namespace + callName = function.name + } } builder.call(name: callName, returnType: function.returnType) } else { builder.call(name: function.name, returnType: function.returnType) } - + try builder.lowerReturnValue(returnType: function.returnType) return builder.render(abiName: function.abiName) } @@ -1578,7 +1543,7 @@ public class ExportSwift { } for method in klass.methods { let builder = ExportedThunkBuilder(effects: method.effects) - + if method.effects.isStatic { for param in method.parameters { try builder.liftParameter(param: param) @@ -1605,7 +1570,9 @@ public class ExportSwift { for property in klass.properties { if property.isStatic { // Generate static property getter - let getterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false, isStatic: true)) + let getterBuilder = ExportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: false, isStatic: true) + ) let staticCallName = "\(klass.swiftCallName).\(property.name)" getterBuilder.callStaticProperty(name: staticCallName, returnType: property.type) try getterBuilder.lowerReturnValue(returnType: property.type) @@ -1613,7 +1580,9 @@ public class ExportSwift { // Generate static property setter if not readonly if !property.isReadonly { - let setterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false, isStatic: true)) + let setterBuilder = ExportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: false, isStatic: true) + ) try setterBuilder.liftParameter( param: Parameter(label: "value", name: "value", type: property.type) ) diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 789188a1..f29dfeb3 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -155,7 +155,7 @@ struct BridgeJSLink { var (js, dts) = try renderExportedFunction(function: function) if function.effects.isStatic, - case .namespaceEnum(_) = function.staticContext + case .namespaceEnum = function.staticContext { data.namespacedFunctions.append(function) } else if function.namespace != nil { @@ -175,25 +175,31 @@ struct BridgeJSLink { let fullNamespace = (enumDefinition.namespace ?? []) + [enumDefinition.name] functionWithNamespace.namespace = fullNamespace data.namespacedFunctions.append(functionWithNamespace) - + var (js, dts) = try renderExportedFunction(function: functionWithNamespace) js[0] = "\(functionWithNamespace.name): " + js[0] js[js.count - 1] += "," data.exportsLines.append(contentsOf: js) data.dtsExportLines.append(contentsOf: dts) } - + for property in enumDefinition.staticProperties { let fullNamespace = (enumDefinition.namespace ?? []) + [enumDefinition.name] let namespacePath = fullNamespace.joined(separator: ".") - let (propJs, _) = try renderNamespaceStaticProperty(property: property, namespacePath: namespacePath) + let (propJs, _) = try renderNamespaceStaticProperty( + property: property, + namespacePath: namespacePath + ) data.enumStaticAssignments.append(contentsOf: propJs) } } for enumDefinition in skeleton.enums where enumDefinition.enumType != .namespace { for function in enumDefinition.staticMethods { - let assignmentJs = try renderEnumStaticFunctionAssignment(function: function, enumName: enumDefinition.name) + let assignmentJs = try renderEnumStaticFunctionAssignment( + function: function, + enumName: enumDefinition.name + ) data.enumStaticAssignments.append(contentsOf: assignmentJs) } for property in enumDefinition.staticProperties { @@ -629,7 +635,7 @@ struct BridgeJSLink { if hasAnyNamespacedItems { var allUniqueNamespaces: [String] = [] var seen = Set() - + let functionNamespacePaths: Set<[String]> = Set( data.namespacedFunctions.compactMap { $0.namespace } ) @@ -637,25 +643,27 @@ struct BridgeJSLink { data.namespacedClasses.compactMap { $0.namespace } ) let allRegularNamespacePaths = functionNamespacePaths.union(classNamespacePaths) - + let enumNamespacePaths: Set<[String]> = Set( data.namespacedEnums.compactMap { $0.namespace } ) - - var namespaceEnumPropertyPaths: Set<[String]> = Set() + + // From static properties with namespace property (collect their complete namespace paths) + var staticPropertyNamespacePaths: Set<[String]> = Set() for skeleton in exportedSkeletons { for enumDefinition in skeleton.enums { for property in enumDefinition.staticProperties { - if case .namespaceEnum(let fullNamespacePath) = property.staticContext { - let namespaceParts = fullNamespacePath.split(separator: ".").map(String.init) - namespaceEnumPropertyPaths.insert(namespaceParts) + if let namespace = property.namespace, !namespace.isEmpty { + staticPropertyNamespacePaths.insert(namespace) } } } } - - let allNamespacePaths = allRegularNamespacePaths.union(enumNamespacePaths).union(namespaceEnumPropertyPaths) - + + let allNamespacePaths = allRegularNamespacePaths.union(enumNamespacePaths).union( + staticPropertyNamespacePaths + ) + allNamespacePaths.forEach { namespacePath in namespacePath.enumerated().forEach { (index, _) in let path = namespacePath[0...index].joined(separator: ".") @@ -664,7 +672,7 @@ struct BridgeJSLink { } } } - + allUniqueNamespaces.sorted().forEach { namespace in printer.write("if (typeof globalThis.\(namespace) === 'undefined') {") printer.indent { @@ -676,14 +684,14 @@ struct BridgeJSLink { // NOW assign enum static function/property implementations (namespaces are ready) printer.write(lines: data.enumStaticAssignments) - + if hasAnyNamespacedItems { printer.write("const exports = {") printer.indent { printer.write(lines: data.exportsLines) } printer.write("};") - + data.namespacedClasses.forEach { klass in let namespacePath: String = klass.namespace?.joined(separator: ".") ?? "" printer.write("globalThis.\(namespacePath).\(klass.name) = exports.\(klass.name);") @@ -954,7 +962,6 @@ struct BridgeJSLink { return (jsLines, dtsLines) } - private func generateDeclarations(enumDefinition: ExportedEnum) -> [String] { let printer = CodeFragmentPrinter() @@ -1120,10 +1127,14 @@ extension BridgeJSLink { return try renderClassStaticFunction(function: function, className: className) case .enumName(let enumName): return try renderEnumStaticFunction(function: function, enumName: enumName) - case .namespaceEnum(let enumName): - return try renderNamespaceFunction(function: function, namespace: enumName) - case .explicitNamespace(let namespace): - return try renderNamespaceFunction(function: function, namespace: namespace.joined(separator: ".")) + case .namespaceEnum: + // Use function's namespace property for namespace enum + if let namespace = function.namespace, !namespace.isEmpty { + return try renderNamespaceFunction(function: function, namespace: namespace.joined(separator: ".")) + } else { + // Fallback to regular function rendering + return try renderExportedFunction(function: function) + } } } @@ -1213,7 +1224,9 @@ extension BridgeJSLink { let returnExpr = try thunkBuilder.call(abiName: function.abiName, returnType: function.returnType) let printer = CodeFragmentPrinter() - printer.write("\(enumName).\(function.name) = function(\(function.parameters.map { $0.name }.joined(separator: ", "))) {") + printer.write( + "\(enumName).\(function.name) = function(\(function.parameters.map { $0.name }.joined(separator: ", "))) {" + ) printer.indent { printer.write(contentsOf: thunkBuilder.body) printer.write(contentsOf: thunkBuilder.cleanupCode) @@ -1233,7 +1246,7 @@ extension BridgeJSLink { enumName: String ) throws -> (js: [String], dts: [String]) { var jsLines: [String] = [] - + // Generate getter assignment let getterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) let getterReturnExpr = try getterThunkBuilder.call( @@ -1247,17 +1260,17 @@ extension BridgeJSLink { returnExpr: getterReturnExpr, declarationPrefixKeyword: nil ) - + // Build Object.defineProperty call var definePropertyLines: [String] = [] definePropertyLines.append("Object.defineProperty(\(enumName), '\(property.name)', { get: function() {") - + // Add getter body (skip function declaration and closing brace) if getterLines.count > 2 { - let bodyLines = Array(getterLines[1.. 2 { - let bodyLines = Array(setterLines[1.. (js: [String], dts: [String]) { var jsLines: [String] = [] - + // Generate getter assignment let getterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) // Use the last component of namespace as the className for ABI name generation @@ -1318,17 +1331,19 @@ extension BridgeJSLink { returnExpr: getterReturnExpr, declarationPrefixKeyword: nil ) - + // Build Object.defineProperty call for namespace var definePropertyLines: [String] = [] - definePropertyLines.append("Object.defineProperty(globalThis.\(namespacePath), '\(property.name)', { get: function() {") - + definePropertyLines.append( + "Object.defineProperty(globalThis.\(namespacePath), '\(property.name)', { get: function() {" + ) + // Add getter body (skip function declaration and closing brace) if getterLines.count > 2 { - let bodyLines = Array(getterLines[1.. 2 { - let bodyLines = Array(setterLines[1.. String { + + let namespacePart: String + if let namespace = namespace, !namespace.isEmpty { + namespacePart = namespace.joined(separator: "_") + } else { + namespacePart = "" + } + + let contextPart: String + if let staticContext = staticContext { + switch staticContext { + case .className(let name), .enumName(let name): + contextPart = name + case .namespaceEnum: + contextPart = namespacePart + + } + } else { + contextPart = namespacePart + } + + var components = ["bjs"] + if !contextPart.isEmpty { + components.append(contextPart) + } + + if staticContext != nil { + components.append("static") + } + + components.append(baseName) + + if let operation = operation { + components.append(operation) + } + + return components.joined(separator: "_") + } +} + // MARK: - Types public enum BridgeType: Codable, Equatable, Sendable { @@ -91,8 +143,7 @@ public struct Effects: Codable, Equatable, Sendable { public enum StaticContext: Codable, Equatable, Sendable { case className(String) case enumName(String) - case namespaceEnum(String) - case explicitNamespace([String]) + case namespaceEnum } // MARK: - Enum Skeleton @@ -131,7 +182,7 @@ public struct ExportedEnum: Codable, Equatable, Sendable { public let name: String public let swiftCallName: String public let explicitAccessControl: String? - public let cases: [EnumCase] + public var cases: [EnumCase] public let rawType: String? public let namespace: [String]? public let emitStyle: EnumEmitStyle @@ -205,6 +256,19 @@ public struct ExportedFunction: Codable, Equatable, Sendable { self.namespace = namespace self.staticContext = staticContext } + + // MARK: - Unified ABI Name Generation (New Approach) + + /// Generates ABI name using the new unified approach + /// This method uses the standardized namespace property and ABINameGenerator + public func abiNameUnified() -> String { + return ABINameGenerator.generateABIName( + baseName: name, + namespace: namespace, + staticContext: effects.isStatic ? staticContext : nil, + operation: nil + ) + } } public struct ExportedClass: Codable { @@ -253,32 +317,40 @@ public struct ExportedProperty: Codable, Equatable, Sendable { public var name: String public var type: BridgeType public var isReadonly: Bool - public var isStatic: Bool = false + public var isStatic: Bool + public var namespace: [String]? public var staticContext: StaticContext? - public init(name: String, type: BridgeType, isReadonly: Bool = false, isStatic: Bool = false, staticContext: StaticContext? = nil) { + public init( + name: String, + type: BridgeType, + isReadonly: Bool = false, + isStatic: Bool = false, + namespace: [String]? = nil, + staticContext: StaticContext? = nil + ) { self.name = name self.type = type self.isReadonly = isReadonly self.isStatic = isStatic + self.namespace = namespace self.staticContext = staticContext } public func getterAbiName(className: String) -> String { if isStatic, let staticContext = staticContext { - // Generate context-aware ABI names for static properties switch staticContext { case .className(let className): return "bjs_\(className)_static_\(name)_get" case .enumName(let enumName): return "bjs_\(enumName)_static_\(name)_get" - case .namespaceEnum(let enumName): - // Convert dots to underscores for namespace enums - let abiEnumName = enumName.split(separator: ".").joined(separator: "_") - return "bjs_\(abiEnumName)_static_\(name)_get" - case .explicitNamespace(let namespace): - let abiNamespace = namespace.joined(separator: "_") - return "bjs_\(abiNamespace)_static_\(name)_get" + case .namespaceEnum: + if let namespace = namespace, !namespace.isEmpty { + let abiNamespace = namespace.joined(separator: "_") + return "bjs_\(abiNamespace)_static_\(name)_get" + } else { + return "bjs_static_\(name)_get" + } } } else if isStatic { return "bjs_\(className)_static_\(name)_get" @@ -295,13 +367,15 @@ public struct ExportedProperty: Codable, Equatable, Sendable { return "bjs_\(className)_static_\(name)_set" case .enumName(let enumName): return "bjs_\(enumName)_static_\(name)_set" - case .namespaceEnum(let enumName): - // Convert dots to underscores for namespace enums - let abiEnumName = enumName.split(separator: ".").joined(separator: "_") - return "bjs_\(abiEnumName)_static_\(name)_set" - case .explicitNamespace(let namespace): - let abiNamespace = namespace.joined(separator: "_") - return "bjs_\(abiNamespace)_static_\(name)_set" + case .namespaceEnum: + // For namespace enums, use ONLY the namespace property to avoid duplication + if let namespace = namespace, !namespace.isEmpty { + let abiNamespace = namespace.joined(separator: "_") + return "bjs_\(abiNamespace)_static_\(name)_set" + } else { + // Fallback if no namespace is available + return "bjs_static_\(name)_set" + } } } else if isStatic { return "bjs_\(className)_static_\(name)_set" @@ -309,6 +383,30 @@ public struct ExportedProperty: Codable, Equatable, Sendable { return "bjs_\(className)_\(name)_set" } } + + // MARK: - Unified ABI Name Generation (New Approach) + + /// Generates getter ABI name using the new unified approach + /// This method uses the standardized namespace property and ABINameGenerator + public func getterAbiNameUnified() -> String { + return ABINameGenerator.generateABIName( + baseName: name, + namespace: namespace, + staticContext: isStatic ? staticContext : nil, + operation: "get" + ) + } + + /// Generates setter ABI name using the new unified approach + /// This method uses the standardized namespace property and ABINameGenerator + public func setterAbiNameUnified() -> String { + return ABINameGenerator.generateABIName( + baseName: name, + namespace: namespace, + staticContext: isStatic ? staticContext : nil, + operation: "set" + ) + } } public struct ExportedSkeleton: Codable { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticFunctions.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticFunctions.swift index 120d6888..1d42cf41 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticFunctions.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticFunctions.swift @@ -4,7 +4,7 @@ @JS class func subtract(a: Int, b: Int) -> Int { return a - b } - + @JS static func add(a: Int, b: Int) -> Int { return a + b } @@ -28,7 +28,7 @@ enum APIResult { case success(String) case failure(Int) - @JS static func roundtrip(value: APIResult) -> APIResult { } + @JS static func roundtrip(value: APIResult) -> APIResult {} } @JS enum Utils { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticProperties.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticProperties.swift index 656b4da0..1a89b55c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticProperties.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StaticProperties.swift @@ -1,27 +1,27 @@ @JS class PropertyClass { @JS init() {} - + @JS static let staticConstant: String = "constant" @JS static var staticVariable: Int = 42 @JS static var jsObjectProperty: JSObject = JSObject() @JS class var classVariable: String = "overridable" - + @JS static var computedProperty: String { get { return "\(staticVariable) computed" } set { staticVariable = newValue + 5 } } - + @JS static var readOnlyComputed: Int { return 100 } - + @JS static var optionalProperty: String? = nil } @JS enum PropertyEnum { case value1 case value2 - + @JS static var enumProperty: String = "enum value" @JS static let enumConstant: Int = 42 @JS static var computedEnum: String { @@ -33,7 +33,7 @@ @JS enum PropertyNamespace { @JS static var namespaceProperty: String = "namespace" @JS static let namespaceConstant: String = "constant" - + @JS enum Nested { @JS nonisolated(unsafe) static var nestedProperty: Int = 999 @JS static let nestedConstant: String = "nested" diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js index f294faa5..f630453d 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js @@ -57,6 +57,11 @@ const __bjs_createAPIResultHelpers = () => { } }); }; +if (typeof globalThis.Utils === 'undefined') { + globalThis.Utils = {}; +} + + export async function createInstantiator(options, swift) { let instance; let memory; @@ -275,10 +280,10 @@ export async function createInstantiator(options, swift) { }; const exports = { MathUtils, - uppercase: function bjs_Utils_String_uppercase(text) { + uppercase: function bjs_Utils_String_static_uppercase(text) { const textBytes = textEncoder.encode(text); const textId = swift.memory.retain(textBytes); - instance.exports.bjs_Utils_String_uppercase(textId, textBytes.length); + instance.exports.bjs_Utils_String_static_uppercase(textId, textBytes.length); const ret = tmpRetString; tmpRetString = undefined; swift.memory.release(textId); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js index f02ca0a2..885deca2 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js @@ -263,6 +263,32 @@ export async function createInstantiator(options, swift) { } } } +<<<<<<< HEAD +======= + if (typeof globalThis.PropertyNamespace === 'undefined') { + globalThis.PropertyNamespace = {}; + } + if (typeof globalThis.PropertyNamespace.Nested === 'undefined') { + globalThis.PropertyNamespace.Nested = {}; + } + Object.defineProperty(globalThis.PropertyNamespace, 'namespaceProperty', { get: function() { + instance.exports.bjs_PropertyNamespace_static_namespaceProperty_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + }, set: function(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_PropertyNamespace_static_namespaceProperty_set(valueId, valueBytes.length); + swift.memory.release(valueId); + } }); + Object.defineProperty(globalThis.PropertyNamespace, 'namespaceConstant', { get: function() { + instance.exports.bjs_PropertyNamespace_static_namespaceConstant_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } }); +>>>>>>> 26a78490 (WIP: Enum handling and nesting simplifications (+1 squashed commit)) Object.defineProperty(globalThis.PropertyNamespace.Nested, 'nestedProperty', { get: function() { const ret = instance.exports.bjs_PropertyNamespace_Nested_static_nestedProperty_get(); return ret; @@ -281,23 +307,6 @@ export async function createInstantiator(options, swift) { }, set: function(value) { instance.exports.bjs_PropertyNamespace_Nested_static_nestedDouble_set(value); } }); - Object.defineProperty(globalThis.PropertyNamespace, 'namespaceProperty', { get: function() { - instance.exports.bjs_PropertyNamespace_static_namespaceProperty_get(); - const ret = tmpRetString; - tmpRetString = undefined; - return ret; - }, set: function(value) { - const valueBytes = textEncoder.encode(value); - const valueId = swift.memory.retain(valueBytes); - instance.exports.bjs_PropertyNamespace_static_namespaceProperty_set(valueId, valueBytes.length); - swift.memory.release(valueId); - } }); - Object.defineProperty(globalThis.PropertyNamespace, 'namespaceConstant', { get: function() { - instance.exports.bjs_PropertyNamespace_static_namespaceConstant_get(); - const ret = tmpRetString; - tmpRetString = undefined; - return ret; - } }); Object.defineProperty(PropertyEnum, 'enumProperty', { get: function() { instance.exports.bjs_PropertyEnum_static_enumProperty_get(); const ret = tmpRetString; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json index 105cd31d..6a9cf04c 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumAssociatedValue.json @@ -252,6 +252,20 @@ ], "swiftCallName" : "ComplexResult" }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Utilities", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Utilities" + }, { "cases" : [ { @@ -325,20 +339,6 @@ ], "swiftCallName" : "Utilities.Result" }, - { - "cases" : [ - - ], - "emitStyle" : "const", - "name" : "Utilities", - "staticMethods" : [ - - ], - "staticProperties" : [ - - ], - "swiftCallName" : "Utilities" - }, { "cases" : [ { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json index 8dc3431e..673cfccc 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json @@ -21,6 +21,9 @@ "isThrows" : false }, "name" : "toString", + "namespace" : [ + "Utils" + ], "parameters" : [ { "label" : "value", @@ -69,6 +72,10 @@ "isThrows" : false }, "name" : "call", + "namespace" : [ + "Networking", + "API" + ], "parameters" : [ { "label" : "_", @@ -118,6 +125,11 @@ "isThrows" : false }, "name" : "call", + "namespace" : [ + "Networking", + "APIV2", + "Internal" + ], "parameters" : [ { "label" : "_", @@ -163,6 +175,37 @@ ], "swiftCallName" : "Utils" }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Networking", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Networking" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "API", + "namespace" : [ + "Networking" + ], + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Networking.API" + }, { "cases" : [ { @@ -209,31 +252,14 @@ ], "emitStyle" : "const", - "name" : "API", - "namespace" : [ - "Networking" - ], - "staticMethods" : [ - - ], - "staticProperties" : [ - - ], - "swiftCallName" : "Networking.API" - }, - { - "cases" : [ - - ], - "emitStyle" : "const", - "name" : "Networking", + "name" : "Configuration", "staticMethods" : [ ], "staticProperties" : [ ], - "swiftCallName" : "Networking" + "swiftCallName" : "Configuration" }, { "cases" : [ @@ -323,14 +349,18 @@ ], "emitStyle" : "const", - "name" : "Configuration", + "name" : "Internal", + "namespace" : [ + "Networking", + "APIV2" + ], "staticMethods" : [ ], "staticProperties" : [ ], - "swiftCallName" : "Configuration" + "swiftCallName" : "Internal" }, { "cases" : [ @@ -361,24 +391,6 @@ ], "swiftCallName" : "Internal.SupportedMethod" - }, - { - "cases" : [ - - ], - "emitStyle" : "const", - "name" : "Internal", - "namespace" : [ - "Networking", - "APIV2" - ], - "staticMethods" : [ - - ], - "staticProperties" : [ - - ], - "swiftCallName" : "Internal" } ], "functions" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.json index 6277494a..8a73c1b8 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.json @@ -257,6 +257,20 @@ { "cases" : [ + ], + "emitStyle" : "const", + "name" : "Utils", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Utils" + }, + { + "cases" : [ + ], "emitStyle" : "const", "name" : "String", @@ -265,13 +279,17 @@ ], "staticMethods" : [ { - "abiName" : "bjs_Utils_String_uppercase", + "abiName" : "bjs_Utils_String_static_uppercase", "effects" : { "isAsync" : false, "isStatic" : true, "isThrows" : false }, "name" : "uppercase", + "namespace" : [ + "Utils", + "String" + ], "parameters" : [ { "label" : "_", @@ -290,7 +308,7 @@ }, "staticContext" : { "namespaceEnum" : { - "_0" : "Utils.String" + } } } @@ -299,20 +317,6 @@ ], "swiftCallName" : "Utils.String" - }, - { - "cases" : [ - - ], - "emitStyle" : "const", - "name" : "Utils", - "staticMethods" : [ - - ], - "staticProperties" : [ - - ], - "swiftCallName" : "Utils" } ], "functions" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.swift index 36923e4a..4e301151 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticFunctions.swift @@ -90,9 +90,9 @@ public func _bjs_APIResult_static_roundtrip(value: Int32) -> Void { #endif } -@_expose(wasm, "bjs_Utils_String_uppercase") -@_cdecl("bjs_Utils_String_uppercase") -public func _bjs_Utils_String_uppercase(textBytes: Int32, textLength: Int32) -> Void { +@_expose(wasm, "bjs_Utils_String_static_uppercase") +@_cdecl("bjs_Utils_String_static_uppercase") +public func _bjs_Utils_String_static_uppercase(textBytes: Int32, textLength: Int32) -> Void { #if arch(wasm32) let ret = Utils.String.uppercase(_: String.bridgeJSLiftParameter(textBytes, textLength)) return ret.bridgeJSLowerReturn() diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.json index 4b00c7e9..15bebde7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.json @@ -205,10 +205,7 @@ ], "emitStyle" : "const", - "name" : "Nested", - "namespace" : [ - "PropertyNamespace" - ], + "name" : "PropertyNamespace", "staticMethods" : [ ], @@ -216,26 +213,14 @@ { "isReadonly" : false, "isStatic" : true, - "name" : "nestedProperty", + "name" : "namespaceProperty", + "namespace" : [ + "PropertyNamespace" + ], "staticContext" : { "namespaceEnum" : { - "_0" : "PropertyNamespace.Nested" - } - }, - "type" : { - "int" : { } - } - }, - { - "isReadonly" : true, - "isStatic" : true, - "name" : "nestedConstant", - "staticContext" : { - "namespaceEnum" : { - "_0" : "PropertyNamespace.Nested" - } }, "type" : { "string" : { @@ -244,29 +229,41 @@ } }, { - "isReadonly" : false, + "isReadonly" : true, "isStatic" : true, - "name" : "nestedDouble", + "name" : "namespaceConstant", + "namespace" : [ + "PropertyNamespace" + ], "staticContext" : { "namespaceEnum" : { - "_0" : "PropertyNamespace.Nested" + } }, "type" : { - "double" : { + "string" : { } } } ], - "swiftCallName" : "PropertyNamespace.Nested" + "swiftCallName" : "PropertyNamespace" +<<<<<<< HEAD + } + ], + "functions" : [ + +======= }, { "cases" : [ ], "emitStyle" : "const", - "name" : "PropertyNamespace", + "name" : "Nested", + "namespace" : [ + "PropertyNamespace" + ], "staticMethods" : [ ], @@ -274,14 +271,18 @@ { "isReadonly" : false, "isStatic" : true, - "name" : "namespaceProperty", + "name" : "nestedProperty", + "namespace" : [ + "PropertyNamespace", + "Nested" + ], "staticContext" : { "namespaceEnum" : { - "_0" : "PropertyNamespace" + } }, "type" : { - "string" : { + "int" : { } } @@ -289,21 +290,45 @@ { "isReadonly" : true, "isStatic" : true, - "name" : "namespaceConstant", + "name" : "nestedConstant", + "namespace" : [ + "PropertyNamespace", + "Nested" + ], "staticContext" : { "namespaceEnum" : { - "_0" : "PropertyNamespace" + } }, "type" : { "string" : { + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "nestedDouble", + "namespace" : [ + "PropertyNamespace", + "Nested" + ], + "staticContext" : { + "namespaceEnum" : { + + } + }, + "type" : { + "double" : { + } } } ], - "swiftCallName" : "PropertyNamespace" + "swiftCallName" : "PropertyNamespace.Nested" } +>>>>>>> 26a78490 (WIP: Enum handling and nesting simplifications (+1 squashed commit)) ], "functions" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.swift index ba45244e..c9420a34 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.swift @@ -94,6 +94,38 @@ public func _bjs_PropertyEnum_static_computedEnum_set(valueBytes: Int32, valueLe #endif } +@_expose(wasm, "bjs_PropertyNamespace_static_namespaceProperty_get") +@_cdecl("bjs_PropertyNamespace_static_namespaceProperty_get") +public func _bjs_PropertyNamespace_static_namespaceProperty_get() -> Void { + #if arch(wasm32) + let ret = PropertyNamespace.namespaceProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyNamespace_static_namespaceProperty_set") +@_cdecl("bjs_PropertyNamespace_static_namespaceProperty_set") +public func _bjs_PropertyNamespace_static_namespaceProperty_set(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + PropertyNamespace.namespaceProperty = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyNamespace_static_namespaceConstant_get") +@_cdecl("bjs_PropertyNamespace_static_namespaceConstant_get") +public func _bjs_PropertyNamespace_static_namespaceConstant_get() -> Void { + #if arch(wasm32) + let ret = PropertyNamespace.namespaceConstant + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_PropertyNamespace_Nested_static_nestedProperty_get") @_cdecl("bjs_PropertyNamespace_Nested_static_nestedProperty_get") public func _bjs_PropertyNamespace_Nested_static_nestedProperty_get() -> Int32 { @@ -147,6 +179,7 @@ public func _bjs_PropertyNamespace_Nested_static_nestedDouble_set(value: Float64 #endif } +<<<<<<< HEAD @_expose(wasm, "bjs_PropertyNamespace_static_namespaceProperty_get") @_cdecl("bjs_PropertyNamespace_static_namespaceProperty_get") public func _bjs_PropertyNamespace_static_namespaceProperty_get() -> Void { @@ -179,6 +212,8 @@ public func _bjs_PropertyNamespace_static_namespaceConstant_get() -> Void { #endif } +======= +>>>>>>> 26a78490 (WIP: Enum handling and nesting simplifications (+1 squashed commit)) @_expose(wasm, "bjs_PropertyClass_init") @_cdecl("bjs_PropertyClass_init") public func _bjs_PropertyClass_init() -> UnsafeMutableRawPointer { diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Functions.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Functions.md index 46ee6102..88d93b17 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Functions.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Functions.md @@ -186,4 +186,4 @@ declare global { | Enum `static func` | ✅ | | Namespace enum `static func` | ✅ | | Generic static functions | ❌ | -| Protocol static requirements | ❌ | \ No newline at end of file +| Protocol static requirements | ❌ | diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index ab1b7f82..a2cd3c1a 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -678,7 +678,7 @@ enum APIOptionalResult { @JS static func add(a: Int, b: Int) -> Int { return a + b } - @JS class func substract(a: Int, b: Int) -> Int { + @JS class func substract(a: Int, b: Int) -> Int { return a - b } } @@ -709,7 +709,7 @@ enum APIOptionalResult { @JS nonisolated(unsafe) static var staticBool: Bool = true @JS nonisolated(unsafe) static var staticFloat: Float = 3.14 @JS nonisolated(unsafe) static var staticDouble: Double = 2.718 - + @JS static var computedProperty: String { get { return "computed: \(staticVariable)" } set { @@ -718,14 +718,14 @@ enum APIOptionalResult { } } } - + @JS static var readOnlyComputed: Int { return staticVariable * 2 } - + @JS nonisolated(unsafe) static var optionalString: String? = nil @JS nonisolated(unsafe) static var optionalInt: Int? = nil - + @JS nonisolated(unsafe) static var jsObjectProperty: JSObject = JSObject() @JS init() {} @@ -735,11 +735,11 @@ enum APIOptionalResult { @JS enum StaticPropertyEnum { case option1 case option2 - + @JS nonisolated(unsafe) static var enumProperty: String = "enum value" @JS static let enumConstant: Int = 42 @JS nonisolated(unsafe) static var enumBool: Bool = false - + @JS nonisolated(unsafe) static var enumVariable: Int = 200 @JS static var computedReadonly: Int { @@ -763,7 +763,7 @@ enum APIOptionalResult { @JS enum StaticPropertyNamespace { @JS nonisolated(unsafe) static var namespaceProperty: String = "namespace" @JS static let namespaceConstant: String = "constant" - + @JS enum NestedProperties { @JS nonisolated(unsafe) static var nestedProperty: Int = 999 @JS static let nestedConstant: String = "nested" @@ -773,7 +773,8 @@ enum APIOptionalResult { // Test functions for static properties @JS func getAllStaticPropertyValues() -> String { - return "const:\(StaticPropertyHolder.staticConstant),var:\(StaticPropertyHolder.staticVariable),computed:\(StaticPropertyHolder.computedProperty),readonly:\(StaticPropertyHolder.readOnlyComputed)" + return + "const:\(StaticPropertyHolder.staticConstant),var:\(StaticPropertyHolder.staticVariable),computed:\(StaticPropertyHolder.computedProperty),readonly:\(StaticPropertyHolder.readOnlyComputed)" } class ExportAPITests: XCTestCase { diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift index 56bd9a13..3b5baabb 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift @@ -534,9 +534,9 @@ public func _bjs_StaticCalculator_static_roundtrip(value: Int32) -> Int32 { #endif } -@_expose(wasm, "bjs_StaticUtils_Nested_roundtrip") -@_cdecl("bjs_StaticUtils_Nested_roundtrip") -public func _bjs_StaticUtils_Nested_roundtrip(valueBytes: Int32, valueLength: Int32) -> Void { +@_expose(wasm, "bjs_StaticUtils_Nested_static_roundtrip") +@_cdecl("bjs_StaticUtils_Nested_static_roundtrip") +public func _bjs_StaticUtils_Nested_static_roundtrip(valueBytes: Int32, valueLength: Int32) -> Void { #if arch(wasm32) let ret = StaticUtils.Nested.roundtrip(_: String.bridgeJSLiftParameter(valueBytes, valueLength)) return ret.bridgeJSLowerReturn() @@ -686,6 +686,38 @@ public func _bjs_StaticPropertyEnum_static_computedReadWrite_set(valueBytes: Int #endif } +@_expose(wasm, "bjs_StaticPropertyNamespace_static_namespaceProperty_get") +@_cdecl("bjs_StaticPropertyNamespace_static_namespaceProperty_get") +public func _bjs_StaticPropertyNamespace_static_namespaceProperty_get() -> Void { + #if arch(wasm32) + let ret = StaticPropertyNamespace.namespaceProperty + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyNamespace_static_namespaceProperty_set") +@_cdecl("bjs_StaticPropertyNamespace_static_namespaceProperty_set") +public func _bjs_StaticPropertyNamespace_static_namespaceProperty_set(valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + StaticPropertyNamespace.namespaceProperty = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_StaticPropertyNamespace_static_namespaceConstant_get") +@_cdecl("bjs_StaticPropertyNamespace_static_namespaceConstant_get") +public func _bjs_StaticPropertyNamespace_static_namespaceConstant_get() -> Void { + #if arch(wasm32) + let ret = StaticPropertyNamespace.namespaceConstant + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_StaticPropertyNamespace_NestedProperties_static_nestedProperty_get") @_cdecl("bjs_StaticPropertyNamespace_NestedProperties_static_nestedProperty_get") public func _bjs_StaticPropertyNamespace_NestedProperties_static_nestedProperty_get() -> Int32 { @@ -739,38 +771,6 @@ public func _bjs_StaticPropertyNamespace_NestedProperties_static_nestedDouble_se #endif } -@_expose(wasm, "bjs_StaticPropertyNamespace_static_namespaceProperty_get") -@_cdecl("bjs_StaticPropertyNamespace_static_namespaceProperty_get") -public func _bjs_StaticPropertyNamespace_static_namespaceProperty_get() -> Void { - #if arch(wasm32) - let ret = StaticPropertyNamespace.namespaceProperty - return ret.bridgeJSLowerReturn() - #else - fatalError("Only available on WebAssembly") - #endif -} - -@_expose(wasm, "bjs_StaticPropertyNamespace_static_namespaceProperty_set") -@_cdecl("bjs_StaticPropertyNamespace_static_namespaceProperty_set") -public func _bjs_StaticPropertyNamespace_static_namespaceProperty_set(valueBytes: Int32, valueLength: Int32) -> Void { - #if arch(wasm32) - StaticPropertyNamespace.namespaceProperty = String.bridgeJSLiftParameter(valueBytes, valueLength) - #else - fatalError("Only available on WebAssembly") - #endif -} - -@_expose(wasm, "bjs_StaticPropertyNamespace_static_namespaceConstant_get") -@_cdecl("bjs_StaticPropertyNamespace_static_namespaceConstant_get") -public func _bjs_StaticPropertyNamespace_static_namespaceConstant_get() -> Void { - #if arch(wasm32) - let ret = StaticPropertyNamespace.namespaceConstant - return ret.bridgeJSLowerReturn() - #else - fatalError("Only available on WebAssembly") - #endif -} - @_expose(wasm, "bjs_roundTripVoid") @_cdecl("bjs_roundTripVoid") public func _bjs_roundTripVoid() -> Void { diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json index c4dc5d23..15c5a3a7 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -211,6 +211,9 @@ "isThrows" : false }, "name" : "toString", + "namespace" : [ + "Utils" + ], "parameters" : [ { "label" : "value", @@ -259,6 +262,10 @@ "isThrows" : false }, "name" : "call", + "namespace" : [ + "Networking", + "API" + ], "parameters" : [ { "label" : "_", @@ -308,6 +315,11 @@ "isThrows" : false }, "name" : "call", + "namespace" : [ + "Networking", + "APIV2", + "Internal" + ], "parameters" : [ { "label" : "_", @@ -1213,6 +1225,37 @@ ], "swiftCallName" : "Utils" }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Networking", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Networking" + }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "API", + "namespace" : [ + "Networking" + ], + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Networking.API" + }, { "cases" : [ { @@ -1259,28 +1302,14 @@ ], "emitStyle" : "const", - "name" : "API", - "staticMethods" : [ - - ], - "staticProperties" : [ - - ], - "swiftCallName" : "API" - }, - { - "cases" : [ - - ], - "emitStyle" : "const", - "name" : "Networking", + "name" : "Configuration", "staticMethods" : [ ], "staticProperties" : [ ], - "swiftCallName" : "Networking" + "swiftCallName" : "Configuration" }, { "cases" : [ @@ -1370,14 +1399,18 @@ ], "emitStyle" : "const", - "name" : "Configuration", + "name" : "Internal", + "namespace" : [ + "Networking", + "APIV2" + ], "staticMethods" : [ ], "staticProperties" : [ ], - "swiftCallName" : "Configuration" + "swiftCallName" : "Internal" }, { "cases" : [ @@ -1409,24 +1442,6 @@ ], "swiftCallName" : "Internal.SupportedMethod" }, - { - "cases" : [ - - ], - "emitStyle" : "const", - "name" : "Internal", - "namespace" : [ - "Networking", - "APIV2" - ], - "staticMethods" : [ - - ], - "staticProperties" : [ - - ], - "swiftCallName" : "Internal" - }, { "cases" : [ { @@ -1702,6 +1717,20 @@ ], "swiftCallName" : "ComplexResult" }, + { + "cases" : [ + + ], + "emitStyle" : "const", + "name" : "Utilities", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Utilities" + }, { "cases" : [ { @@ -1780,14 +1809,14 @@ ], "emitStyle" : "const", - "name" : "Utilities", + "name" : "API", "staticMethods" : [ ], "staticProperties" : [ ], - "swiftCallName" : "Utilities" + "swiftCallName" : "API" }, { "cases" : [ @@ -1836,20 +1865,6 @@ ], "swiftCallName" : "API.NetworkingResult" }, - { - "cases" : [ - - ], - "emitStyle" : "const", - "name" : "API", - "staticMethods" : [ - - ], - "staticProperties" : [ - - ], - "swiftCallName" : "API" - }, { "cases" : [ { @@ -2001,6 +2016,20 @@ { "cases" : [ + ], + "emitStyle" : "const", + "name" : "StaticUtils", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "StaticUtils" + }, + { + "cases" : [ + ], "emitStyle" : "const", "name" : "Nested", @@ -2009,13 +2038,17 @@ ], "staticMethods" : [ { - "abiName" : "bjs_StaticUtils_Nested_roundtrip", + "abiName" : "bjs_StaticUtils_Nested_static_roundtrip", "effects" : { "isAsync" : false, "isStatic" : true, "isThrows" : false }, "name" : "roundtrip", + "namespace" : [ + "StaticUtils", + "Nested" + ], "parameters" : [ { "label" : "_", @@ -2034,7 +2067,7 @@ }, "staticContext" : { "namespaceEnum" : { - "_0" : "StaticUtils.Nested" + } } } @@ -2044,20 +2077,6 @@ ], "swiftCallName" : "StaticUtils.Nested" }, - { - "cases" : [ - - ], - "emitStyle" : "const", - "name" : "StaticUtils", - "staticMethods" : [ - - ], - "staticProperties" : [ - - ], - "swiftCallName" : "StaticUtils" - }, { "cases" : [ { @@ -2177,10 +2196,7 @@ ], "emitStyle" : "const", - "name" : "NestedProperties", - "namespace" : [ - "StaticPropertyNamespace" - ], + "name" : "StaticPropertyNamespace", "staticMethods" : [ ], @@ -2188,26 +2204,14 @@ { "isReadonly" : false, "isStatic" : true, - "name" : "nestedProperty", + "name" : "namespaceProperty", + "namespace" : [ + "StaticPropertyNamespace" + ], "staticContext" : { "namespaceEnum" : { - "_0" : "StaticPropertyNamespace.NestedProperties" - } - }, - "type" : { - "int" : { } - } - }, - { - "isReadonly" : true, - "isStatic" : true, - "name" : "nestedConstant", - "staticContext" : { - "namespaceEnum" : { - "_0" : "StaticPropertyNamespace.NestedProperties" - } }, "type" : { "string" : { @@ -2216,29 +2220,35 @@ } }, { - "isReadonly" : false, + "isReadonly" : true, "isStatic" : true, - "name" : "nestedDouble", + "name" : "namespaceConstant", + "namespace" : [ + "StaticPropertyNamespace" + ], "staticContext" : { "namespaceEnum" : { - "_0" : "StaticPropertyNamespace.NestedProperties" + } }, "type" : { - "double" : { + "string" : { } } } ], - "swiftCallName" : "StaticPropertyNamespace.NestedProperties" + "swiftCallName" : "StaticPropertyNamespace" }, { "cases" : [ ], "emitStyle" : "const", - "name" : "StaticPropertyNamespace", + "name" : "NestedProperties", + "namespace" : [ + "StaticPropertyNamespace" + ], "staticMethods" : [ ], @@ -2246,14 +2256,18 @@ { "isReadonly" : false, "isStatic" : true, - "name" : "namespaceProperty", + "name" : "nestedProperty", + "namespace" : [ + "StaticPropertyNamespace", + "NestedProperties" + ], "staticContext" : { "namespaceEnum" : { - "_0" : "StaticPropertyNamespace" + } }, "type" : { - "string" : { + "int" : { } } @@ -2261,20 +2275,43 @@ { "isReadonly" : true, "isStatic" : true, - "name" : "namespaceConstant", + "name" : "nestedConstant", + "namespace" : [ + "StaticPropertyNamespace", + "NestedProperties" + ], "staticContext" : { "namespaceEnum" : { - "_0" : "StaticPropertyNamespace" + } }, "type" : { "string" : { + } + } + }, + { + "isReadonly" : false, + "isStatic" : true, + "name" : "nestedDouble", + "namespace" : [ + "StaticPropertyNamespace", + "NestedProperties" + ], + "staticContext" : { + "namespaceEnum" : { + + } + }, + "type" : { + "double" : { + } } } ], - "swiftCallName" : "StaticPropertyNamespace" + "swiftCallName" : "StaticPropertyNamespace.NestedProperties" } ], "functions" : [ From 722fa70376c98e09b957f7c7c5f1a0cfd828a837 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Wed, 17 Sep 2025 08:38:45 -0600 Subject: [PATCH 4/7] BridgeJS: Reduce repetition for properties declaration --- .../Sources/BridgeJSCore/ExportSwift.swift | 215 ++++++++---------- .../Sources/BridgeJSLink/BridgeJSLink.swift | 14 +- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 145 +++++------- .../BridgeJSLinkTests/Namespaces.Export.js | 4 +- .../StaticFunctions.Export.js | 5 - .../StaticProperties.Export.js | 9 - .../ExportSwiftTests/Namespaces.json | 2 +- .../ExportSwiftTests/Namespaces.swift | 6 +- .../ExportSwiftTests/StaticProperties.json | 7 - .../ExportSwiftTests/StaticProperties.swift | 35 --- 10 files changed, 172 insertions(+), 270 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 12908f9f..b874ea20 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -277,14 +277,11 @@ public class ExportSwift { switch state { case .topLevel: - abiName = "bjs_\(name)" staticContext = nil case .classBody(let className, _): if isStatic { - abiName = "bjs_\(className)_static_\(name)" staticContext = .className(className) } else { - abiName = "bjs_\(className)_\(name)" staticContext = nil } case .enumBody(let enumName, let enumKey): @@ -295,16 +292,21 @@ public class ExportSwift { let isNamespaceEnum = exportedEnumByName[enumKey]?.cases.isEmpty ?? true staticContext = isNamespaceEnum ? .namespaceEnum : .enumName(enumName) - - if isNamespaceEnum, let namespace = finalNamespace, !namespace.isEmpty { - // For namespace enums, use ONLY the resolved namespace to avoid duplication - // The finalNamespace already contains the correct namespace path - let abiNamespace = namespace.joined(separator: "_") - abiName = "bjs_\(abiNamespace)_static_\(name)" - } else { - abiName = "bjs_\(enumName)_static_\(name)" - } } + + let classNameForABI: String? + if case .classBody(let className, _) = state { + classNameForABI = className + } else { + classNameForABI = nil + } + abiName = ABINameGenerator.generateABIName( + baseName: name, + namespace: finalNamespace, + staticContext: isStatic ? staticContext : nil, + operation: nil, + className: classNameForABI + ) guard let effects = collectEffects(signature: node.signature, isStatic: isStatic) else { return nil @@ -316,7 +318,7 @@ public class ExportSwift { parameters: parameters, returnType: returnType, effects: effects, - namespace: finalNamespace, // UPDATED: Use resolved namespace + namespace: finalNamespace, staticContext: staticContext ) } @@ -991,50 +993,7 @@ public class ExportSwift { } for staticProperty in enumDef.staticProperties { - let getterBuilder = ExportedThunkBuilder( - effects: Effects(isAsync: false, isThrows: false, isStatic: true) - ) - - // Use context-aware call name based on static context and namespace - let staticCallName: String - if let staticContext = staticProperty.staticContext { - switch staticContext { - case .className(let className): - staticCallName = "\(className).\(staticProperty.name)" - case .enumName(let enumName): - staticCallName = "\(enumName).\(staticProperty.name)" - case .namespaceEnum: - // For namespace enums and explicit namespace, use the namespace property - if let namespace = staticProperty.namespace, !namespace.isEmpty { - let namespacePath = namespace.joined(separator: ".") - staticCallName = "\(namespacePath).\(staticProperty.name)" - } else { - // Fallback to using the enum's swift call name - staticCallName = "\(enumDef.swiftCallName).\(staticProperty.name)" - } - } - } else { - staticCallName = "\(enumDef.swiftCallName).\(staticProperty.name)" - } - - getterBuilder.callStaticProperty(name: staticCallName, returnType: staticProperty.type) - try getterBuilder.lowerReturnValue(returnType: staticProperty.type) - decls.append(getterBuilder.render(abiName: staticProperty.getterAbiName(className: enumDef.name))) - - if !staticProperty.isReadonly { - let setterBuilder = ExportedThunkBuilder( - effects: Effects(isAsync: false, isThrows: false, isStatic: true) - ) - try setterBuilder.liftParameter( - param: Parameter(label: "value", name: "value", type: staticProperty.type) - ) - setterBuilder.callStaticPropertySetter( - klassName: staticCallName.components(separatedBy: ".").dropLast().joined(separator: "."), - propertyName: staticProperty.name - ) - try setterBuilder.lowerReturnValue(returnType: .void) - decls.append(setterBuilder.render(abiName: staticProperty.setterAbiName(className: enumDef.name))) - } + decls.append(contentsOf: try renderSingleExportedProperty(property: staticProperty, context: .enumStatic(enumDef: enumDef))) } } @@ -1141,7 +1100,7 @@ public class ExportSwift { func callStaticProperty(name: String, returnType: BridgeType) { if returnType == .void { - append("let ret = \(raw: name)") + append("\(raw: name)") } else { append("let ret = \(raw: name)") } @@ -1450,6 +1409,83 @@ public class ExportSwift { return cases } } + + /// Context for property rendering that determines call behavior and ABI generation + private enum PropertyRenderingContext { + case enumStatic(enumDef: ExportedEnum) + case classStatic(klass: ExportedClass) + case classInstance(klass: ExportedClass) + } + + /// Renders getter and setter Swift thunk code for a property in any context + /// This unified function eliminates duplication between enum static, class static, and class instance property rendering + private func renderSingleExportedProperty(property: ExportedProperty, context: PropertyRenderingContext) throws -> [DeclSyntax] { + var decls: [DeclSyntax] = [] + + let (callName, className, isStatic): (String, String, Bool) + switch context { + case .enumStatic(let enumDef): + callName = property.callName(prefix: enumDef.swiftCallName) + className = enumDef.name + isStatic = true + case .classStatic(let klass): + callName = property.callName() + className = klass.name + isStatic = true + + case .classInstance(let klass): + callName = property.callName() + className = klass.name + isStatic = false + } + + let getterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false, isStatic: isStatic)) + + if !isStatic { + try getterBuilder.liftParameter( + param: Parameter(label: nil, name: "_self", type: .swiftHeapObject(className)) + ) + } + + if isStatic { + getterBuilder.callStaticProperty(name: callName, returnType: property.type) + } else { + getterBuilder.callPropertyGetter(klassName: className, propertyName: callName, returnType: property.type) + } + + try getterBuilder.lowerReturnValue(returnType: property.type) + decls.append(getterBuilder.render(abiName: property.getterAbiName(className: className))) + + // Generate property setter if not readonly + if !property.isReadonly { + let setterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false, isStatic: isStatic)) + + // Lift parameters based on property type + if !isStatic { + // Instance properties need _self parameter + try setterBuilder.liftParameter( + param: Parameter(label: nil, name: "_self", type: .swiftHeapObject(className)) + ) + } + + try setterBuilder.liftParameter( + param: Parameter(label: "value", name: "value", type: property.type) + ) + + if isStatic { + let klassName = callName.components(separatedBy: ".").dropLast().joined(separator: ".") + setterBuilder.callStaticPropertySetter(klassName: klassName, propertyName: property.name) + } else { + setterBuilder.callPropertySetter(klassName: className, propertyName: callName) + } + + try setterBuilder.lowerReturnValue(returnType: .void) + decls.append(setterBuilder.render(abiName: property.setterAbiName(className: className))) + } + + return decls + } + func renderSingleExportedFunction(function: ExportedFunction) throws -> DeclSyntax { let builder = ExportedThunkBuilder(effects: function.effects) @@ -1460,16 +1496,12 @@ public class ExportSwift { if function.effects.isStatic, let staticContext = function.staticContext { let callName: String switch staticContext { - case .className(let className): - callName = "\(className).\(function.name)" - case .enumName(let enumName): - callName = "\(enumName).\(function.name)" + case .className(let baseName), .enumName(let baseName): + callName = "\(baseName).\(function.name)" case .namespaceEnum: - // For namespace enums and explicit namespace, use the namespace property if let namespace = function.namespace, !namespace.isEmpty { callName = "\(namespace.joined(separator: ".")).\(function.name)" } else { - // Fallback to just the function name for functions without namespace callName = function.name } } @@ -1569,60 +1601,9 @@ public class ExportSwift { // Generate property getters and setters for property in klass.properties { if property.isStatic { - // Generate static property getter - let getterBuilder = ExportedThunkBuilder( - effects: Effects(isAsync: false, isThrows: false, isStatic: true) - ) - let staticCallName = "\(klass.swiftCallName).\(property.name)" - getterBuilder.callStaticProperty(name: staticCallName, returnType: property.type) - try getterBuilder.lowerReturnValue(returnType: property.type) - decls.append(getterBuilder.render(abiName: property.getterAbiName(className: klass.name))) - - // Generate static property setter if not readonly - if !property.isReadonly { - let setterBuilder = ExportedThunkBuilder( - effects: Effects(isAsync: false, isThrows: false, isStatic: true) - ) - try setterBuilder.liftParameter( - param: Parameter(label: "value", name: "value", type: property.type) - ) - setterBuilder.callStaticPropertySetter( - klassName: klass.swiftCallName, - propertyName: property.name - ) - try setterBuilder.lowerReturnValue(returnType: .void) - decls.append(setterBuilder.render(abiName: property.setterAbiName(className: klass.name))) - } + decls.append(contentsOf: try renderSingleExportedProperty(property: property, context: .classStatic(klass: klass))) } else { - // Generate instance property getter - let getterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) - try getterBuilder.liftParameter( - param: Parameter(label: nil, name: "_self", type: .swiftHeapObject(klass.name)) - ) - getterBuilder.callPropertyGetter( - klassName: klass.name, - propertyName: property.name, - returnType: property.type - ) - try getterBuilder.lowerReturnValue(returnType: property.type) - decls.append(getterBuilder.render(abiName: property.getterAbiName(className: klass.name))) - - // Generate instance property setter if not readonly - if !property.isReadonly { - let setterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) - try setterBuilder.liftParameter( - param: Parameter(label: nil, name: "_self", type: .swiftHeapObject(klass.name)) - ) - try setterBuilder.liftParameter( - param: Parameter(label: "value", name: "value", type: property.type) - ) - setterBuilder.callPropertySetter( - klassName: klass.name, - propertyName: property.name - ) - try setterBuilder.lowerReturnValue(returnType: .void) - decls.append(setterBuilder.render(abiName: property.setterAbiName(className: klass.name))) - } + decls.append(contentsOf: try renderSingleExportedProperty(property: property, context: .classInstance(klass: klass))) } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index f29dfeb3..a12e2e84 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -1128,11 +1128,9 @@ extension BridgeJSLink { case .enumName(let enumName): return try renderEnumStaticFunction(function: function, enumName: enumName) case .namespaceEnum: - // Use function's namespace property for namespace enum if let namespace = function.namespace, !namespace.isEmpty { return try renderNamespaceFunction(function: function, namespace: namespace.joined(separator: ".")) } else { - // Fallback to regular function rendering return try renderExportedFunction(function: function) } } @@ -1250,7 +1248,7 @@ extension BridgeJSLink { // Generate getter assignment let getterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) let getterReturnExpr = try getterThunkBuilder.call( - abiName: property.getterAbiName(className: enumName), + abiName: property.getterAbiName(), returnType: property.type ) @@ -1278,7 +1276,7 @@ extension BridgeJSLink { param: Parameter(label: "value", name: "value", type: property.type) ) _ = try setterThunkBuilder.call( - abiName: property.setterAbiName(className: enumName), + abiName: property.setterAbiName(), returnType: .void ) @@ -1321,7 +1319,7 @@ extension BridgeJSLink { // Use the last component of namespace as the className for ABI name generation let className = namespacePath.components(separatedBy: ".").last ?? namespacePath let getterReturnExpr = try getterThunkBuilder.call( - abiName: property.getterAbiName(className: className), + abiName: property.getterAbiName(), returnType: property.type ) @@ -1351,7 +1349,7 @@ extension BridgeJSLink { param: Parameter(label: "value", name: "value", type: property.type) ) _ = try setterThunkBuilder.call( - abiName: property.setterAbiName(className: className), + abiName: property.setterAbiName(), returnType: .void ) @@ -1488,7 +1486,7 @@ extension BridgeJSLink { // Generate static property getter let getterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) let getterReturnExpr = try getterThunkBuilder.call( - abiName: property.getterAbiName(className: klass.name), + abiName: property.getterAbiName(), returnType: property.type ) @@ -1510,7 +1508,7 @@ extension BridgeJSLink { param: Parameter(label: "value", name: "value", type: property.type) ) _ = try setterThunkBuilder.call( - abiName: property.setterAbiName(className: klass.name), + abiName: property.setterAbiName(), returnType: .void ) diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 6509037e..0436d2ce 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -2,40 +2,43 @@ // MARK: - ABI Name Generation -/// Utility for generating consistent ABI names across all exported types -struct ABINameGenerator { +/// Utility for generating consistent ABI names across all exported and imported types +public struct ABINameGenerator { + static let prefixComponent = "bjs" /// Generates ABI name using standardized namespace + context pattern - static func generateABIName( + public static func generateABIName( baseName: String, namespace: [String]?, staticContext: StaticContext?, - operation: String? = nil + operation: String? = nil, + className: String? = nil ) -> String { - - let namespacePart: String + + let namespacePart: String? if let namespace = namespace, !namespace.isEmpty { namespacePart = namespace.joined(separator: "_") } else { - namespacePart = "" + namespacePart = nil } - - let contextPart: String + + let contextPart: String? if let staticContext = staticContext { switch staticContext { case .className(let name), .enumName(let name): contextPart = name case .namespaceEnum: contextPart = namespacePart - } + } else if let className = className { + contextPart = className } else { contextPart = namespacePart } - - var components = ["bjs"] - if !contextPart.isEmpty { - components.append(contextPart) + + var components = [ABINameGenerator.prefixComponent] + if let context = contextPart { + components.append(context) } if staticContext != nil { @@ -47,9 +50,17 @@ struct ABINameGenerator { if let operation = operation { components.append(operation) } - + return components.joined(separator: "_") } + + static func generateImportedABIName( + baseName: String, + context: ImportedTypeSkeleton? = nil, + operation: String? = nil + ) -> String { + return [ABINameGenerator.prefixComponent, context?.name, baseName, operation].compactMap { $0 }.joined(separator: "_") + } } // MARK: - Types @@ -256,19 +267,6 @@ public struct ExportedFunction: Codable, Equatable, Sendable { self.namespace = namespace self.staticContext = staticContext } - - // MARK: - Unified ABI Name Generation (New Approach) - - /// Generates ABI name using the new unified approach - /// This method uses the standardized namespace property and ABINameGenerator - public func abiNameUnified() -> String { - return ABINameGenerator.generateABIName( - baseName: name, - namespace: namespace, - staticContext: effects.isStatic ? staticContext : nil, - operation: nil - ) - } } public struct ExportedClass: Codable { @@ -336,75 +334,42 @@ public struct ExportedProperty: Codable, Equatable, Sendable { self.namespace = namespace self.staticContext = staticContext } - - public func getterAbiName(className: String) -> String { - if isStatic, let staticContext = staticContext { + + public func callName(prefix: String? = nil) -> String { + if let staticContext = staticContext { switch staticContext { - case .className(let className): - return "bjs_\(className)_static_\(name)_get" - case .enumName(let enumName): - return "bjs_\(enumName)_static_\(name)_get" + case .className(let baseName), .enumName(let baseName): + return "\(baseName).\(name)" case .namespaceEnum: if let namespace = namespace, !namespace.isEmpty { - let abiNamespace = namespace.joined(separator: "_") - return "bjs_\(abiNamespace)_static_\(name)_get" - } else { - return "bjs_static_\(name)_get" + let namespacePath = namespace.joined(separator: ".") + return "\(namespacePath).\(name)" } } - } else if isStatic { - return "bjs_\(className)_static_\(name)_get" - } else { - return "bjs_\(className)_\(name)_get" } - } - - public func setterAbiName(className: String) -> String { - if isStatic, let staticContext = staticContext { - // Generate context-aware ABI names for static properties - switch staticContext { - case .className(let className): - return "bjs_\(className)_static_\(name)_set" - case .enumName(let enumName): - return "bjs_\(enumName)_static_\(name)_set" - case .namespaceEnum: - // For namespace enums, use ONLY the namespace property to avoid duplication - if let namespace = namespace, !namespace.isEmpty { - let abiNamespace = namespace.joined(separator: "_") - return "bjs_\(abiNamespace)_static_\(name)_set" - } else { - // Fallback if no namespace is available - return "bjs_static_\(name)_set" - } - } - } else if isStatic { - return "bjs_\(className)_static_\(name)_set" - } else { - return "bjs_\(className)_\(name)_set" + if let prefix = prefix { + return "\(prefix).\(name)" } + return name } - // MARK: - Unified ABI Name Generation (New Approach) - - /// Generates getter ABI name using the new unified approach - /// This method uses the standardized namespace property and ABINameGenerator - public func getterAbiNameUnified() -> String { + public func getterAbiName(className: String = "") -> String { return ABINameGenerator.generateABIName( baseName: name, namespace: namespace, staticContext: isStatic ? staticContext : nil, - operation: "get" + operation: "get", + className: isStatic ? nil : className ) } - /// Generates setter ABI name using the new unified approach - /// This method uses the standardized namespace property and ABINameGenerator - public func setterAbiNameUnified() -> String { + public func setterAbiName(className: String = "") -> String { return ABINameGenerator.generateABIName( baseName: name, namespace: namespace, staticContext: isStatic ? staticContext : nil, - operation: "set" + operation: "set", + className: isStatic ? nil : className ) } } @@ -430,9 +395,12 @@ public struct ImportedFunctionSkeleton: Codable { public let parameters: [Parameter] public let returnType: BridgeType public let documentation: String? - + public func abiName(context: ImportedTypeSkeleton?) -> String { - return context.map { "bjs_\($0.name)_\(name)" } ?? "bjs_\(name)" + return ABINameGenerator.generateImportedABIName( + baseName: name, + context: context + ) } } @@ -440,7 +408,10 @@ public struct ImportedConstructorSkeleton: Codable { public let parameters: [Parameter] public func abiName(context: ImportedTypeSkeleton) -> String { - return "bjs_\(context.name)_init" + return ABINameGenerator.generateImportedABIName( + baseName: "init", + context: context + ) } } @@ -451,11 +422,19 @@ public struct ImportedPropertySkeleton: Codable { public let documentation: String? public func getterAbiName(context: ImportedTypeSkeleton) -> String { - return "bjs_\(context.name)_\(name)_get" + return ABINameGenerator.generateImportedABIName( + baseName: name, + context: context, + operation: "get" + ) } - + public func setterAbiName(context: ImportedTypeSkeleton) -> String { - return "bjs_\(context.name)_\(name)_set" + return ABINameGenerator.generateImportedABIName( + baseName: name, + context: context, + operation: "set" + ) } } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js index ebc55d03..5044b4b1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js @@ -260,8 +260,8 @@ export async function createInstantiator(options, swift) { tmpRetString = undefined; return ret; }, - namespacedFunction: function bjs_namespacedFunction() { - instance.exports.bjs_namespacedFunction(); + namespacedFunction: function bjs_MyModule_Utils_namespacedFunction() { + instance.exports.bjs_MyModule_Utils_namespacedFunction(); const ret = tmpRetString; tmpRetString = undefined; return ret; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js index f630453d..3fd8b381 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js @@ -57,11 +57,6 @@ const __bjs_createAPIResultHelpers = () => { } }); }; -if (typeof globalThis.Utils === 'undefined') { - globalThis.Utils = {}; -} - - export async function createInstantiator(options, swift) { let instance; let memory; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js index 885deca2..aa8ec3bc 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js @@ -263,14 +263,6 @@ export async function createInstantiator(options, swift) { } } } -<<<<<<< HEAD -======= - if (typeof globalThis.PropertyNamespace === 'undefined') { - globalThis.PropertyNamespace = {}; - } - if (typeof globalThis.PropertyNamespace.Nested === 'undefined') { - globalThis.PropertyNamespace.Nested = {}; - } Object.defineProperty(globalThis.PropertyNamespace, 'namespaceProperty', { get: function() { instance.exports.bjs_PropertyNamespace_static_namespaceProperty_get(); const ret = tmpRetString; @@ -288,7 +280,6 @@ export async function createInstantiator(options, swift) { tmpRetString = undefined; return ret; } }); ->>>>>>> 26a78490 (WIP: Enum handling and nesting simplifications (+1 squashed commit)) Object.defineProperty(globalThis.PropertyNamespace.Nested, 'nestedProperty', { get: function() { const ret = instance.exports.bjs_PropertyNamespace_Nested_static_nestedProperty_get(); return ret; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.json index c9e00721..87684f13 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.json @@ -151,7 +151,7 @@ } }, { - "abiName" : "bjs_namespacedFunction", + "abiName" : "bjs_MyModule_Utils_namespacedFunction", "effects" : { "isAsync" : false, "isStatic" : false, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.swift index f0e7f657..f868fa11 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.swift @@ -17,9 +17,9 @@ public func _bjs_plainFunction() -> Void { #endif } -@_expose(wasm, "bjs_namespacedFunction") -@_cdecl("bjs_namespacedFunction") -public func _bjs_namespacedFunction() -> Void { +@_expose(wasm, "bjs_MyModule_Utils_namespacedFunction") +@_cdecl("bjs_MyModule_Utils_namespacedFunction") +public func _bjs_MyModule_Utils_namespacedFunction() -> Void { #if arch(wasm32) let ret = namespacedFunction() return ret.bridgeJSLowerReturn() diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.json index 15bebde7..4808e518 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.json @@ -248,12 +248,6 @@ } ], "swiftCallName" : "PropertyNamespace" -<<<<<<< HEAD - } - ], - "functions" : [ - -======= }, { "cases" : [ @@ -328,7 +322,6 @@ ], "swiftCallName" : "PropertyNamespace.Nested" } ->>>>>>> 26a78490 (WIP: Enum handling and nesting simplifications (+1 squashed commit)) ], "functions" : [ diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.swift index c9420a34..6edd0c8f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StaticProperties.swift @@ -179,41 +179,6 @@ public func _bjs_PropertyNamespace_Nested_static_nestedDouble_set(value: Float64 #endif } -<<<<<<< HEAD -@_expose(wasm, "bjs_PropertyNamespace_static_namespaceProperty_get") -@_cdecl("bjs_PropertyNamespace_static_namespaceProperty_get") -public func _bjs_PropertyNamespace_static_namespaceProperty_get() -> Void { - #if arch(wasm32) - let ret = PropertyNamespace.namespaceProperty - return ret.bridgeJSLowerReturn() - #else - fatalError("Only available on WebAssembly") - #endif -} - -@_expose(wasm, "bjs_PropertyNamespace_static_namespaceProperty_set") -@_cdecl("bjs_PropertyNamespace_static_namespaceProperty_set") -public func _bjs_PropertyNamespace_static_namespaceProperty_set(valueBytes: Int32, valueLength: Int32) -> Void { - #if arch(wasm32) - PropertyNamespace.namespaceProperty = String.bridgeJSLiftParameter(valueBytes, valueLength) - #else - fatalError("Only available on WebAssembly") - #endif -} - -@_expose(wasm, "bjs_PropertyNamespace_static_namespaceConstant_get") -@_cdecl("bjs_PropertyNamespace_static_namespaceConstant_get") -public func _bjs_PropertyNamespace_static_namespaceConstant_get() -> Void { - #if arch(wasm32) - let ret = PropertyNamespace.namespaceConstant - return ret.bridgeJSLowerReturn() - #else - fatalError("Only available on WebAssembly") - #endif -} - -======= ->>>>>>> 26a78490 (WIP: Enum handling and nesting simplifications (+1 squashed commit)) @_expose(wasm, "bjs_PropertyClass_init") @_cdecl("bjs_PropertyClass_init") public func _bjs_PropertyClass_init() -> UnsafeMutableRawPointer { From e2525102ddd925145b418e5657a726f66fdb4854 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Wed, 17 Sep 2025 10:30:35 -0600 Subject: [PATCH 5/7] BridgeJS: Improve namespace JS code generation logic after introduction of static properties and static functions --- .../JavaScript/BridgeJS.ExportSwift.json | 35 ++ .../JavaScript/BridgeJS.ExportSwift.json | 6 + .../Sources/BridgeJSCore/ExportSwift.swift | 61 ++-- .../Sources/BridgeJSLink/BridgeJSLink.swift | 317 ++++++------------ .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 18 +- .../EnumAssociatedValue.Export.js | 8 +- .../BridgeJSLinkTests/EnumNamespace.Export.js | 8 +- .../Exporting-Swift/Exporting-Swift-Class.md | 1 + 8 files changed, 206 insertions(+), 248 deletions(-) diff --git a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.ExportSwift.json b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.ExportSwift.json index cfd7c19e..cbd7d10e 100644 --- a/Benchmarks/Sources/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Benchmarks/Sources/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -5,6 +5,7 @@ "abiName" : "bjs_EnumRoundtrip_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -16,6 +17,7 @@ "abiName" : "bjs_EnumRoundtrip_take", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "take", @@ -40,6 +42,7 @@ "abiName" : "bjs_EnumRoundtrip_makeSuccess", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeSuccess", @@ -56,6 +59,7 @@ "abiName" : "bjs_EnumRoundtrip_makeFailure", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeFailure", @@ -72,6 +76,7 @@ "abiName" : "bjs_EnumRoundtrip_makeFlag", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeFlag", @@ -88,6 +93,7 @@ "abiName" : "bjs_EnumRoundtrip_makeRate", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeRate", @@ -104,6 +110,7 @@ "abiName" : "bjs_EnumRoundtrip_makePrecise", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makePrecise", @@ -120,6 +127,7 @@ "abiName" : "bjs_EnumRoundtrip_makeInfo", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeInfo", @@ -136,6 +144,7 @@ "abiName" : "bjs_EnumRoundtrip_roundtrip", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtrip", @@ -168,6 +177,7 @@ "abiName" : "bjs_ComplexResultRoundtrip_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -179,6 +189,7 @@ "abiName" : "bjs_ComplexResultRoundtrip_take", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "take", @@ -203,6 +214,7 @@ "abiName" : "bjs_ComplexResultRoundtrip_makeSuccess", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeSuccess", @@ -219,6 +231,7 @@ "abiName" : "bjs_ComplexResultRoundtrip_makeError", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeError", @@ -235,6 +248,7 @@ "abiName" : "bjs_ComplexResultRoundtrip_makeLocation", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeLocation", @@ -251,6 +265,7 @@ "abiName" : "bjs_ComplexResultRoundtrip_makeStatus", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeStatus", @@ -267,6 +282,7 @@ "abiName" : "bjs_ComplexResultRoundtrip_makeCoordinates", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeCoordinates", @@ -283,6 +299,7 @@ "abiName" : "bjs_ComplexResultRoundtrip_makeComprehensive", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeComprehensive", @@ -299,6 +316,7 @@ "abiName" : "bjs_ComplexResultRoundtrip_makeInfo", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "makeInfo", @@ -315,6 +333,7 @@ "abiName" : "bjs_ComplexResultRoundtrip_roundtrip", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "roundtrip", @@ -347,6 +366,7 @@ "abiName" : "bjs_StringRoundtrip_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -358,6 +378,7 @@ "abiName" : "bjs_StringRoundtrip_take", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "take", @@ -382,6 +403,7 @@ "abiName" : "bjs_StringRoundtrip_make", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "make", @@ -474,6 +496,12 @@ ], "emitStyle" : "const", "name" : "APIResult", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], "swiftCallName" : "APIResult" }, { @@ -664,6 +692,12 @@ ], "emitStyle" : "const", "name" : "ComplexResult", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], "swiftCallName" : "ComplexResult" } ], @@ -672,6 +706,7 @@ "abiName" : "bjs_run", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "run", diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json index 625556f2..5ef85519 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -5,6 +5,7 @@ "abiName" : "bjs_PlayBridgeJS_init", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "parameters" : [ @@ -16,6 +17,7 @@ "abiName" : "bjs_PlayBridgeJS_update", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : true }, "name" : "update", @@ -58,6 +60,7 @@ "abiName" : "bjs_PlayBridgeJSOutput_outputJs", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "outputJs", @@ -74,6 +77,7 @@ "abiName" : "bjs_PlayBridgeJSOutput_outputDts", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "outputDts", @@ -90,6 +94,7 @@ "abiName" : "bjs_PlayBridgeJSOutput_importSwiftGlue", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "importSwiftGlue", @@ -106,6 +111,7 @@ "abiName" : "bjs_PlayBridgeJSOutput_exportSwiftGlue", "effects" : { "isAsync" : false, + "isStatic" : false, "isThrows" : false }, "name" : "exportSwiftGlue", diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index b874ea20..6679c4a8 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -293,7 +293,7 @@ public class ExportSwift { let isNamespaceEnum = exportedEnumByName[enumKey]?.cases.isEmpty ?? true staticContext = isNamespaceEnum ? .namespaceEnum : .enumName(enumName) } - + let classNameForABI: String? if case .classBody(let className, _) = state { classNameForABI = className @@ -993,7 +993,12 @@ public class ExportSwift { } for staticProperty in enumDef.staticProperties { - decls.append(contentsOf: try renderSingleExportedProperty(property: staticProperty, context: .enumStatic(enumDef: enumDef))) + decls.append( + contentsOf: try renderSingleExportedProperty( + property: staticProperty, + context: .enumStatic(enumDef: enumDef) + ) + ) } } @@ -1409,19 +1414,22 @@ public class ExportSwift { return cases } } - + /// Context for property rendering that determines call behavior and ABI generation private enum PropertyRenderingContext { case enumStatic(enumDef: ExportedEnum) - case classStatic(klass: ExportedClass) + case classStatic(klass: ExportedClass) case classInstance(klass: ExportedClass) } /// Renders getter and setter Swift thunk code for a property in any context /// This unified function eliminates duplication between enum static, class static, and class instance property rendering - private func renderSingleExportedProperty(property: ExportedProperty, context: PropertyRenderingContext) throws -> [DeclSyntax] { + private func renderSingleExportedProperty( + property: ExportedProperty, + context: PropertyRenderingContext + ) throws -> [DeclSyntax] { var decls: [DeclSyntax] = [] - + let (callName, className, isStatic): (String, String, Bool) switch context { case .enumStatic(let enumDef): @@ -1432,34 +1440,36 @@ public class ExportSwift { callName = property.callName() className = klass.name isStatic = true - + case .classInstance(let klass): callName = property.callName() className = klass.name isStatic = false } - + let getterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false, isStatic: isStatic)) - + if !isStatic { try getterBuilder.liftParameter( param: Parameter(label: nil, name: "_self", type: .swiftHeapObject(className)) ) } - + if isStatic { getterBuilder.callStaticProperty(name: callName, returnType: property.type) } else { getterBuilder.callPropertyGetter(klassName: className, propertyName: callName, returnType: property.type) } - + try getterBuilder.lowerReturnValue(returnType: property.type) decls.append(getterBuilder.render(abiName: property.getterAbiName(className: className))) - + // Generate property setter if not readonly if !property.isReadonly { - let setterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false, isStatic: isStatic)) - + let setterBuilder = ExportedThunkBuilder( + effects: Effects(isAsync: false, isThrows: false, isStatic: isStatic) + ) + // Lift parameters based on property type if !isStatic { // Instance properties need _self parameter @@ -1467,26 +1477,25 @@ public class ExportSwift { param: Parameter(label: nil, name: "_self", type: .swiftHeapObject(className)) ) } - + try setterBuilder.liftParameter( param: Parameter(label: "value", name: "value", type: property.type) ) - + if isStatic { let klassName = callName.components(separatedBy: ".").dropLast().joined(separator: ".") setterBuilder.callStaticPropertySetter(klassName: klassName, propertyName: property.name) } else { setterBuilder.callPropertySetter(klassName: className, propertyName: callName) } - + try setterBuilder.lowerReturnValue(returnType: .void) decls.append(setterBuilder.render(abiName: property.setterAbiName(className: className))) } - + return decls } - func renderSingleExportedFunction(function: ExportedFunction) throws -> DeclSyntax { let builder = ExportedThunkBuilder(effects: function.effects) for param in function.parameters { @@ -1601,9 +1610,19 @@ public class ExportSwift { // Generate property getters and setters for property in klass.properties { if property.isStatic { - decls.append(contentsOf: try renderSingleExportedProperty(property: property, context: .classStatic(klass: klass))) + decls.append( + contentsOf: try renderSingleExportedProperty( + property: property, + context: .classStatic(klass: klass) + ) + ) } else { - decls.append(contentsOf: try renderSingleExportedProperty(property: property, context: .classInstance(klass: klass))) + decls.append( + contentsOf: try renderSingleExportedProperty( + property: property, + context: .classInstance(klass: klass) + ) + ) } } diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index a12e2e84..3544fd73 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -561,13 +561,19 @@ struct BridgeJSLink { printer.write(lines: data.topLevelEnumLines) // Namespace assignments section - let namespaceBuilder = NamespaceBuilder() - let topLevelNamespaceCode = namespaceBuilder.renderTopLevelEnumNamespaceAssignments( - namespacedEnums: data.namespacedEnums, - exportedSkeletons: exportedSkeletons + let topLevelNamespaceCode = generateNamespaceInitializationCode( + namespacePaths: Set(data.namespacedEnums.compactMap { $0.namespace }) ) printer.write(lines: topLevelNamespaceCode) + // Add enum assignments to global namespace + for enumDef in data.namespacedEnums { + if enumDef.enumType != .namespace { + let namespacePath = enumDef.namespace?.joined(separator: ".") ?? "" + printer.write("globalThis.\(namespacePath).\(enumDef.name) = \(enumDef.name);") + } + } + // Main function declaration printer.write("export async function createInstantiator(options, \(JSGlueVariableScope.reservedSwift)) {") @@ -631,85 +637,20 @@ struct BridgeJSLink { printer.write(lines: data.classLines) - // CRITICAL: Initialize ALL namespaces BEFORE any property assignments + // Initialize all namespaces before property assignments if hasAnyNamespacedItems { - var allUniqueNamespaces: [String] = [] - var seen = Set() - - let functionNamespacePaths: Set<[String]> = Set( - data.namespacedFunctions.compactMap { $0.namespace } - ) - let classNamespacePaths: Set<[String]> = Set( - data.namespacedClasses.compactMap { $0.namespace } - ) - let allRegularNamespacePaths = functionNamespacePaths.union(classNamespacePaths) - - let enumNamespacePaths: Set<[String]> = Set( - data.namespacedEnums.compactMap { $0.namespace } + let allNamespacePaths = collectAllNamespacePaths(data: data) + let namespaceInitializationCode = generateNamespaceInitializationCode( + namespacePaths: allNamespacePaths ) - - // From static properties with namespace property (collect their complete namespace paths) - var staticPropertyNamespacePaths: Set<[String]> = Set() - for skeleton in exportedSkeletons { - for enumDefinition in skeleton.enums { - for property in enumDefinition.staticProperties { - if let namespace = property.namespace, !namespace.isEmpty { - staticPropertyNamespacePaths.insert(namespace) - } - } - } - } - - let allNamespacePaths = allRegularNamespacePaths.union(enumNamespacePaths).union( - staticPropertyNamespacePaths - ) - - allNamespacePaths.forEach { namespacePath in - namespacePath.enumerated().forEach { (index, _) in - let path = namespacePath[0...index].joined(separator: ".") - if seen.insert(path).inserted { - allUniqueNamespaces.append(path) - } - } - } - - allUniqueNamespaces.sorted().forEach { namespace in - printer.write("if (typeof globalThis.\(namespace) === 'undefined') {") - printer.indent { - printer.write("globalThis.\(namespace) = {};") - } - printer.write("}") - } + printer.write(lines: namespaceInitializationCode) } - // NOW assign enum static function/property implementations (namespaces are ready) - printer.write(lines: data.enumStaticAssignments) - - if hasAnyNamespacedItems { - printer.write("const exports = {") - printer.indent { - printer.write(lines: data.exportsLines) - } - printer.write("};") - - data.namespacedClasses.forEach { klass in - let namespacePath: String = klass.namespace?.joined(separator: ".") ?? "" - printer.write("globalThis.\(namespacePath).\(klass.name) = exports.\(klass.name);") - } - - data.namespacedFunctions.forEach { function in - let namespacePath: String = function.namespace?.joined(separator: ".") ?? "" - printer.write("globalThis.\(namespacePath).\(function.name) = exports.\(function.name);") - } - - printer.write("return exports;") - } else { - printer.write("return {") - printer.indent { - printer.write(lines: data.exportsLines) - } - printer.write("};") - } + let propertyAssignments = generateNamespacePropertyAssignments( + data: data, + hasAnyNamespacedItems: hasAnyNamespacedItems + ) + printer.write(lines: propertyAssignments) } printer.write("},") } @@ -777,6 +718,94 @@ struct BridgeJSLink { return wrapperLines } + /// Collects all unique namespace paths from functions, classes, enums, and static properties + private func collectAllNamespacePaths(data: LinkData) -> Set<[String]> { + let functionNamespacePaths: Set<[String]> = Set( + data.namespacedFunctions.compactMap { $0.namespace } + ) + let classNamespacePaths: Set<[String]> = Set( + data.namespacedClasses.compactMap { $0.namespace } + ) + let allRegularNamespacePaths = functionNamespacePaths.union(classNamespacePaths) + + let enumNamespacePaths: Set<[String]> = Set( + data.namespacedEnums.compactMap { $0.namespace } + ) + var staticPropertyNamespacePaths: Set<[String]> = Set() + for skeleton in exportedSkeletons { + for enumDefinition in skeleton.enums { + for property in enumDefinition.staticProperties { + if let namespace = property.namespace, !namespace.isEmpty { + staticPropertyNamespacePaths.insert(namespace) + } + } + } + } + + return allRegularNamespacePaths.union(enumNamespacePaths).union(staticPropertyNamespacePaths) + } + + /// Generates JavaScript code lines for initializing namespace objects on globalThis + private func generateNamespaceInitializationCode(namespacePaths: Set<[String]>) -> [String] { + let printer = CodeFragmentPrinter() + var allUniqueNamespaces: [String] = [] + var seen = Set() + + namespacePaths.forEach { namespacePath in + namespacePath.enumerated().forEach { (index, _) in + let path = namespacePath[0...index].joined(separator: ".") + if seen.insert(path).inserted { + allUniqueNamespaces.append(path) + } + } + } + + allUniqueNamespaces.sorted().forEach { namespace in + printer.write("if (typeof globalThis.\(namespace) === 'undefined') {") + printer.indent { + printer.write("globalThis.\(namespace) = {};") + } + printer.write("}") + } + + return printer.lines + } + + /// Generates JavaScript code for assigning namespaced items to globalThis + private func generateNamespacePropertyAssignments(data: LinkData, hasAnyNamespacedItems: Bool) -> [String] { + let printer = CodeFragmentPrinter() + + printer.write(lines: data.enumStaticAssignments) + + if hasAnyNamespacedItems { + printer.write("const exports = {") + printer.indent() + printer.write(lines: data.exportsLines.map { "\($0)" }) + printer.unindent() + printer.write("};") + + data.namespacedClasses.forEach { klass in + let namespacePath: String = klass.namespace?.joined(separator: ".") ?? "" + printer.write("globalThis.\(namespacePath).\(klass.name) = exports.\(klass.name);") + } + + data.namespacedFunctions.forEach { function in + let namespacePath: String = function.namespace?.joined(separator: ".") ?? "" + printer.write("globalThis.\(namespacePath).\(function.name) = exports.\(function.name);") + } + + printer.write("return exports;") + } else { + printer.write("return {") + printer.indent() + printer.write(lines: data.exportsLines.map { "\($0)" }) + printer.unindent() + printer.write("};") + } + + return printer.lines + } + private func generateImportedTypeDefinitions() -> [String] { let printer = CodeFragmentPrinter() @@ -1316,8 +1345,6 @@ extension BridgeJSLink { // Generate getter assignment let getterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) - // Use the last component of namespace as the className for ABI name generation - let className = namespacePath.components(separatedBy: ".").last ?? namespacePath let getterReturnExpr = try getterThunkBuilder.call( abiName: property.getterAbiName(), returnType: property.type @@ -1750,134 +1777,6 @@ extension BridgeJSLink { } struct NamespaceBuilder { - - /// Generates JavaScript code for setting up global namespace structure - /// - /// This function creates the necessary JavaScript code to properly expose namespaced - /// functions, classes, and enums on the global object (globalThis). It ensures that - /// nested namespace paths are created correctly and that all exported items are - /// accessible through their full namespace paths. - /// - /// For example, if you have @JS("Utils.Math") func add() it will generate code that - /// makes globalThis.Utils.Math.add accessible. - /// - /// - Parameters: - /// - namespacedFunctions: Functions annotated with @JS("namespace.path") - /// - namespacedClasses: Classes annotated with @JS("namespace.path") - /// - Returns: Array of JavaScript code lines that set up the global namespace structure - func renderGlobalNamespace( - namespacedFunctions: [ExportedFunction], - namespacedClasses: [ExportedClass] - ) -> [String] { - let printer = CodeFragmentPrinter() - var uniqueNamespaces: [String] = [] - var seen = Set() - - let functionNamespacePaths: Set<[String]> = Set( - namespacedFunctions - .compactMap { $0.namespace } - ) - let classNamespacePaths: Set<[String]> = Set( - namespacedClasses - .compactMap { $0.namespace } - ) - - let allNamespacePaths = - functionNamespacePaths - .union(classNamespacePaths) - - allNamespacePaths.forEach { namespacePath in - namespacePath.makeIterator().enumerated().forEach { (index, _) in - let path = namespacePath[0...index].joined(separator: ".") - if seen.insert(path).inserted { - uniqueNamespaces.append(path) - } - } - } - - uniqueNamespaces.sorted().forEach { namespace in - printer.write("if (typeof globalThis.\(namespace) === 'undefined') {") - printer.indent { - printer.write("globalThis.\(namespace) = {};") - } - printer.write("}") - } - - namespacedClasses.forEach { klass in - let namespacePath: String = klass.namespace?.joined(separator: ".") ?? "" - printer.write("globalThis.\(namespacePath).\(klass.name) = exports.\(klass.name);") - } - - namespacedFunctions.forEach { function in - let namespacePath: String = function.namespace?.joined(separator: ".") ?? "" - printer.write("globalThis.\(namespacePath).\(function.name) = exports.\(function.name);") - } - - return printer.lines - } - - func renderTopLevelEnumNamespaceAssignments( - namespacedEnums: [ExportedEnum], - exportedSkeletons: [ExportedSkeleton] - ) -> [String] { - guard !namespacedEnums.isEmpty else { return [] } - - let printer = CodeFragmentPrinter() - var uniqueNamespaces: [String] = [] - var seen = Set() - - for enumDef in namespacedEnums { - guard let namespacePath = enumDef.namespace else { continue } - namespacePath.enumerated().forEach { (index, _) in - let path = namespacePath[0...index].joined(separator: ".") - if !seen.contains(path) { - seen.insert(path) - uniqueNamespaces.append(path) - } - } - } - - // Collect namespaces from static properties with namespace property - for skeleton in exportedSkeletons { - for enumDefinition in skeleton.enums { - for property in enumDefinition.staticProperties { - if let namespace = property.namespace, !namespace.isEmpty { - namespace.enumerated().forEach { (index, _) in - let path = namespace[0...index].joined(separator: ".") - if !seen.contains(path) { - seen.insert(path) - uniqueNamespaces.append(path) - } - } - } - } - } - } - - for namespace in uniqueNamespaces { - printer.write("if (typeof globalThis.\(namespace) === 'undefined') {") - printer.indent { - printer.write("globalThis.\(namespace) = {};") - } - printer.write("}") - } - - if !uniqueNamespaces.isEmpty { - printer.nextLine() - } - - for enumDef in namespacedEnums { - if enumDef.enumType == .namespace { - continue - } - - let namespacePath = enumDef.namespace?.joined(separator: ".") ?? "" - printer.write("globalThis.\(namespacePath).\(enumDef.name) = \(enumDef.name);") - } - printer.nextLine() - return printer.lines - } - private struct NamespaceContent { var functions: [ExportedFunction] = [] var classes: [ExportedClass] = [] diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 0436d2ce..e581fa40 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -14,14 +14,14 @@ public struct ABINameGenerator { operation: String? = nil, className: String? = nil ) -> String { - + let namespacePart: String? if let namespace = namespace, !namespace.isEmpty { namespacePart = namespace.joined(separator: "_") } else { namespacePart = nil } - + let contextPart: String? if let staticContext = staticContext { switch staticContext { @@ -35,7 +35,7 @@ public struct ABINameGenerator { } else { contextPart = namespacePart } - + var components = [ABINameGenerator.prefixComponent] if let context = contextPart { components.append(context) @@ -50,7 +50,7 @@ public struct ABINameGenerator { if let operation = operation { components.append(operation) } - + return components.joined(separator: "_") } @@ -59,7 +59,9 @@ public struct ABINameGenerator { context: ImportedTypeSkeleton? = nil, operation: String? = nil ) -> String { - return [ABINameGenerator.prefixComponent, context?.name, baseName, operation].compactMap { $0 }.joined(separator: "_") + return [ABINameGenerator.prefixComponent, context?.name, baseName, operation].compactMap { $0 }.joined( + separator: "_" + ) } } @@ -334,7 +336,7 @@ public struct ExportedProperty: Codable, Equatable, Sendable { self.namespace = namespace self.staticContext = staticContext } - + public func callName(prefix: String? = nil) -> String { if let staticContext = staticContext { switch staticContext { @@ -395,7 +397,7 @@ public struct ImportedFunctionSkeleton: Codable { public let parameters: [Parameter] public let returnType: BridgeType public let documentation: String? - + public func abiName(context: ImportedTypeSkeleton?) -> String { return ABINameGenerator.generateImportedABIName( baseName: name, @@ -428,7 +430,7 @@ public struct ImportedPropertySkeleton: Codable { operation: "get" ) } - + public func setterAbiName(context: ImportedTypeSkeleton) -> String { return ABINameGenerator.generateImportedABIName( baseName: name, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js index 6a690945..b0fb70ae 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js @@ -479,16 +479,14 @@ const __bjs_createAPIOptionalResultHelpers = () => { } }); }; -if (typeof globalThis.Utilities === 'undefined') { - globalThis.Utilities = {}; -} if (typeof globalThis.API === 'undefined') { globalThis.API = {}; } - +if (typeof globalThis.Utilities === 'undefined') { + globalThis.Utilities = {}; +} globalThis.Utilities.Result = Result; globalThis.API.NetworkingResult = NetworkingResult; - export async function createInstantiator(options, swift) { let instance; let memory; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js index 224fcf88..5b6bda0f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js @@ -29,27 +29,25 @@ export const SupportedMethod = { Post: 1, }; +if (typeof globalThis.Configuration === 'undefined') { + globalThis.Configuration = {}; +} if (typeof globalThis.Networking === 'undefined') { globalThis.Networking = {}; } if (typeof globalThis.Networking.API === 'undefined') { globalThis.Networking.API = {}; } -if (typeof globalThis.Configuration === 'undefined') { - globalThis.Configuration = {}; -} if (typeof globalThis.Networking.APIV2 === 'undefined') { globalThis.Networking.APIV2 = {}; } if (typeof globalThis.Networking.APIV2.Internal === 'undefined') { globalThis.Networking.APIV2.Internal = {}; } - globalThis.Networking.API.Method = Method; globalThis.Configuration.LogLevel = LogLevel; globalThis.Configuration.Port = Port; globalThis.Networking.APIV2.Internal.SupportedMethod = SupportedMethod; - export async function createInstantiator(options, swift) { let instance; let memory; diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md index a9a23a59..3a4df4ae 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md @@ -88,6 +88,7 @@ export type Exports = { | Stored properties: `var`, `let` (with `willSet`, `didSet`) | ✅ | | Computed properties: `var x: X { get set }` | ✅ | | Computed properties with effects: `var x: X { get async throws }` | 🚧 | +| Static / class properties: `static var`, `class let` | ✅ (See )| | Methods: `func` | ✅ (See ) | | Static/class methods: `static func`, `class func` | ✅ (See ) | | Subscripts: `subscript()` | ❌ | From f8dcf587220a559032c7bb66ba0b8b40d0855034 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Mon, 29 Sep 2025 13:40:24 +0200 Subject: [PATCH 6/7] BridgeJS: Static functions & properties improvements for access via Exports --- .../Sources/BridgeJSLink/BridgeJSLink.swift | 141 +++++++++++++----- .../Sources/BridgeJSLink/JSGlueGen.swift | 24 --- .../BridgeJSLinkTests/EnumCase.Export.d.ts | 6 +- .../BridgeJSLinkTests/EnumRawType.Export.d.ts | 20 +-- .../StaticFunctions.Export.d.ts | 10 +- .../StaticFunctions.Export.js | 28 ++-- .../StaticProperties.Export.d.ts | 10 +- .../StaticProperties.Export.js | 59 ++++---- Tests/prelude.mjs | 26 ++-- 9 files changed, 185 insertions(+), 139 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 3544fd73..0f649d7f 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -146,12 +146,6 @@ struct BridgeJSLink { // Process functions for function in skeleton.functions { - if function.effects.isStatic, - case .enumName(_) = function.staticContext - { - continue - } - var (js, dts) = try renderExportedFunction(function: function) if function.effects.isStatic, @@ -195,17 +189,110 @@ struct BridgeJSLink { } for enumDefinition in skeleton.enums where enumDefinition.enumType != .namespace { + // Process enum static methods to add to Exports type + let enumExportPrinter = CodeFragmentPrinter() + let enumDtsPrinter = CodeFragmentPrinter() + for function in enumDefinition.staticMethods { - let assignmentJs = try renderEnumStaticFunctionAssignment( - function: function, - enumName: enumDefinition.name + let thunkBuilder = ExportedThunkBuilder(effects: function.effects) + for param in function.parameters { + try thunkBuilder.lowerParameter(param: param) + } + let returnExpr = try thunkBuilder.call(abiName: function.abiName, returnType: function.returnType) + + let methodPrinter = CodeFragmentPrinter() + methodPrinter.write("\(function.name): function(\(function.parameters.map { $0.name }.joined(separator: ", "))) {") + methodPrinter.indent { + methodPrinter.write(contentsOf: thunkBuilder.body) + methodPrinter.write(contentsOf: thunkBuilder.cleanupCode) + methodPrinter.write(lines: thunkBuilder.checkExceptionLines()) + if let returnExpr = returnExpr { + methodPrinter.write("return \(returnExpr);") + } + } + methodPrinter.write("},") + + enumExportPrinter.write(lines: methodPrinter.lines) + enumDtsPrinter.write("\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));") + } + + let enumExportLines = enumExportPrinter.lines + let enumDtsLines = enumDtsPrinter.lines + + let enumPropertyPrinter = CodeFragmentPrinter() + let enumPropertyDtsPrinter = CodeFragmentPrinter() + + for property in enumDefinition.staticProperties { + let readonly = property.isReadonly ? "readonly " : "" + enumPropertyDtsPrinter.write("\(readonly)\(property.name): \(property.type.tsType);") + + let getterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) + let getterReturnExpr = try getterThunkBuilder.call( + abiName: property.getterAbiName(), + returnType: property.type ) - data.enumStaticAssignments.append(contentsOf: assignmentJs) + + enumPropertyPrinter.write("get \(property.name)() {") + enumPropertyPrinter.indent { + enumPropertyPrinter.write(contentsOf: getterThunkBuilder.body) + enumPropertyPrinter.write(contentsOf: getterThunkBuilder.cleanupCode) + enumPropertyPrinter.write(lines: getterThunkBuilder.checkExceptionLines()) + if let returnExpr = getterReturnExpr { + enumPropertyPrinter.write("return \(returnExpr);") + } + } + enumPropertyPrinter.write("},") + + if !property.isReadonly { + let setterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) + try setterThunkBuilder.lowerParameter( + param: Parameter(label: "value", name: "value", type: property.type) + ) + _ = try setterThunkBuilder.call( + abiName: property.setterAbiName(), + returnType: .void + ) + + enumPropertyPrinter.write("set \(property.name)(value) {") + enumPropertyPrinter.indent { + enumPropertyPrinter.write(contentsOf: setterThunkBuilder.body) + enumPropertyPrinter.write(contentsOf: setterThunkBuilder.cleanupCode) + enumPropertyPrinter.write(lines: setterThunkBuilder.checkExceptionLines()) + } + enumPropertyPrinter.write("},") + } } - for property in enumDefinition.staticProperties { - let (propJs, _) = try renderEnumStaticProperty(property: property, enumName: enumDefinition.name) - data.enumStaticAssignments.append(contentsOf: propJs) + + let enumPropertyLines = enumPropertyPrinter.lines + let enumPropertyDtsLines = enumPropertyDtsPrinter.lines + + if !enumExportLines.isEmpty || !enumPropertyLines.isEmpty { + let exportsPrinter = CodeFragmentPrinter() + let dtsExportsPrinter = CodeFragmentPrinter() + + exportsPrinter.write("\(enumDefinition.name): {") + exportsPrinter.indent { + // Combine all lines and handle trailing comma removal + var allLines = enumExportLines + enumPropertyLines + if let lastLineIndex = allLines.indices.last, allLines[lastLineIndex].hasSuffix(",") { + allLines[lastLineIndex] = String(allLines[lastLineIndex].dropLast()) + } + exportsPrinter.write(lines: allLines) + } + exportsPrinter.write("},") + + dtsExportsPrinter.write("\(enumDefinition.name): {") + dtsExportsPrinter.indent { + dtsExportsPrinter.write(lines: enumDtsLines) + dtsExportsPrinter.write(lines: enumPropertyDtsLines) + } + dtsExportsPrinter.write("}") + + data.exportsLines.append(contentsOf: exportsPrinter.lines) + data.dtsExportLines.append(contentsOf: dtsExportsPrinter.lines) } + + // Static properties are now handled in Exports type - no global assignments needed } } @@ -994,8 +1081,6 @@ struct BridgeJSLink { private func generateDeclarations(enumDefinition: ExportedEnum) -> [String] { let printer = CodeFragmentPrinter() - let enumStaticFunctions = enumDefinition.staticMethods - switch enumDefinition.emitStyle { case .tsEnum: switch enumDefinition.enumType { @@ -1035,22 +1120,10 @@ struct BridgeJSLink { printer.write("readonly \(caseName): \(value);") } - for function in enumStaticFunctions { - let signature = renderTSSignature( - parameters: function.parameters, - returnType: function.returnType, - effects: function.effects - ) - printer.write("\(function.name)\(signature);") - } - for property in enumDefinition.staticProperties { - let readonly = property.isReadonly ? "readonly " : "" - printer.write("\(readonly)\(property.name): \(property.type.tsType);") - } } printer.write("};") printer.write( - "export type \(enumDefinition.name) = typeof \(enumDefinition.name)[keyof typeof \(enumDefinition.name)];" + "export type \(enumDefinition.name)Tag = typeof \(enumDefinition.name)[keyof typeof \(enumDefinition.name)];" ) printer.nextLine() case .associatedValue: @@ -1064,18 +1137,6 @@ struct BridgeJSLink { } printer.write("};") - for function in enumStaticFunctions { - let signature = renderTSSignature( - parameters: function.parameters, - returnType: function.returnType, - effects: function.effects - ) - printer.write("\(function.name)\(signature);") - } - for property in enumDefinition.staticProperties { - let readonly = property.isReadonly ? "readonly " : "" - printer.write("\(readonly)\(property.name): \(property.type.tsType);") - } } printer.write("};") printer.nextLine() diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index e54e9609..8b0f3f47 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -537,14 +537,6 @@ struct IntrinsicJSFragment: Sendable { } } printer.write("},") - - for function in enumDefinition.staticMethods { - printer.write("\(function.name): null,") - } - - for property in enumDefinition.staticProperties { - printer.write("\(property.name): null,") - } } printer.write("};") printer.nextLine() @@ -633,14 +625,6 @@ struct IntrinsicJSFragment: Sendable { let caseName = enumCase.name.capitalizedFirstLetter printer.write("\(caseName): \(index),") } - - for function in enumDefinition.staticMethods { - printer.write("\(function.name): null,") - } - - for property in enumDefinition.staticProperties { - printer.write("\(property.name): null,") - } } printer.write("};") printer.nextLine() @@ -667,14 +651,6 @@ struct IntrinsicJSFragment: Sendable { printer.write("\(caseName): \(formattedValue),") } - - for function in enumDefinition.staticMethods { - printer.write("\(function.name): null,") - } - - for property in enumDefinition.staticProperties { - printer.write("\(property.name): null,") - } } printer.write("};") printer.nextLine() diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts index 9a04f59a..87c12b29 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts @@ -10,14 +10,14 @@ export const Direction: { readonly East: 2; readonly West: 3; }; -export type Direction = typeof Direction[keyof typeof Direction]; +export type DirectionTag = typeof Direction[keyof typeof Direction]; export const Status: { readonly Loading: 0; readonly Success: 1; readonly Error: 2; }; -export type Status = typeof Status[keyof typeof Status]; +export type StatusTag = typeof Status[keyof typeof Status]; export enum TSDirection { North = 0, @@ -29,7 +29,7 @@ export enum TSDirection { export const PublicStatus: { readonly Success: 0; }; -export type PublicStatus = typeof PublicStatus[keyof typeof PublicStatus]; +export type PublicStatusTag = typeof PublicStatus[keyof typeof PublicStatus]; export type Exports = { setDirection(direction: Direction): void; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.d.ts index 96297187..1d2b3ecf 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.d.ts @@ -9,7 +9,7 @@ export const Theme: { readonly Dark: "dark"; readonly Auto: "auto"; }; -export type Theme = typeof Theme[keyof typeof Theme]; +export type ThemeTag = typeof Theme[keyof typeof Theme]; export enum TSTheme { Light = "light", @@ -21,14 +21,14 @@ export const FeatureFlag: { readonly Enabled: true; readonly Disabled: false; }; -export type FeatureFlag = typeof FeatureFlag[keyof typeof FeatureFlag]; +export type FeatureFlagTag = typeof FeatureFlag[keyof typeof FeatureFlag]; export const HttpStatus: { readonly Ok: 200; readonly NotFound: 404; readonly ServerError: 500; }; -export type HttpStatus = typeof HttpStatus[keyof typeof HttpStatus]; +export type HttpStatusTag = typeof HttpStatus[keyof typeof HttpStatus]; export enum TSHttpStatus { Ok = 200, @@ -43,7 +43,7 @@ export const Priority: { readonly High: 4; readonly Highest: 5; }; -export type Priority = typeof Priority[keyof typeof Priority]; +export type PriorityTag = typeof Priority[keyof typeof Priority]; export const FileSize: { readonly Tiny: 1024; @@ -51,35 +51,35 @@ export const FileSize: { readonly Medium: 102400; readonly Large: 1048576; }; -export type FileSize = typeof FileSize[keyof typeof FileSize]; +export type FileSizeTag = typeof FileSize[keyof typeof FileSize]; export const UserId: { readonly Guest: 0; readonly User: 1000; readonly Admin: 9999; }; -export type UserId = typeof UserId[keyof typeof UserId]; +export type UserIdTag = typeof UserId[keyof typeof UserId]; export const TokenId: { readonly Invalid: 0; readonly Session: 12345; readonly Refresh: 67890; }; -export type TokenId = typeof TokenId[keyof typeof TokenId]; +export type TokenIdTag = typeof TokenId[keyof typeof TokenId]; export const SessionId: { readonly None: 0; readonly Active: 9876543210; readonly Expired: 1234567890; }; -export type SessionId = typeof SessionId[keyof typeof SessionId]; +export type SessionIdTag = typeof SessionId[keyof typeof SessionId]; export const Precision: { readonly Rough: 0.1; readonly Normal: 0.01; readonly Fine: 0.001; }; -export type Precision = typeof Precision[keyof typeof Precision]; +export type PrecisionTag = typeof Precision[keyof typeof Precision]; export const Ratio: { readonly Quarter: 0.25; @@ -87,7 +87,7 @@ export const Ratio: { readonly Golden: 1.618; readonly Pi: 3.14159; }; -export type Ratio = typeof Ratio[keyof typeof Ratio]; +export type RatioTag = typeof Ratio[keyof typeof Ratio]; export type Exports = { setTheme(theme: Theme): void; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.d.ts index 715d9d8c..b8d8b719 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.d.ts @@ -7,16 +7,14 @@ export const Calculator: { readonly Scientific: 0; readonly Basic: 1; - square(value: number): number; }; -export type Calculator = typeof Calculator[keyof typeof Calculator]; +export type CalculatorTag = typeof Calculator[keyof typeof Calculator]; export const APIResult: { readonly Tag: { readonly Success: 0; readonly Failure: 1; }; - roundtrip(value: APIResult): APIResult; }; export type APIResult = @@ -48,6 +46,12 @@ export type Exports = { subtract(a: number, b: number): number; add(a: number, b: number): number; } + Calculator: { + square(value: number): number; + } + APIResult: { + roundtrip(value: APIResult): APIResult; + } } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js index 3fd8b381..0a2ff179 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js @@ -7,7 +7,6 @@ export const Calculator = { Scientific: 0, Basic: 1, - square: null, }; export const APIResult = { @@ -15,7 +14,6 @@ export const APIResult = { Success: 0, Failure: 1, }, - roundtrip: null, }; const __bjs_createAPIResultHelpers = () => { @@ -262,17 +260,6 @@ export async function createInstantiator(options, swift) { if (typeof globalThis.Utils.String === 'undefined') { globalThis.Utils.String = {}; } - Calculator.square = function(value) { - const ret = instance.exports.bjs_Calculator_static_square(value); - return ret; - }; - APIResult.roundtrip = function(value) { - const { caseId: valueCaseId, cleanup: valueCleanup } = enumHelpers.APIResult.lower(value); - instance.exports.bjs_APIResult_static_roundtrip(valueCaseId); - const ret = enumHelpers.APIResult.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s); - if (valueCleanup) { valueCleanup(); } - return ret; - }; const exports = { MathUtils, uppercase: function bjs_Utils_String_static_uppercase(text) { @@ -284,6 +271,21 @@ export async function createInstantiator(options, swift) { swift.memory.release(textId); return ret; }, + Calculator: { + square: function(value) { + const ret = instance.exports.bjs_Calculator_static_square(value); + return ret; + } + }, + APIResult: { + roundtrip: function(value) { + const { caseId: valueCaseId, cleanup: valueCleanup } = enumHelpers.APIResult.lower(value); + instance.exports.bjs_APIResult_static_roundtrip(valueCaseId); + const ret = enumHelpers.APIResult.raise(tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s); + if (valueCleanup) { valueCleanup(); } + return ret; + } + }, }; globalThis.Utils.String.uppercase = exports.uppercase; return exports; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.d.ts index 59c30ca9..5c9ae065 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.d.ts @@ -7,11 +7,8 @@ export const PropertyEnum: { readonly Value1: 0; readonly Value2: 1; - enumProperty: string; - readonly enumConstant: number; - computedEnum: string; }; -export type PropertyEnum = typeof PropertyEnum[keyof typeof PropertyEnum]; +export type PropertyEnumTag = typeof PropertyEnum[keyof typeof PropertyEnum]; export {}; @@ -47,6 +44,11 @@ export type Exports = { readonly readOnlyComputed: number; optionalProperty: string | null; } + PropertyEnum: { + enumProperty: string; + readonly enumConstant: number; + computedEnum: string; + } } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js index aa8ec3bc..86cce7e6 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js @@ -7,9 +7,6 @@ export const PropertyEnum = { Value1: 0, Value2: 1, - enumProperty: null, - enumConstant: null, - computedEnum: null, }; export async function createInstantiator(options, swift) { @@ -298,34 +295,38 @@ export async function createInstantiator(options, swift) { }, set: function(value) { instance.exports.bjs_PropertyNamespace_Nested_static_nestedDouble_set(value); } }); - Object.defineProperty(PropertyEnum, 'enumProperty', { get: function() { - instance.exports.bjs_PropertyEnum_static_enumProperty_get(); - const ret = tmpRetString; - tmpRetString = undefined; - return ret; - }, set: function(value) { - const valueBytes = textEncoder.encode(value); - const valueId = swift.memory.retain(valueBytes); - instance.exports.bjs_PropertyEnum_static_enumProperty_set(valueId, valueBytes.length); - swift.memory.release(valueId); - } }); - Object.defineProperty(PropertyEnum, 'enumConstant', { get: function() { - const ret = instance.exports.bjs_PropertyEnum_static_enumConstant_get(); - return ret; - } }); - Object.defineProperty(PropertyEnum, 'computedEnum', { get: function() { - instance.exports.bjs_PropertyEnum_static_computedEnum_get(); - const ret = tmpRetString; - tmpRetString = undefined; - return ret; - }, set: function(value) { - const valueBytes = textEncoder.encode(value); - const valueId = swift.memory.retain(valueBytes); - instance.exports.bjs_PropertyEnum_static_computedEnum_set(valueId, valueBytes.length); - swift.memory.release(valueId); - } }); return { PropertyClass, + PropertyEnum: { + get enumProperty() { + instance.exports.bjs_PropertyEnum_static_enumProperty_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + }, + set enumProperty(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_PropertyEnum_static_enumProperty_set(valueId, valueBytes.length); + swift.memory.release(valueId); + }, + get enumConstant() { + const ret = instance.exports.bjs_PropertyEnum_static_enumConstant_get(); + return ret; + }, + get computedEnum() { + instance.exports.bjs_PropertyEnum_static_computedEnum_get(); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + }, + set computedEnum(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_PropertyEnum_static_computedEnum_set(valueId, valueBytes.length); + swift.memory.release(valueId); + } + }, }; }, } diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 0c74ec4f..c9ce04c0 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -325,20 +325,20 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.equal(exports.StaticPropertyHolder.readOnlyComputed, 600); // staticVariable * 2 = 300 * 2 // Test static properties in enums - assert.equal(StaticPropertyEnum.enumProperty, "enum value"); - assert.equal(StaticPropertyEnum.enumConstant, 42); - assert.equal(StaticPropertyEnum.enumBool, false); + assert.equal(exports.StaticPropertyEnum.enumProperty, "enum value"); + assert.equal(exports.StaticPropertyEnum.enumConstant, 42); + assert.equal(exports.StaticPropertyEnum.enumBool, false); - StaticPropertyEnum.enumProperty = "modified enum"; - StaticPropertyEnum.enumBool = true; - assert.equal(StaticPropertyEnum.enumProperty, "modified enum"); - assert.equal(StaticPropertyEnum.enumBool, true); + exports.StaticPropertyEnum.enumProperty = "modified enum"; + exports.StaticPropertyEnum.enumBool = true; + assert.equal(exports.StaticPropertyEnum.enumProperty, "modified enum"); + assert.equal(exports.StaticPropertyEnum.enumBool, true); - assert.equal(StaticPropertyEnum.enumVariable, 200); - assert.equal(StaticPropertyEnum.computedReadonly, 400); - assert.equal(StaticPropertyEnum.computedReadWrite, "Value: 200"); - StaticPropertyEnum.computedReadWrite = "Value: 500"; - assert.equal(StaticPropertyEnum.enumVariable, 500); + assert.equal(exports.StaticPropertyEnum.enumVariable, 200); + assert.equal(exports.StaticPropertyEnum.computedReadonly, 400); + assert.equal(exports.StaticPropertyEnum.computedReadWrite, "Value: 200"); + exports.StaticPropertyEnum.computedReadWrite = "Value: 500"; + assert.equal(exports.StaticPropertyEnum.enumVariable, 500); // Namespace enum static properties assert.equal(globalThis.StaticPropertyNamespace.namespaceProperty, "namespace"); @@ -635,7 +635,7 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.equal(exports.roundTripOptionalAPIOptionalResult(null), null); assert.equal(exports.MathUtils.add(2147483647, 0), 2147483647); - assert.equal(StaticCalculator.roundtrip(42), 42); + assert.equal(exports.StaticCalculator.roundtrip(42), 42); assert.equal(StaticCalculator.Scientific, 0); assert.equal(StaticCalculator.Basic, 1); From f286bd87782325a3c9409b1bd82b3a06bf2c1562 Mon Sep 17 00:00:00 2001 From: Krzysztof Rodak Date: Tue, 30 Sep 2025 11:54:50 +0200 Subject: [PATCH 7/7] BridgeJS: Introduce new enum elements naming and Object type and add it to Exports --- .../Sources/BridgeJSLink/BridgeJSLink.swift | 147 +++++++----- .../EnumAssociatedValue.Export.d.ts | 67 ++++-- .../EnumAssociatedValue.Export.js | 215 ++++++++--------- .../BridgeJSLinkTests/EnumCase.Export.d.ts | 35 ++- .../BridgeJSLinkTests/EnumCase.Export.js | 9 +- .../EnumNamespace.Export.d.ts | 36 ++- .../BridgeJSLinkTests/EnumNamespace.Export.js | 20 +- .../BridgeJSLinkTests/EnumRawType.Export.d.ts | 148 +++++++----- .../BridgeJSLinkTests/EnumRawType.Export.js | 30 ++- .../StaticFunctions.Export.d.ts | 26 +- .../StaticFunctions.Export.js | 30 +-- .../StaticProperties.Export.d.ts | 16 +- .../StaticProperties.Export.js | 3 +- .../Exporting-Swift/Exporting-Swift-Enum.md | 158 +++++++------ .../Exporting-Swift-Static-Functions.md | 125 ++++------ .../Exporting-Swift-Static-Properties.md | 169 +++++++------ Tests/prelude.mjs | 222 +++++++++--------- 17 files changed, 782 insertions(+), 674 deletions(-) diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 0f649d7f..42bfef50 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -188,20 +188,22 @@ struct BridgeJSLink { } } - for enumDefinition in skeleton.enums where enumDefinition.enumType != .namespace { - // Process enum static methods to add to Exports type + for enumDefinition in skeleton.enums + where enumDefinition.enumType != .namespace && enumDefinition.emitStyle != .tsEnum { let enumExportPrinter = CodeFragmentPrinter() - let enumDtsPrinter = CodeFragmentPrinter() - + let enumValuesName = "\(enumDefinition.name)Values" + for function in enumDefinition.staticMethods { let thunkBuilder = ExportedThunkBuilder(effects: function.effects) for param in function.parameters { try thunkBuilder.lowerParameter(param: param) } let returnExpr = try thunkBuilder.call(abiName: function.abiName, returnType: function.returnType) - + let methodPrinter = CodeFragmentPrinter() - methodPrinter.write("\(function.name): function(\(function.parameters.map { $0.name }.joined(separator: ", "))) {") + methodPrinter.write( + "\(function.name): function(\(function.parameters.map { $0.name }.joined(separator: ", "))) {" + ) methodPrinter.indent { methodPrinter.write(contentsOf: thunkBuilder.body) methodPrinter.write(contentsOf: thunkBuilder.cleanupCode) @@ -211,27 +213,21 @@ struct BridgeJSLink { } } methodPrinter.write("},") - + enumExportPrinter.write(lines: methodPrinter.lines) - enumDtsPrinter.write("\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));") } - + let enumExportLines = enumExportPrinter.lines - let enumDtsLines = enumDtsPrinter.lines - + let enumPropertyPrinter = CodeFragmentPrinter() - let enumPropertyDtsPrinter = CodeFragmentPrinter() - + for property in enumDefinition.staticProperties { - let readonly = property.isReadonly ? "readonly " : "" - enumPropertyDtsPrinter.write("\(readonly)\(property.name): \(property.type.tsType);") - let getterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) let getterReturnExpr = try getterThunkBuilder.call( abiName: property.getterAbiName(), returnType: property.type ) - + enumPropertyPrinter.write("get \(property.name)() {") enumPropertyPrinter.indent { enumPropertyPrinter.write(contentsOf: getterThunkBuilder.body) @@ -242,7 +238,7 @@ struct BridgeJSLink { } } enumPropertyPrinter.write("},") - + if !property.isReadonly { let setterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) try setterThunkBuilder.lowerParameter( @@ -252,7 +248,7 @@ struct BridgeJSLink { abiName: property.setterAbiName(), returnType: .void ) - + enumPropertyPrinter.write("set \(property.name)(value) {") enumPropertyPrinter.indent { enumPropertyPrinter.write(contentsOf: setterThunkBuilder.body) @@ -262,17 +258,16 @@ struct BridgeJSLink { enumPropertyPrinter.write("},") } } - + let enumPropertyLines = enumPropertyPrinter.lines - let enumPropertyDtsLines = enumPropertyDtsPrinter.lines - + + let exportsPrinter = CodeFragmentPrinter() + let dtsExportsPrinter = CodeFragmentPrinter() + if !enumExportLines.isEmpty || !enumPropertyLines.isEmpty { - let exportsPrinter = CodeFragmentPrinter() - let dtsExportsPrinter = CodeFragmentPrinter() - exportsPrinter.write("\(enumDefinition.name): {") exportsPrinter.indent { - // Combine all lines and handle trailing comma removal + exportsPrinter.write("...\(enumValuesName),") var allLines = enumExportLines + enumPropertyLines if let lastLineIndex = allLines.indices.last, allLines[lastLineIndex].hasSuffix(",") { allLines[lastLineIndex] = String(allLines[lastLineIndex].dropLast()) @@ -280,21 +275,15 @@ struct BridgeJSLink { exportsPrinter.write(lines: allLines) } exportsPrinter.write("},") - - dtsExportsPrinter.write("\(enumDefinition.name): {") - dtsExportsPrinter.indent { - dtsExportsPrinter.write(lines: enumDtsLines) - dtsExportsPrinter.write(lines: enumPropertyDtsLines) - } - dtsExportsPrinter.write("}") - - data.exportsLines.append(contentsOf: exportsPrinter.lines) - data.dtsExportLines.append(contentsOf: dtsExportsPrinter.lines) + } else { + exportsPrinter.write("\(enumDefinition.name): \(enumValuesName),") } - - // Static properties are now handled in Exports type - no global assignments needed - } + dtsExportsPrinter.write("\(enumDefinition.name): \(enumDefinition.name)Object") + + data.exportsLines.append(contentsOf: exportsPrinter.lines) + data.dtsExportLines.append(contentsOf: dtsExportsPrinter.lines) + } } // Process imported skeletons @@ -585,6 +574,34 @@ struct BridgeJSLink { printer.nextLine() printer.write(lines: data.topLevelDtsEnumLines) + // Generate Object types for const-style enums + for skeleton in exportedSkeletons { + for enumDefinition in skeleton.enums + where enumDefinition.enumType != .namespace && enumDefinition.emitStyle != .tsEnum { + let enumValuesName = "\(enumDefinition.name)Values" + let enumObjectName = "\(enumDefinition.name)Object" + + if !enumDefinition.staticMethods.isEmpty || !enumDefinition.staticProperties.isEmpty { + printer.write("export type \(enumObjectName) = typeof \(enumValuesName) & {") + printer.indent { + for function in enumDefinition.staticMethods { + printer.write( + "\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));" + ) + } + for property in enumDefinition.staticProperties { + let readonly = property.isReadonly ? "readonly " : "" + printer.write("\(readonly)\(property.name): \(property.type.tsType);") + } + } + printer.write("};") + } else { + printer.write("export type \(enumObjectName) = typeof \(enumValuesName);") + } + printer.nextLine() + } + } + // Type definitions section (namespace declarations, class definitions, imported types) let namespaceBuilder = NamespaceBuilder() let namespaceDeclarationsLines = namespaceBuilder.namespaceDeclarations( @@ -657,7 +674,8 @@ struct BridgeJSLink { for enumDef in data.namespacedEnums { if enumDef.enumType != .namespace { let namespacePath = enumDef.namespace?.joined(separator: ".") ?? "" - printer.write("globalThis.\(namespacePath).\(enumDef.name) = \(enumDef.name);") + let enumValuesName = enumDef.emitStyle == .tsEnum ? enumDef.name : "\(enumDef.name)Values" + printer.write("globalThis.\(namespacePath).\(enumValuesName) = \(enumValuesName);") } } @@ -761,8 +779,9 @@ struct BridgeJSLink { for skeleton in exportedSkeletons { for enumDef in skeleton.enums where enumDef.enumType == .associatedValue { let base = enumDef.name + let baseValues = "\(base)Values" printer.write( - "const \(base)Helpers = __bjs_create\(base)Helpers()(\(JSGlueVariableScope.reservedTmpParamInts), \(JSGlueVariableScope.reservedTmpParamF32s), \(JSGlueVariableScope.reservedTmpParamF64s), \(JSGlueVariableScope.reservedTextEncoder), \(JSGlueVariableScope.reservedSwift));" + "const \(base)Helpers = __bjs_create\(baseValues)Helpers()(\(JSGlueVariableScope.reservedTmpParamInts), \(JSGlueVariableScope.reservedTmpParamF32s), \(JSGlueVariableScope.reservedTmpParamF64s), \(JSGlueVariableScope.reservedTextEncoder), \(JSGlueVariableScope.reservedSwift));" ) printer.write("enumHelpers.\(base) = \(base)Helpers;") printer.nextLine() @@ -1049,11 +1068,12 @@ struct BridgeJSLink { let scope = JSGlueVariableScope() let cleanup = CodeFragmentPrinter() let printer = CodeFragmentPrinter() + let enumValuesName = enumDefinition.emitStyle == .tsEnum ? enumDefinition.name : "\(enumDefinition.name)Values" switch enumDefinition.enumType { case .simple: let fragment = IntrinsicJSFragment.simpleEnumHelper(enumDefinition: enumDefinition) - _ = fragment.printCode([enumDefinition.name], scope, printer, cleanup) + _ = fragment.printCode([enumValuesName], scope, printer, cleanup) jsLines.append(contentsOf: printer.lines) case .rawValue: guard enumDefinition.rawType != nil else { @@ -1061,11 +1081,11 @@ struct BridgeJSLink { } let fragment = IntrinsicJSFragment.rawValueEnumHelper(enumDefinition: enumDefinition) - _ = fragment.printCode([enumDefinition.name], scope, printer, cleanup) + _ = fragment.printCode([enumValuesName], scope, printer, cleanup) jsLines.append(contentsOf: printer.lines) case .associatedValue: let fragment = IntrinsicJSFragment.associatedValueEnumHelper(enumDefinition: enumDefinition) - _ = fragment.printCode([enumDefinition.name], scope, printer, cleanup) + _ = fragment.printCode([enumValuesName], scope, printer, cleanup) jsLines.append(contentsOf: printer.lines) case .namespace: break @@ -1080,6 +1100,7 @@ struct BridgeJSLink { private func generateDeclarations(enumDefinition: ExportedEnum) -> [String] { let printer = CodeFragmentPrinter() + let enumValuesName = enumDefinition.emitStyle == .tsEnum ? enumDefinition.name : "\(enumDefinition.name)Values" switch enumDefinition.emitStyle { case .tsEnum: @@ -1105,7 +1126,7 @@ struct BridgeJSLink { } case .const: - printer.write("export const \(enumDefinition.name): {") + printer.write("export const \(enumValuesName): {") switch enumDefinition.enumType { case .simple, .rawValue: printer.indent { @@ -1123,7 +1144,7 @@ struct BridgeJSLink { } printer.write("};") printer.write( - "export type \(enumDefinition.name)Tag = typeof \(enumDefinition.name)[keyof typeof \(enumDefinition.name)];" + "export type \(enumDefinition.name)Tag = typeof \(enumValuesName)[keyof typeof \(enumValuesName)];" ) printer.nextLine() case .associatedValue: @@ -1145,11 +1166,11 @@ struct BridgeJSLink { for enumCase in enumDefinition.cases { if enumCase.associatedValues.isEmpty { unionParts.append( - "{ tag: typeof \(enumDefinition.name).Tag.\(enumCase.name.capitalizedFirstLetter) }" + "{ tag: typeof \(enumValuesName).Tag.\(enumCase.name.capitalizedFirstLetter) }" ) } else { var fields: [String] = [ - "tag: typeof \(enumDefinition.name).Tag.\(enumCase.name.capitalizedFirstLetter)" + "tag: typeof \(enumValuesName).Tag.\(enumCase.name.capitalizedFirstLetter)" ] for (associatedValueIndex, associatedValue) in enumCase.associatedValues.enumerated() { let prop = associatedValue.label ?? "param\(associatedValueIndex)" @@ -1159,7 +1180,9 @@ struct BridgeJSLink { } } - printer.write("export type \(enumDefinition.name) =") + let unionTypeName = + enumDefinition.emitStyle == .tsEnum ? enumDefinition.name : "\(enumDefinition.name)Tag" + printer.write("export type \(unionTypeName) =") printer.write(" " + unionParts.joined(separator: " | ")) printer.nextLine() case .namespace: @@ -1989,6 +2012,8 @@ extension BridgeJSLink { let sortedEnums = childNode.content.enums.sorted { $0.name < $1.name } for enumDefinition in sortedEnums { let style: EnumEmitStyle = enumDefinition.emitStyle + let enumValuesName = + enumDefinition.emitStyle == .tsEnum ? enumDefinition.name : "\(enumDefinition.name)Values" switch enumDefinition.enumType { case .simple: switch style { @@ -2002,7 +2027,7 @@ extension BridgeJSLink { } printer.write("}") case .const: - printer.write("const \(enumDefinition.name): {") + printer.write("const \(enumValuesName): {") printer.indent { for (index, enumCase) in enumDefinition.cases.enumerated() { let caseName = enumCase.name.capitalizedFirstLetter @@ -2011,7 +2036,7 @@ extension BridgeJSLink { } printer.write("};") printer.write( - "type \(enumDefinition.name) = typeof \(enumDefinition.name)[keyof typeof \(enumDefinition.name)];" + "type \(enumDefinition.name)Tag = typeof \(enumValuesName)[keyof typeof \(enumValuesName)];" ) } case .rawValue: @@ -2029,7 +2054,7 @@ extension BridgeJSLink { } printer.write("}") case .const: - printer.write("const \(enumDefinition.name): {") + printer.write("const \(enumValuesName): {") printer.indent { for enumCase in enumDefinition.cases { let caseName = enumCase.name.capitalizedFirstLetter @@ -2040,11 +2065,11 @@ extension BridgeJSLink { } printer.write("};") printer.write( - "type \(enumDefinition.name) = typeof \(enumDefinition.name)[keyof typeof \(enumDefinition.name)];" + "type \(enumDefinition.name)Tag = typeof \(enumValuesName)[keyof typeof \(enumValuesName)];" ) } case .associatedValue: - printer.write("const \(enumDefinition.name): {") + printer.write("const \(enumValuesName): {") printer.indent { printer.write("readonly Tag: {") printer.indent { @@ -2061,11 +2086,11 @@ extension BridgeJSLink { for enumCase in enumDefinition.cases { if enumCase.associatedValues.isEmpty { unionParts.append( - "{ tag: typeof \(enumDefinition.name).Tag.\(enumCase.name.capitalizedFirstLetter) }" + "{ tag: typeof \(enumValuesName).Tag.\(enumCase.name.capitalizedFirstLetter) }" ) } else { var fields: [String] = [ - "tag: typeof \(enumDefinition.name).Tag.\(enumCase.name.capitalizedFirstLetter)" + "tag: typeof \(enumValuesName).Tag.\(enumCase.name.capitalizedFirstLetter)" ] for (associatedValueIndex, associatedValue) in enumCase.associatedValues .enumerated() @@ -2076,7 +2101,9 @@ extension BridgeJSLink { unionParts.append("{ \(fields.joined(separator: "; ")) }") } } - printer.write("type \(enumDefinition.name) =") + let unionTypeName = + enumDefinition.emitStyle == .tsEnum ? enumDefinition.name : "\(enumDefinition.name)Tag" + printer.write("type \(unionTypeName) =") printer.write(" " + unionParts.joined(separator: " | ")) case .namespace: continue @@ -2274,11 +2301,11 @@ extension BridgeType { case .optional(let wrappedType): return "\(wrappedType.tsType) | null" case .caseEnum(let name): - return name + return "\(name)Tag" case .rawValueEnum(let name, _): - return name + return "\(name)Tag" case .associatedValueEnum(let name): - return name + return "\(name)Tag" case .namespaceEnum(let name): return name } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts index 4195e9a2..aac40514 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.d.ts @@ -4,7 +4,7 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. -export const APIResult: { +export const APIResultValues: { readonly Tag: { readonly Success: 0; readonly Failure: 1; @@ -15,10 +15,10 @@ export const APIResult: { }; }; -export type APIResult = - { tag: typeof APIResult.Tag.Success; param0: string } | { tag: typeof APIResult.Tag.Failure; param0: number } | { tag: typeof APIResult.Tag.Flag; param0: boolean } | { tag: typeof APIResult.Tag.Rate; param0: number } | { tag: typeof APIResult.Tag.Precise; param0: number } | { tag: typeof APIResult.Tag.Info } +export type APIResultTag = + { tag: typeof APIResultValues.Tag.Success; param0: string } | { tag: typeof APIResultValues.Tag.Failure; param0: number } | { tag: typeof APIResultValues.Tag.Flag; param0: boolean } | { tag: typeof APIResultValues.Tag.Rate; param0: number } | { tag: typeof APIResultValues.Tag.Precise; param0: number } | { tag: typeof APIResultValues.Tag.Info } -export const ComplexResult: { +export const ComplexResultValues: { readonly Tag: { readonly Success: 0; readonly Error: 1; @@ -29,10 +29,10 @@ export const ComplexResult: { }; }; -export type ComplexResult = - { tag: typeof ComplexResult.Tag.Success; param0: string } | { tag: typeof ComplexResult.Tag.Error; param0: string; param1: number } | { tag: typeof ComplexResult.Tag.Status; param0: boolean; param1: number; param2: string } | { tag: typeof ComplexResult.Tag.Coordinates; param0: number; param1: number; param2: number } | { tag: typeof ComplexResult.Tag.Comprehensive; param0: boolean; param1: boolean; param2: number; param3: number; param4: number; param5: number; param6: string; param7: string; param8: string } | { tag: typeof ComplexResult.Tag.Info } +export type ComplexResultTag = + { tag: typeof ComplexResultValues.Tag.Success; param0: string } | { tag: typeof ComplexResultValues.Tag.Error; param0: string; param1: number } | { tag: typeof ComplexResultValues.Tag.Status; param0: boolean; param1: number; param2: string } | { tag: typeof ComplexResultValues.Tag.Coordinates; param0: number; param1: number; param2: number } | { tag: typeof ComplexResultValues.Tag.Comprehensive; param0: boolean; param1: boolean; param2: number; param3: number; param4: number; param5: number; param6: string; param7: string; param8: string } | { tag: typeof ComplexResultValues.Tag.Info } -export const APIOptionalResult: { +export const APIOptionalResultValues: { readonly Tag: { readonly Success: 0; readonly Failure: 1; @@ -40,47 +40,62 @@ export const APIOptionalResult: { }; }; -export type APIOptionalResult = - { tag: typeof APIOptionalResult.Tag.Success; param0: string | null } | { tag: typeof APIOptionalResult.Tag.Failure; param0: number | null; param1: boolean | null } | { tag: typeof APIOptionalResult.Tag.Status; param0: boolean | null; param1: number | null; param2: string | null } +export type APIOptionalResultTag = + { tag: typeof APIOptionalResultValues.Tag.Success; param0: string | null } | { tag: typeof APIOptionalResultValues.Tag.Failure; param0: number | null; param1: boolean | null } | { tag: typeof APIOptionalResultValues.Tag.Status; param0: boolean | null; param1: number | null; param2: string | null } + +export type APIResultObject = typeof APIResultValues; + +export type ComplexResultObject = typeof ComplexResultValues; + +export type ResultObject = typeof ResultValues; + +export type NetworkingResultObject = typeof NetworkingResultValues; + +export type APIOptionalResultObject = typeof APIOptionalResultValues; export {}; declare global { namespace API { - const NetworkingResult: { + const NetworkingResultValues: { readonly Tag: { readonly Success: 0; readonly Failure: 1; }; }; - type NetworkingResult = - { tag: typeof NetworkingResult.Tag.Success; param0: string } | { tag: typeof NetworkingResult.Tag.Failure; param0: string; param1: number } + type NetworkingResultTag = + { tag: typeof NetworkingResultValues.Tag.Success; param0: string } | { tag: typeof NetworkingResultValues.Tag.Failure; param0: string; param1: number } } namespace Utilities { - const Result: { + const ResultValues: { readonly Tag: { readonly Success: 0; readonly Failure: 1; readonly Status: 2; }; }; - type Result = - { tag: typeof Result.Tag.Success; param0: string } | { tag: typeof Result.Tag.Failure; param0: string; param1: number } | { tag: typeof Result.Tag.Status; param0: boolean; param1: number; param2: string } + type ResultTag = + { tag: typeof ResultValues.Tag.Success; param0: string } | { tag: typeof ResultValues.Tag.Failure; param0: string; param1: number } | { tag: typeof ResultValues.Tag.Status; param0: boolean; param1: number; param2: string } } } export type Exports = { - handle(result: APIResult): void; - getResult(): APIResult; - roundtripAPIResult(result: APIResult): APIResult; - roundTripOptionalAPIResult(result: APIResult | null): APIResult | null; - handleComplex(result: ComplexResult): void; - getComplexResult(): ComplexResult; - roundtripComplexResult(result: ComplexResult): ComplexResult; - roundTripOptionalComplexResult(result: ComplexResult | null): ComplexResult | null; - roundTripOptionalUtilitiesResult(result: Utilities.Result | null): Utilities.Result | null; - roundTripOptionalNetworkingResult(result: NetworkingResult | null): NetworkingResult | null; - roundTripOptionalAPIOptionalResult(result: APIOptionalResult | null): APIOptionalResult | null; + handle(result: APIResultTag): void; + getResult(): APIResultTag; + roundtripAPIResult(result: APIResultTag): APIResultTag; + roundTripOptionalAPIResult(result: APIResultTag | null): APIResultTag | null; + handleComplex(result: ComplexResultTag): void; + getComplexResult(): ComplexResultTag; + roundtripComplexResult(result: ComplexResultTag): ComplexResultTag; + roundTripOptionalComplexResult(result: ComplexResultTag | null): ComplexResultTag | null; + roundTripOptionalUtilitiesResult(result: Utilities.ResultTag | null): Utilities.ResultTag | null; + roundTripOptionalNetworkingResult(result: NetworkingResultTag | null): NetworkingResultTag | null; + roundTripOptionalAPIOptionalResult(result: APIOptionalResultTag | null): APIOptionalResultTag | null; + APIResult: APIResultObject + ComplexResult: ComplexResultObject + Result: ResultObject + NetworkingResult: NetworkingResultObject + APIOptionalResult: APIOptionalResultObject } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js index b0fb70ae..ea0aef15 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumAssociatedValue.Export.js @@ -4,7 +4,7 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. -export const APIResult = { +export const APIResultValues = { Tag: { Success: 0, Failure: 1, @@ -15,12 +15,12 @@ export const APIResult = { }, }; -const __bjs_createAPIResultHelpers = () => { +const __bjs_createAPIResultValuesHelpers = () => { return (tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift) => ({ lower: (value) => { const enumTag = value.tag; switch (enumTag) { - case APIResult.Tag.Success: { + case APIResultValues.Tag.Success: { const bytes = textEncoder.encode(value.param0); const id = swift.memory.retain(bytes); tmpParamInts.push(bytes.length); @@ -28,65 +28,65 @@ const __bjs_createAPIResultHelpers = () => { const cleanup = () => { swift.memory.release(id); }; - return { caseId: APIResult.Tag.Success, cleanup }; + return { caseId: APIResultValues.Tag.Success, cleanup }; } - case APIResult.Tag.Failure: { + case APIResultValues.Tag.Failure: { tmpParamInts.push((value.param0 | 0)); const cleanup = undefined; - return { caseId: APIResult.Tag.Failure, cleanup }; + return { caseId: APIResultValues.Tag.Failure, cleanup }; } - case APIResult.Tag.Flag: { + case APIResultValues.Tag.Flag: { tmpParamInts.push(value.param0 ? 1 : 0); const cleanup = undefined; - return { caseId: APIResult.Tag.Flag, cleanup }; + return { caseId: APIResultValues.Tag.Flag, cleanup }; } - case APIResult.Tag.Rate: { + case APIResultValues.Tag.Rate: { tmpParamF32s.push(Math.fround(value.param0)); const cleanup = undefined; - return { caseId: APIResult.Tag.Rate, cleanup }; + return { caseId: APIResultValues.Tag.Rate, cleanup }; } - case APIResult.Tag.Precise: { + case APIResultValues.Tag.Precise: { tmpParamF64s.push(value.param0); const cleanup = undefined; - return { caseId: APIResult.Tag.Precise, cleanup }; + return { caseId: APIResultValues.Tag.Precise, cleanup }; } - case APIResult.Tag.Info: { + case APIResultValues.Tag.Info: { const cleanup = undefined; - return { caseId: APIResult.Tag.Info, cleanup }; + return { caseId: APIResultValues.Tag.Info, cleanup }; } - default: throw new Error("Unknown APIResult tag: " + String(enumTag)); + default: throw new Error("Unknown APIResultValues tag: " + String(enumTag)); } }, raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s) => { const tag = tmpRetTag | 0; switch (tag) { - case APIResult.Tag.Success: { + case APIResultValues.Tag.Success: { const string = tmpRetStrings.pop(); - return { tag: APIResult.Tag.Success, param0: string }; + return { tag: APIResultValues.Tag.Success, param0: string }; } - case APIResult.Tag.Failure: { + case APIResultValues.Tag.Failure: { const int = tmpRetInts.pop(); - return { tag: APIResult.Tag.Failure, param0: int }; + return { tag: APIResultValues.Tag.Failure, param0: int }; } - case APIResult.Tag.Flag: { + case APIResultValues.Tag.Flag: { const bool = tmpRetInts.pop(); - return { tag: APIResult.Tag.Flag, param0: bool }; + return { tag: APIResultValues.Tag.Flag, param0: bool }; } - case APIResult.Tag.Rate: { + case APIResultValues.Tag.Rate: { const f32 = tmpRetF32s.pop(); - return { tag: APIResult.Tag.Rate, param0: f32 }; + return { tag: APIResultValues.Tag.Rate, param0: f32 }; } - case APIResult.Tag.Precise: { + case APIResultValues.Tag.Precise: { const f64 = tmpRetF64s.pop(); - return { tag: APIResult.Tag.Precise, param0: f64 }; + return { tag: APIResultValues.Tag.Precise, param0: f64 }; } - case APIResult.Tag.Info: return { tag: APIResult.Tag.Info }; - default: throw new Error("Unknown APIResult tag returned from Swift: " + String(tag)); + case APIResultValues.Tag.Info: return { tag: APIResultValues.Tag.Info }; + default: throw new Error("Unknown APIResultValues tag returned from Swift: " + String(tag)); } } }); }; -export const ComplexResult = { +export const ComplexResultValues = { Tag: { Success: 0, Error: 1, @@ -97,12 +97,12 @@ export const ComplexResult = { }, }; -const __bjs_createComplexResultHelpers = () => { +const __bjs_createComplexResultValuesHelpers = () => { return (tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift) => ({ lower: (value) => { const enumTag = value.tag; switch (enumTag) { - case ComplexResult.Tag.Success: { + case ComplexResultValues.Tag.Success: { const bytes = textEncoder.encode(value.param0); const id = swift.memory.retain(bytes); tmpParamInts.push(bytes.length); @@ -110,9 +110,9 @@ const __bjs_createComplexResultHelpers = () => { const cleanup = () => { swift.memory.release(id); }; - return { caseId: ComplexResult.Tag.Success, cleanup }; + return { caseId: ComplexResultValues.Tag.Success, cleanup }; } - case ComplexResult.Tag.Error: { + case ComplexResultValues.Tag.Error: { tmpParamInts.push((value.param1 | 0)); const bytes = textEncoder.encode(value.param0); const id = swift.memory.retain(bytes); @@ -121,9 +121,9 @@ const __bjs_createComplexResultHelpers = () => { const cleanup = () => { swift.memory.release(id); }; - return { caseId: ComplexResult.Tag.Error, cleanup }; + return { caseId: ComplexResultValues.Tag.Error, cleanup }; } - case ComplexResult.Tag.Status: { + case ComplexResultValues.Tag.Status: { const bytes = textEncoder.encode(value.param2); const id = swift.memory.retain(bytes); tmpParamInts.push(bytes.length); @@ -133,16 +133,16 @@ const __bjs_createComplexResultHelpers = () => { const cleanup = () => { swift.memory.release(id); }; - return { caseId: ComplexResult.Tag.Status, cleanup }; + return { caseId: ComplexResultValues.Tag.Status, cleanup }; } - case ComplexResult.Tag.Coordinates: { + case ComplexResultValues.Tag.Coordinates: { tmpParamF64s.push(value.param2); tmpParamF64s.push(value.param1); tmpParamF64s.push(value.param0); const cleanup = undefined; - return { caseId: ComplexResult.Tag.Coordinates, cleanup }; + return { caseId: ComplexResultValues.Tag.Coordinates, cleanup }; } - case ComplexResult.Tag.Comprehensive: { + case ComplexResultValues.Tag.Comprehensive: { const bytes = textEncoder.encode(value.param8); const id = swift.memory.retain(bytes); tmpParamInts.push(bytes.length); @@ -166,40 +166,40 @@ const __bjs_createComplexResultHelpers = () => { swift.memory.release(id1); swift.memory.release(id2); }; - return { caseId: ComplexResult.Tag.Comprehensive, cleanup }; + return { caseId: ComplexResultValues.Tag.Comprehensive, cleanup }; } - case ComplexResult.Tag.Info: { + case ComplexResultValues.Tag.Info: { const cleanup = undefined; - return { caseId: ComplexResult.Tag.Info, cleanup }; + return { caseId: ComplexResultValues.Tag.Info, cleanup }; } - default: throw new Error("Unknown ComplexResult tag: " + String(enumTag)); + default: throw new Error("Unknown ComplexResultValues tag: " + String(enumTag)); } }, raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s) => { const tag = tmpRetTag | 0; switch (tag) { - case ComplexResult.Tag.Success: { + case ComplexResultValues.Tag.Success: { const string = tmpRetStrings.pop(); - return { tag: ComplexResult.Tag.Success, param0: string }; + return { tag: ComplexResultValues.Tag.Success, param0: string }; } - case ComplexResult.Tag.Error: { + case ComplexResultValues.Tag.Error: { const int = tmpRetInts.pop(); const string = tmpRetStrings.pop(); - return { tag: ComplexResult.Tag.Error, param0: string, param1: int }; + return { tag: ComplexResultValues.Tag.Error, param0: string, param1: int }; } - case ComplexResult.Tag.Status: { + case ComplexResultValues.Tag.Status: { const string = tmpRetStrings.pop(); const int = tmpRetInts.pop(); const bool = tmpRetInts.pop(); - return { tag: ComplexResult.Tag.Status, param0: bool, param1: int, param2: string }; + return { tag: ComplexResultValues.Tag.Status, param0: bool, param1: int, param2: string }; } - case ComplexResult.Tag.Coordinates: { + case ComplexResultValues.Tag.Coordinates: { const f64 = tmpRetF64s.pop(); const f641 = tmpRetF64s.pop(); const f642 = tmpRetF64s.pop(); - return { tag: ComplexResult.Tag.Coordinates, param0: f642, param1: f641, param2: f64 }; + return { tag: ComplexResultValues.Tag.Coordinates, param0: f642, param1: f641, param2: f64 }; } - case ComplexResult.Tag.Comprehensive: { + case ComplexResultValues.Tag.Comprehensive: { const string = tmpRetStrings.pop(); const string1 = tmpRetStrings.pop(); const string2 = tmpRetStrings.pop(); @@ -209,15 +209,15 @@ const __bjs_createComplexResultHelpers = () => { const int1 = tmpRetInts.pop(); const bool = tmpRetInts.pop(); const bool1 = tmpRetInts.pop(); - return { tag: ComplexResult.Tag.Comprehensive, param0: bool1, param1: bool, param2: int1, param3: int, param4: f641, param5: f64, param6: string2, param7: string1, param8: string }; + return { tag: ComplexResultValues.Tag.Comprehensive, param0: bool1, param1: bool, param2: int1, param3: int, param4: f641, param5: f64, param6: string2, param7: string1, param8: string }; } - case ComplexResult.Tag.Info: return { tag: ComplexResult.Tag.Info }; - default: throw new Error("Unknown ComplexResult tag returned from Swift: " + String(tag)); + case ComplexResultValues.Tag.Info: return { tag: ComplexResultValues.Tag.Info }; + default: throw new Error("Unknown ComplexResultValues tag returned from Swift: " + String(tag)); } } }); }; -export const Result = { +export const ResultValues = { Tag: { Success: 0, Failure: 1, @@ -225,12 +225,12 @@ export const Result = { }, }; -const __bjs_createResultHelpers = () => { +const __bjs_createResultValuesHelpers = () => { return (tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift) => ({ lower: (value) => { const enumTag = value.tag; switch (enumTag) { - case Result.Tag.Success: { + case ResultValues.Tag.Success: { const bytes = textEncoder.encode(value.param0); const id = swift.memory.retain(bytes); tmpParamInts.push(bytes.length); @@ -238,9 +238,9 @@ const __bjs_createResultHelpers = () => { const cleanup = () => { swift.memory.release(id); }; - return { caseId: Result.Tag.Success, cleanup }; + return { caseId: ResultValues.Tag.Success, cleanup }; } - case Result.Tag.Failure: { + case ResultValues.Tag.Failure: { tmpParamInts.push((value.param1 | 0)); const bytes = textEncoder.encode(value.param0); const id = swift.memory.retain(bytes); @@ -249,9 +249,9 @@ const __bjs_createResultHelpers = () => { const cleanup = () => { swift.memory.release(id); }; - return { caseId: Result.Tag.Failure, cleanup }; + return { caseId: ResultValues.Tag.Failure, cleanup }; } - case Result.Tag.Status: { + case ResultValues.Tag.Status: { const bytes = textEncoder.encode(value.param2); const id = swift.memory.retain(bytes); tmpParamInts.push(bytes.length); @@ -261,47 +261,47 @@ const __bjs_createResultHelpers = () => { const cleanup = () => { swift.memory.release(id); }; - return { caseId: Result.Tag.Status, cleanup }; + return { caseId: ResultValues.Tag.Status, cleanup }; } - default: throw new Error("Unknown Result tag: " + String(enumTag)); + default: throw new Error("Unknown ResultValues tag: " + String(enumTag)); } }, raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s) => { const tag = tmpRetTag | 0; switch (tag) { - case Result.Tag.Success: { + case ResultValues.Tag.Success: { const string = tmpRetStrings.pop(); - return { tag: Result.Tag.Success, param0: string }; + return { tag: ResultValues.Tag.Success, param0: string }; } - case Result.Tag.Failure: { + case ResultValues.Tag.Failure: { const int = tmpRetInts.pop(); const string = tmpRetStrings.pop(); - return { tag: Result.Tag.Failure, param0: string, param1: int }; + return { tag: ResultValues.Tag.Failure, param0: string, param1: int }; } - case Result.Tag.Status: { + case ResultValues.Tag.Status: { const string = tmpRetStrings.pop(); const int = tmpRetInts.pop(); const bool = tmpRetInts.pop(); - return { tag: Result.Tag.Status, param0: bool, param1: int, param2: string }; + return { tag: ResultValues.Tag.Status, param0: bool, param1: int, param2: string }; } - default: throw new Error("Unknown Result tag returned from Swift: " + String(tag)); + default: throw new Error("Unknown ResultValues tag returned from Swift: " + String(tag)); } } }); }; -export const NetworkingResult = { +export const NetworkingResultValues = { Tag: { Success: 0, Failure: 1, }, }; -const __bjs_createNetworkingResultHelpers = () => { +const __bjs_createNetworkingResultValuesHelpers = () => { return (tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift) => ({ lower: (value) => { const enumTag = value.tag; switch (enumTag) { - case NetworkingResult.Tag.Success: { + case NetworkingResultValues.Tag.Success: { const bytes = textEncoder.encode(value.param0); const id = swift.memory.retain(bytes); tmpParamInts.push(bytes.length); @@ -309,9 +309,9 @@ const __bjs_createNetworkingResultHelpers = () => { const cleanup = () => { swift.memory.release(id); }; - return { caseId: NetworkingResult.Tag.Success, cleanup }; + return { caseId: NetworkingResultValues.Tag.Success, cleanup }; } - case NetworkingResult.Tag.Failure: { + case NetworkingResultValues.Tag.Failure: { tmpParamInts.push((value.param1 | 0)); const bytes = textEncoder.encode(value.param0); const id = swift.memory.retain(bytes); @@ -320,29 +320,29 @@ const __bjs_createNetworkingResultHelpers = () => { const cleanup = () => { swift.memory.release(id); }; - return { caseId: NetworkingResult.Tag.Failure, cleanup }; + return { caseId: NetworkingResultValues.Tag.Failure, cleanup }; } - default: throw new Error("Unknown NetworkingResult tag: " + String(enumTag)); + default: throw new Error("Unknown NetworkingResultValues tag: " + String(enumTag)); } }, raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s) => { const tag = tmpRetTag | 0; switch (tag) { - case NetworkingResult.Tag.Success: { + case NetworkingResultValues.Tag.Success: { const string = tmpRetStrings.pop(); - return { tag: NetworkingResult.Tag.Success, param0: string }; + return { tag: NetworkingResultValues.Tag.Success, param0: string }; } - case NetworkingResult.Tag.Failure: { + case NetworkingResultValues.Tag.Failure: { const int = tmpRetInts.pop(); const string = tmpRetStrings.pop(); - return { tag: NetworkingResult.Tag.Failure, param0: string, param1: int }; + return { tag: NetworkingResultValues.Tag.Failure, param0: string, param1: int }; } - default: throw new Error("Unknown NetworkingResult tag returned from Swift: " + String(tag)); + default: throw new Error("Unknown NetworkingResultValues tag returned from Swift: " + String(tag)); } } }); }; -export const APIOptionalResult = { +export const APIOptionalResultValues = { Tag: { Success: 0, Failure: 1, @@ -350,12 +350,12 @@ export const APIOptionalResult = { }, }; -const __bjs_createAPIOptionalResultHelpers = () => { +const __bjs_createAPIOptionalResultValuesHelpers = () => { return (tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift) => ({ lower: (value) => { const enumTag = value.tag; switch (enumTag) { - case APIOptionalResult.Tag.Success: { + case APIOptionalResultValues.Tag.Success: { const isSome = value.param0 != null; let id; if (isSome) { @@ -373,9 +373,9 @@ const __bjs_createAPIOptionalResultHelpers = () => { swift.memory.release(id); } }; - return { caseId: APIOptionalResult.Tag.Success, cleanup }; + return { caseId: APIOptionalResultValues.Tag.Success, cleanup }; } - case APIOptionalResult.Tag.Failure: { + case APIOptionalResultValues.Tag.Failure: { const isSome = value.param1 != null; tmpParamInts.push(isSome ? (value.param1 ? 1 : 0) : 0); tmpParamInts.push(isSome ? 1 : 0); @@ -383,9 +383,9 @@ const __bjs_createAPIOptionalResultHelpers = () => { tmpParamInts.push(isSome1 ? (value.param0 | 0) : 0); tmpParamInts.push(isSome1 ? 1 : 0); const cleanup = undefined; - return { caseId: APIOptionalResult.Tag.Failure, cleanup }; + return { caseId: APIOptionalResultValues.Tag.Failure, cleanup }; } - case APIOptionalResult.Tag.Status: { + case APIOptionalResultValues.Tag.Status: { const isSome = value.param2 != null; let id; if (isSome) { @@ -409,15 +409,15 @@ const __bjs_createAPIOptionalResultHelpers = () => { swift.memory.release(id); } }; - return { caseId: APIOptionalResult.Tag.Status, cleanup }; + return { caseId: APIOptionalResultValues.Tag.Status, cleanup }; } - default: throw new Error("Unknown APIOptionalResult tag: " + String(enumTag)); + default: throw new Error("Unknown APIOptionalResultValues tag: " + String(enumTag)); } }, raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s) => { const tag = tmpRetTag | 0; switch (tag) { - case APIOptionalResult.Tag.Success: { + case APIOptionalResultValues.Tag.Success: { const isSome = tmpRetInts.pop(); let optional; if (isSome) { @@ -426,9 +426,9 @@ const __bjs_createAPIOptionalResultHelpers = () => { } else { optional = null; } - return { tag: APIOptionalResult.Tag.Success, param0: optional }; + return { tag: APIOptionalResultValues.Tag.Success, param0: optional }; } - case APIOptionalResult.Tag.Failure: { + case APIOptionalResultValues.Tag.Failure: { const isSome = tmpRetInts.pop(); let optional; if (isSome) { @@ -445,9 +445,9 @@ const __bjs_createAPIOptionalResultHelpers = () => { } else { optional1 = null; } - return { tag: APIOptionalResult.Tag.Failure, param0: optional1, param1: optional }; + return { tag: APIOptionalResultValues.Tag.Failure, param0: optional1, param1: optional }; } - case APIOptionalResult.Tag.Status: { + case APIOptionalResultValues.Tag.Status: { const isSome = tmpRetInts.pop(); let optional; if (isSome) { @@ -472,9 +472,9 @@ const __bjs_createAPIOptionalResultHelpers = () => { } else { optional2 = null; } - return { tag: APIOptionalResult.Tag.Status, param0: optional2, param1: optional1, param2: optional }; + return { tag: APIOptionalResultValues.Tag.Status, param0: optional2, param1: optional1, param2: optional }; } - default: throw new Error("Unknown APIOptionalResult tag returned from Swift: " + String(tag)); + default: throw new Error("Unknown APIOptionalResultValues tag returned from Swift: " + String(tag)); } } }); @@ -485,8 +485,8 @@ if (typeof globalThis.API === 'undefined') { if (typeof globalThis.Utilities === 'undefined') { globalThis.Utilities = {}; } -globalThis.Utilities.Result = Result; -globalThis.API.NetworkingResult = NetworkingResult; +globalThis.Utilities.ResultValues = ResultValues; +globalThis.API.NetworkingResultValues = NetworkingResultValues; export async function createInstantiator(options, swift) { let instance; let memory; @@ -627,19 +627,19 @@ export async function createInstantiator(options, swift) { instance = i; memory = instance.exports.memory; - const APIResultHelpers = __bjs_createAPIResultHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); + const APIResultHelpers = __bjs_createAPIResultValuesHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); enumHelpers.APIResult = APIResultHelpers; - const ComplexResultHelpers = __bjs_createComplexResultHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); + const ComplexResultHelpers = __bjs_createComplexResultValuesHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); enumHelpers.ComplexResult = ComplexResultHelpers; - const ResultHelpers = __bjs_createResultHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); + const ResultHelpers = __bjs_createResultValuesHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); enumHelpers.Result = ResultHelpers; - const NetworkingResultHelpers = __bjs_createNetworkingResultHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); + const NetworkingResultHelpers = __bjs_createNetworkingResultValuesHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); enumHelpers.NetworkingResult = NetworkingResultHelpers; - const APIOptionalResultHelpers = __bjs_createAPIOptionalResultHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); + const APIOptionalResultHelpers = __bjs_createAPIOptionalResultValuesHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); enumHelpers.APIOptionalResult = APIOptionalResultHelpers; setException = (error) => { @@ -785,6 +785,11 @@ export async function createInstantiator(options, swift) { if (resultCleanup) { resultCleanup(); } return optResult; }, + APIResult: APIResultValues, + ComplexResult: ComplexResultValues, + Result: ResultValues, + NetworkingResult: NetworkingResultValues, + APIOptionalResult: APIOptionalResultValues, }; return exports; }, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts index 87c12b29..47fb52d9 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.d.ts @@ -4,20 +4,20 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. -export const Direction: { +export const DirectionValues: { readonly North: 0; readonly South: 1; readonly East: 2; readonly West: 3; }; -export type DirectionTag = typeof Direction[keyof typeof Direction]; +export type DirectionTag = typeof DirectionValues[keyof typeof DirectionValues]; -export const Status: { +export const StatusValues: { readonly Loading: 0; readonly Success: 1; readonly Error: 2; }; -export type StatusTag = typeof Status[keyof typeof Status]; +export type StatusTag = typeof StatusValues[keyof typeof StatusValues]; export enum TSDirection { North = 0, @@ -26,19 +26,28 @@ export enum TSDirection { West = 3, } -export const PublicStatus: { +export const PublicStatusValues: { readonly Success: 0; }; -export type PublicStatusTag = typeof PublicStatus[keyof typeof PublicStatus]; +export type PublicStatusTag = typeof PublicStatusValues[keyof typeof PublicStatusValues]; + +export type DirectionObject = typeof DirectionValues; + +export type StatusObject = typeof StatusValues; + +export type PublicStatusObject = typeof PublicStatusValues; export type Exports = { - setDirection(direction: Direction): void; - getDirection(): Direction; - processDirection(input: Direction): Status; - roundTripOptionalDirection(input: Direction | null): Direction | null; - setTSDirection(direction: TSDirection): void; - getTSDirection(): TSDirection; - roundTripOptionalTSDirection(input: TSDirection | null): TSDirection | null; + setDirection(direction: DirectionTag): void; + getDirection(): DirectionTag; + processDirection(input: DirectionTag): StatusTag; + roundTripOptionalDirection(input: DirectionTag | null): DirectionTag | null; + setTSDirection(direction: TSDirectionTag): void; + getTSDirection(): TSDirectionTag; + roundTripOptionalTSDirection(input: TSDirectionTag | null): TSDirectionTag | null; + Direction: DirectionObject + Status: StatusObject + PublicStatus: PublicStatusObject } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js index 5eb0b420..9cbed9a2 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumCase.Export.js @@ -4,14 +4,14 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. -export const Direction = { +export const DirectionValues = { North: 0, South: 1, East: 2, West: 3, }; -export const Status = { +export const StatusValues = { Loading: 0, Success: 1, Error: 2, @@ -24,7 +24,7 @@ export const TSDirection = { West: 3, }; -export const PublicStatus = { +export const PublicStatusValues = { Success: 0, }; @@ -207,6 +207,9 @@ export async function createInstantiator(options, swift) { tmpRetOptionalInt = undefined; return optResult; }, + Direction: DirectionValues, + Status: StatusValues, + PublicStatus: PublicStatusValues, }; }, } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.d.ts index 3d37ca6c..7c05f171 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.d.ts @@ -4,49 +4,57 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. +export type MethodObject = typeof MethodValues; + +export type LogLevelObject = typeof LogLevelValues; + +export type PortObject = typeof PortValues; + +export type SupportedMethodObject = typeof SupportedMethodValues; + export {}; declare global { namespace Configuration { - const LogLevel: { + const LogLevelValues: { readonly Debug: "debug"; readonly Info: "info"; readonly Warning: "warning"; readonly Error: "error"; }; - type LogLevel = typeof LogLevel[keyof typeof LogLevel]; - const Port: { + type LogLevelTag = typeof LogLevelValues[keyof typeof LogLevelValues]; + const PortValues: { readonly Http: 80; readonly Https: 443; readonly Development: 3000; }; - type Port = typeof Port[keyof typeof Port]; + type PortTag = typeof PortValues[keyof typeof PortValues]; } namespace Networking { namespace API { class HTTPServer { constructor(); - call(method: Networking.API.Method): void; + call(method: Networking.API.MethodTag): void; } - const Method: { + const MethodValues: { readonly Get: 0; readonly Post: 1; readonly Put: 2; readonly Delete: 3; }; - type Method = typeof Method[keyof typeof Method]; + type MethodTag = typeof MethodValues[keyof typeof MethodValues]; } namespace APIV2 { namespace Internal { class TestServer { constructor(); - call(method: Internal.SupportedMethod): void; + call(method: Internal.SupportedMethodTag): void; } - const SupportedMethod: { + const SupportedMethodValues: { readonly Get: 0; readonly Post: 1; }; - type SupportedMethod = typeof SupportedMethod[keyof typeof SupportedMethod]; + type SupportedMethodTag = typeof SupportedMethodValues[keyof typeof SupportedMethodValues]; } } } @@ -69,10 +77,10 @@ export interface Converter extends SwiftHeapObject { toString(value: number): string; } export interface HTTPServer extends SwiftHeapObject { - call(method: Networking.API.Method): void; + call(method: Networking.API.MethodTag): void; } export interface TestServer extends SwiftHeapObject { - call(method: Internal.SupportedMethod): void; + call(method: Internal.SupportedMethodTag): void; } export type Exports = { Converter: { @@ -84,6 +92,10 @@ export type Exports = { TestServer: { new(): TestServer; } + Method: MethodObject + LogLevel: LogLevelObject + Port: PortObject + SupportedMethod: SupportedMethodObject } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js index 5b6bda0f..b934456f 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumNamespace.Export.js @@ -4,27 +4,27 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. -export const Method = { +export const MethodValues = { Get: 0, Post: 1, Put: 2, Delete: 3, }; -export const LogLevel = { +export const LogLevelValues = { Debug: "debug", Info: "info", Warning: "warning", Error: "error", }; -export const Port = { +export const PortValues = { Http: 80, Https: 443, Development: 3000, }; -export const SupportedMethod = { +export const SupportedMethodValues = { Get: 0, Post: 1, }; @@ -44,10 +44,10 @@ if (typeof globalThis.Networking.APIV2 === 'undefined') { if (typeof globalThis.Networking.APIV2.Internal === 'undefined') { globalThis.Networking.APIV2.Internal = {}; } -globalThis.Networking.API.Method = Method; -globalThis.Configuration.LogLevel = LogLevel; -globalThis.Configuration.Port = Port; -globalThis.Networking.APIV2.Internal.SupportedMethod = SupportedMethod; +globalThis.Networking.API.MethodValues = MethodValues; +globalThis.Configuration.LogLevelValues = LogLevelValues; +globalThis.Configuration.PortValues = PortValues; +globalThis.Networking.APIV2.Internal.SupportedMethodValues = SupportedMethodValues; export async function createInstantiator(options, swift) { let instance; let memory; @@ -293,6 +293,10 @@ export async function createInstantiator(options, swift) { Converter, HTTPServer, TestServer, + Method: MethodValues, + LogLevel: LogLevelValues, + Port: PortValues, + SupportedMethod: SupportedMethodValues, }; globalThis.Utils.Converter = exports.Converter; globalThis.Networking.API.HTTPServer = exports.HTTPServer; diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.d.ts index 1d2b3ecf..b92c54ce 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.d.ts @@ -4,12 +4,12 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. -export const Theme: { +export const ThemeValues: { readonly Light: "light"; readonly Dark: "dark"; readonly Auto: "auto"; }; -export type ThemeTag = typeof Theme[keyof typeof Theme]; +export type ThemeTag = typeof ThemeValues[keyof typeof ThemeValues]; export enum TSTheme { Light = "light", @@ -17,18 +17,18 @@ export enum TSTheme { Auto = "auto", } -export const FeatureFlag: { +export const FeatureFlagValues: { readonly Enabled: true; readonly Disabled: false; }; -export type FeatureFlagTag = typeof FeatureFlag[keyof typeof FeatureFlag]; +export type FeatureFlagTag = typeof FeatureFlagValues[keyof typeof FeatureFlagValues]; -export const HttpStatus: { +export const HttpStatusValues: { readonly Ok: 200; readonly NotFound: 404; readonly ServerError: 500; }; -export type HttpStatusTag = typeof HttpStatus[keyof typeof HttpStatus]; +export type HttpStatusTag = typeof HttpStatusValues[keyof typeof HttpStatusValues]; export enum TSHttpStatus { Ok = 200, @@ -36,99 +36,129 @@ export enum TSHttpStatus { ServerError = 500, } -export const Priority: { +export const PriorityValues: { readonly Lowest: 1; readonly Low: 2; readonly Medium: 3; readonly High: 4; readonly Highest: 5; }; -export type PriorityTag = typeof Priority[keyof typeof Priority]; +export type PriorityTag = typeof PriorityValues[keyof typeof PriorityValues]; -export const FileSize: { +export const FileSizeValues: { readonly Tiny: 1024; readonly Small: 10240; readonly Medium: 102400; readonly Large: 1048576; }; -export type FileSizeTag = typeof FileSize[keyof typeof FileSize]; +export type FileSizeTag = typeof FileSizeValues[keyof typeof FileSizeValues]; -export const UserId: { +export const UserIdValues: { readonly Guest: 0; readonly User: 1000; readonly Admin: 9999; }; -export type UserIdTag = typeof UserId[keyof typeof UserId]; +export type UserIdTag = typeof UserIdValues[keyof typeof UserIdValues]; -export const TokenId: { +export const TokenIdValues: { readonly Invalid: 0; readonly Session: 12345; readonly Refresh: 67890; }; -export type TokenIdTag = typeof TokenId[keyof typeof TokenId]; +export type TokenIdTag = typeof TokenIdValues[keyof typeof TokenIdValues]; -export const SessionId: { +export const SessionIdValues: { readonly None: 0; readonly Active: 9876543210; readonly Expired: 1234567890; }; -export type SessionIdTag = typeof SessionId[keyof typeof SessionId]; +export type SessionIdTag = typeof SessionIdValues[keyof typeof SessionIdValues]; -export const Precision: { +export const PrecisionValues: { readonly Rough: 0.1; readonly Normal: 0.01; readonly Fine: 0.001; }; -export type PrecisionTag = typeof Precision[keyof typeof Precision]; +export type PrecisionTag = typeof PrecisionValues[keyof typeof PrecisionValues]; -export const Ratio: { +export const RatioValues: { readonly Quarter: 0.25; readonly Half: 0.5; readonly Golden: 1.618; readonly Pi: 3.14159; }; -export type RatioTag = typeof Ratio[keyof typeof Ratio]; +export type RatioTag = typeof RatioValues[keyof typeof RatioValues]; + +export type ThemeObject = typeof ThemeValues; + +export type FeatureFlagObject = typeof FeatureFlagValues; + +export type HttpStatusObject = typeof HttpStatusValues; + +export type PriorityObject = typeof PriorityValues; + +export type FileSizeObject = typeof FileSizeValues; + +export type UserIdObject = typeof UserIdValues; + +export type TokenIdObject = typeof TokenIdValues; + +export type SessionIdObject = typeof SessionIdValues; + +export type PrecisionObject = typeof PrecisionValues; + +export type RatioObject = typeof RatioValues; export type Exports = { - setTheme(theme: Theme): void; - getTheme(): Theme; - roundTripOptionalTheme(input: Theme | null): Theme | null; - setTSTheme(theme: TSTheme): void; - getTSTheme(): TSTheme; - roundTripOptionalTSTheme(input: TSTheme | null): TSTheme | null; - setFeatureFlag(flag: FeatureFlag): void; - getFeatureFlag(): FeatureFlag; - roundTripOptionalFeatureFlag(input: FeatureFlag | null): FeatureFlag | null; - setHttpStatus(status: HttpStatus): void; - getHttpStatus(): HttpStatus; - roundTripOptionalHttpStatus(input: HttpStatus | null): HttpStatus | null; - setTSHttpStatus(status: TSHttpStatus): void; - getTSHttpStatus(): TSHttpStatus; - roundTripOptionalHttpStatus(input: TSHttpStatus | null): TSHttpStatus | null; - setPriority(priority: Priority): void; - getPriority(): Priority; - roundTripOptionalPriority(input: Priority | null): Priority | null; - setFileSize(size: FileSize): void; - getFileSize(): FileSize; - roundTripOptionalFileSize(input: FileSize | null): FileSize | null; - setUserId(id: UserId): void; - getUserId(): UserId; - roundTripOptionalUserId(input: UserId | null): UserId | null; - setTokenId(token: TokenId): void; - getTokenId(): TokenId; - roundTripOptionalTokenId(input: TokenId | null): TokenId | null; - setSessionId(session: SessionId): void; - getSessionId(): SessionId; - roundTripOptionalSessionId(input: SessionId | null): SessionId | null; - setPrecision(precision: Precision): void; - getPrecision(): Precision; - roundTripOptionalPrecision(input: Precision | null): Precision | null; - setRatio(ratio: Ratio): void; - getRatio(): Ratio; - roundTripOptionalRatio(input: Ratio | null): Ratio | null; - processTheme(theme: Theme): HttpStatus; - convertPriority(status: HttpStatus): Priority; - validateSession(session: SessionId): Theme; + setTheme(theme: ThemeTag): void; + getTheme(): ThemeTag; + roundTripOptionalTheme(input: ThemeTag | null): ThemeTag | null; + setTSTheme(theme: TSThemeTag): void; + getTSTheme(): TSThemeTag; + roundTripOptionalTSTheme(input: TSThemeTag | null): TSThemeTag | null; + setFeatureFlag(flag: FeatureFlagTag): void; + getFeatureFlag(): FeatureFlagTag; + roundTripOptionalFeatureFlag(input: FeatureFlagTag | null): FeatureFlagTag | null; + setHttpStatus(status: HttpStatusTag): void; + getHttpStatus(): HttpStatusTag; + roundTripOptionalHttpStatus(input: HttpStatusTag | null): HttpStatusTag | null; + setTSHttpStatus(status: TSHttpStatusTag): void; + getTSHttpStatus(): TSHttpStatusTag; + roundTripOptionalHttpStatus(input: TSHttpStatusTag | null): TSHttpStatusTag | null; + setPriority(priority: PriorityTag): void; + getPriority(): PriorityTag; + roundTripOptionalPriority(input: PriorityTag | null): PriorityTag | null; + setFileSize(size: FileSizeTag): void; + getFileSize(): FileSizeTag; + roundTripOptionalFileSize(input: FileSizeTag | null): FileSizeTag | null; + setUserId(id: UserIdTag): void; + getUserId(): UserIdTag; + roundTripOptionalUserId(input: UserIdTag | null): UserIdTag | null; + setTokenId(token: TokenIdTag): void; + getTokenId(): TokenIdTag; + roundTripOptionalTokenId(input: TokenIdTag | null): TokenIdTag | null; + setSessionId(session: SessionIdTag): void; + getSessionId(): SessionIdTag; + roundTripOptionalSessionId(input: SessionIdTag | null): SessionIdTag | null; + setPrecision(precision: PrecisionTag): void; + getPrecision(): PrecisionTag; + roundTripOptionalPrecision(input: PrecisionTag | null): PrecisionTag | null; + setRatio(ratio: RatioTag): void; + getRatio(): RatioTag; + roundTripOptionalRatio(input: RatioTag | null): RatioTag | null; + processTheme(theme: ThemeTag): HttpStatusTag; + convertPriority(status: HttpStatusTag): PriorityTag; + validateSession(session: SessionIdTag): ThemeTag; + Theme: ThemeObject + FeatureFlag: FeatureFlagObject + HttpStatus: HttpStatusObject + Priority: PriorityObject + FileSize: FileSizeObject + UserId: UserIdObject + TokenId: TokenIdObject + SessionId: SessionIdObject + Precision: PrecisionObject + Ratio: RatioObject } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js index fe1b4258..3c7239b7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/EnumRawType.Export.js @@ -4,7 +4,7 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. -export const Theme = { +export const ThemeValues = { Light: "light", Dark: "dark", Auto: "auto", @@ -16,12 +16,12 @@ export const TSTheme = { Auto: "auto", }; -export const FeatureFlag = { +export const FeatureFlagValues = { Enabled: true, Disabled: false, }; -export const HttpStatus = { +export const HttpStatusValues = { Ok: 200, NotFound: 404, ServerError: 500, @@ -33,7 +33,7 @@ export const TSHttpStatus = { ServerError: 500, }; -export const Priority = { +export const PriorityValues = { Lowest: 1, Low: 2, Medium: 3, @@ -41,38 +41,38 @@ export const Priority = { Highest: 5, }; -export const FileSize = { +export const FileSizeValues = { Tiny: 1024, Small: 10240, Medium: 102400, Large: 1048576, }; -export const UserId = { +export const UserIdValues = { Guest: 0, User: 1000, Admin: 9999, }; -export const TokenId = { +export const TokenIdValues = { Invalid: 0, Session: 12345, Refresh: 67890, }; -export const SessionId = { +export const SessionIdValues = { None: 0, Active: 9876543210, Expired: 1234567890, }; -export const Precision = { +export const PrecisionValues = { Rough: 0.1, Normal: 0.01, Fine: 0.001, }; -export const Ratio = { +export const RatioValues = { Quarter: 0.25, Half: 0.5, Golden: 1.618, @@ -437,6 +437,16 @@ export async function createInstantiator(options, swift) { tmpRetString = undefined; return ret; }, + Theme: ThemeValues, + FeatureFlag: FeatureFlagValues, + HttpStatus: HttpStatusValues, + Priority: PriorityValues, + FileSize: FileSizeValues, + UserId: UserIdValues, + TokenId: TokenIdValues, + SessionId: SessionIdValues, + Precision: PrecisionValues, + Ratio: RatioValues, }; }, } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.d.ts index b8d8b719..9622891a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.d.ts @@ -4,21 +4,29 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. -export const Calculator: { +export const CalculatorValues: { readonly Scientific: 0; readonly Basic: 1; }; -export type CalculatorTag = typeof Calculator[keyof typeof Calculator]; +export type CalculatorTag = typeof CalculatorValues[keyof typeof CalculatorValues]; -export const APIResult: { +export const APIResultValues: { readonly Tag: { readonly Success: 0; readonly Failure: 1; }; }; -export type APIResult = - { tag: typeof APIResult.Tag.Success; param0: string } | { tag: typeof APIResult.Tag.Failure; param0: number } +export type APIResultTag = + { tag: typeof APIResultValues.Tag.Success; param0: string } | { tag: typeof APIResultValues.Tag.Failure; param0: number } + +export type CalculatorObject = typeof CalculatorValues & { + square(value: number): number; +}; + +export type APIResultObject = typeof APIResultValues & { + roundtrip(value: APIResultTag): APIResultTag; +}; export {}; @@ -46,12 +54,8 @@ export type Exports = { subtract(a: number, b: number): number; add(a: number, b: number): number; } - Calculator: { - square(value: number): number; - } - APIResult: { - roundtrip(value: APIResult): APIResult; - } + Calculator: CalculatorObject + APIResult: APIResultObject } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js index 0a2ff179..44ad0a93 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticFunctions.Export.js @@ -4,24 +4,24 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. -export const Calculator = { +export const CalculatorValues = { Scientific: 0, Basic: 1, }; -export const APIResult = { +export const APIResultValues = { Tag: { Success: 0, Failure: 1, }, }; -const __bjs_createAPIResultHelpers = () => { +const __bjs_createAPIResultValuesHelpers = () => { return (tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift) => ({ lower: (value) => { const enumTag = value.tag; switch (enumTag) { - case APIResult.Tag.Success: { + case APIResultValues.Tag.Success: { const bytes = textEncoder.encode(value.param0); const id = swift.memory.retain(bytes); tmpParamInts.push(bytes.length); @@ -29,28 +29,28 @@ const __bjs_createAPIResultHelpers = () => { const cleanup = () => { swift.memory.release(id); }; - return { caseId: APIResult.Tag.Success, cleanup }; + return { caseId: APIResultValues.Tag.Success, cleanup }; } - case APIResult.Tag.Failure: { + case APIResultValues.Tag.Failure: { tmpParamInts.push((value.param0 | 0)); const cleanup = undefined; - return { caseId: APIResult.Tag.Failure, cleanup }; + return { caseId: APIResultValues.Tag.Failure, cleanup }; } - default: throw new Error("Unknown APIResult tag: " + String(enumTag)); + default: throw new Error("Unknown APIResultValues tag: " + String(enumTag)); } }, raise: (tmpRetTag, tmpRetStrings, tmpRetInts, tmpRetF32s, tmpRetF64s) => { const tag = tmpRetTag | 0; switch (tag) { - case APIResult.Tag.Success: { + case APIResultValues.Tag.Success: { const string = tmpRetStrings.pop(); - return { tag: APIResult.Tag.Success, param0: string }; + return { tag: APIResultValues.Tag.Success, param0: string }; } - case APIResult.Tag.Failure: { + case APIResultValues.Tag.Failure: { const int = tmpRetInts.pop(); - return { tag: APIResult.Tag.Failure, param0: int }; + return { tag: APIResultValues.Tag.Failure, param0: int }; } - default: throw new Error("Unknown APIResult tag returned from Swift: " + String(tag)); + default: throw new Error("Unknown APIResultValues tag returned from Swift: " + String(tag)); } } }); @@ -203,7 +203,7 @@ export async function createInstantiator(options, swift) { instance = i; memory = instance.exports.memory; - const APIResultHelpers = __bjs_createAPIResultHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); + const APIResultHelpers = __bjs_createAPIResultValuesHelpers()(tmpParamInts, tmpParamF32s, tmpParamF64s, textEncoder, swift); enumHelpers.APIResult = APIResultHelpers; setException = (error) => { @@ -272,12 +272,14 @@ export async function createInstantiator(options, swift) { return ret; }, Calculator: { + ...CalculatorValues, square: function(value) { const ret = instance.exports.bjs_Calculator_static_square(value); return ret; } }, APIResult: { + ...APIResultValues, roundtrip: function(value) { const { caseId: valueCaseId, cleanup: valueCleanup } = enumHelpers.APIResult.lower(value); instance.exports.bjs_APIResult_static_roundtrip(valueCaseId); diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.d.ts index 5c9ae065..40e28718 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.d.ts @@ -4,11 +4,17 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. -export const PropertyEnum: { +export const PropertyEnumValues: { readonly Value1: 0; readonly Value2: 1; }; -export type PropertyEnumTag = typeof PropertyEnum[keyof typeof PropertyEnum]; +export type PropertyEnumTag = typeof PropertyEnumValues[keyof typeof PropertyEnumValues]; + +export type PropertyEnumObject = typeof PropertyEnumValues & { + enumProperty: string; + readonly enumConstant: number; + computedEnum: string; +}; export {}; @@ -44,11 +50,7 @@ export type Exports = { readonly readOnlyComputed: number; optionalProperty: string | null; } - PropertyEnum: { - enumProperty: string; - readonly enumConstant: number; - computedEnum: string; - } + PropertyEnum: PropertyEnumObject } export type Imports = { } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js index 86cce7e6..eb1c5ad1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StaticProperties.Export.js @@ -4,7 +4,7 @@ // To update this file, just rebuild your project or run // `swift package bridge-js`. -export const PropertyEnum = { +export const PropertyEnumValues = { Value1: 0, Value2: 1, }; @@ -298,6 +298,7 @@ export async function createInstantiator(options, swift) { return { PropertyClass, PropertyEnum: { + ...PropertyEnumValues, get enumProperty() { instance.exports.bjs_PropertyEnum_static_enumProperty_get(); const ret = tmpRetString; diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Enum.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Enum.md index 9c0f9971..93c1fdd2 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Enum.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Enum.md @@ -13,6 +13,12 @@ BridgeJS supports two output styles for enums, controlled by the `enumStyle` par Examples output of both styles can be found below. +BridgeJS generates separate objects with descriptive naming for `.const` enums: + +- **`EnumNameValues`**: Contains the enum case constants for all enums +- **`EnumNameTag`**: Represents the union type for enums +- **`EnumNameObject`**: Object type for all const-style enums, contains static members for enums with methods/properties or references the values type for simple enums + #### Case Enums **Swift Definition:** @@ -43,50 +49,50 @@ Examples output of both styles can be found below. ```typescript // Const object style (default) -const Direction: { +export const DirectionValues: { readonly North: 0; readonly South: 1; readonly East: 2; readonly West: 3; }; -type Direction = typeof Direction[keyof typeof Direction]; +export type DirectionTag = typeof DirectionValues[keyof typeof DirectionValues]; // Native TypeScript enum style -enum TSDirection { +export enum TSDirection { North = 0, South = 1, East = 2, West = 3, } -const Status: { +export const StatusValues: { readonly Loading: 0; readonly Success: 1; readonly Error: 2; }; -type Status = typeof Status[keyof typeof Status]; +export type StatusTag = typeof StatusValues[keyof typeof StatusValues]; ``` **Usage in TypeScript:** ```typescript -const direction: Direction = exports.Direction.North; -const tsDirection: TSDirection = exports.TSDirection.North; -const status: Status = exports.Status.Loading; +const direction: DirectionTag = DirectionValues.North; +const tsDirection: TSDirection = TSDirection.North; +const status: StatusTag = StatusValues.Loading; -exports.setDirection(exports.Direction.South); -exports.setTSDirection(exports.TSDirection.East); -const currentDirection: Direction = exports.getDirection(); +exports.setDirection(DirectionValues.South); +exports.setTSDirection(TSDirection.East); +const currentDirection: DirectionTag = exports.getDirection(); const currentTSDirection: TSDirection = exports.getTSDirection(); -const result: Status = exports.processDirection(exports.Direction.East); +const result: StatusTag = exports.processDirection(DirectionValues.East); -function handleDirection(direction: Direction) { +function handleDirection(direction: DirectionTag) { switch (direction) { - case exports.Direction.North: + case DirectionValues.North: console.log("Going north"); break; - case exports.Direction.South: + case DirectionValues.South: console.log("Going south"); break; // TypeScript will warn about missing cases @@ -175,15 +181,15 @@ public func _bjs_getDirection() -> Int32 { ```typescript // Const object style (default) -const Theme: { +export const ThemeValues: { readonly Light: "light"; readonly Dark: "dark"; readonly Auto: "auto"; }; -type Theme = typeof Theme[keyof typeof Theme]; +export type ThemeTag = typeof ThemeValues[keyof typeof ThemeValues]; // Native TypeScript enum style -enum TSTheme { +export enum TSTheme { Light = "light", Dark = "dark", Auto = "auto", @@ -193,14 +199,14 @@ enum TSTheme { **Usage in TypeScript:** ```typescript -// Both styles work similarly in usage -const theme: Theme = exports.Theme.Dark; -const tsTheme: TSTheme = exports.TSTheme.Dark; +// Raw value enums work similarly to case enums +const theme: ThemeTag = ThemeValues.Dark; +const tsTheme: TSTheme = TSTheme.Dark; -exports.setTheme(exports.Theme.Light); -const currentTheme: Theme = exports.getTheme(); +exports.setTheme(ThemeValues.Light); +const currentTheme: ThemeTag = exports.getTheme(); -const status: HttpStatus = exports.processTheme(exports.Theme.Auto); +const status: HttpStatusTag = exports.processTheme(ThemeValues.Auto); ``` ##### Integer Raw Values @@ -235,41 +241,41 @@ const status: HttpStatus = exports.processTheme(exports.Theme.Auto); ```typescript // Const object style (default) -const HttpStatus: { +export const HttpStatusValues: { readonly Ok: 200; readonly NotFound: 404; readonly ServerError: 500; }; -type HttpStatus = typeof HttpStatus[keyof typeof HttpStatus]; +export type HttpStatusTag = typeof HttpStatusValues[keyof typeof HttpStatusValues]; // Native TypeScript enum style -enum TSHttpStatus { +export enum TSHttpStatus { Ok = 200, NotFound = 404, ServerError = 500, } -const Priority: { +export const PriorityValues: { readonly Lowest: 1; readonly Low: 2; readonly Medium: 3; readonly High: 4; readonly Highest: 5; }; -type Priority = typeof Priority[keyof typeof Priority]; +export type PriorityTag = typeof PriorityValues[keyof typeof PriorityValues]; ``` **Usage in TypeScript:** ```typescript -const status: HttpStatus = exports.HttpStatus.Ok; -const tsStatus: TSHttpStatus = exports.TSHttpStatus.Ok; -const priority: Priority = exports.Priority.High; +const status: HttpStatusTag = HttpStatusValues.Ok; +const tsStatus: TSHttpStatus = TSHttpStatus.Ok; +const priority: PriorityTag = PriorityValues.High; -exports.setHttpStatus(exports.HttpStatus.NotFound); -exports.setPriority(exports.Priority.Medium); +exports.setHttpStatus(HttpStatusValues.NotFound); +exports.setPriority(PriorityValues.Medium); -const convertedPriority: Priority = exports.convertPriority(exports.HttpStatus.Ok); +const convertedPriority: PriorityTag = exports.convertPriority(HttpStatusValues.Ok); ``` ### Namespace Enums @@ -333,25 +339,25 @@ declare global { namespace API { class HTTPServer { constructor(); - call(method: Networking.API.Method): void; + call(method: Networking.API.MethodTag): void; } - const Method: { + const MethodValues: { readonly Get: 0; readonly Post: 1; }; - type Method = typeof Method[keyof typeof Method]; + type MethodTag = typeof MethodValues[keyof typeof MethodValues]; } namespace APIV2 { namespace Internal { class TestServer { constructor(); - call(method: Internal.SupportedMethod): void; + call(method: Internal.SupportedMethodTag): void; } - const SupportedMethod: { + const SupportedMethodValues: { readonly Get: 0; readonly Post: 1; }; - type SupportedMethod = typeof SupportedMethod[keyof typeof SupportedMethod]; + type SupportedMethodTag = typeof SupportedMethodValues[keyof typeof SupportedMethodValues]; } } } @@ -361,16 +367,16 @@ declare global { **Usage in TypeScript:** ```typescript -// Access nested classes through namespaces -const converter = new globalThis.Utils.Converter(); +// Access nested classes through namespaces (no globalThis prefix needed) +const converter = new Utils.Converter(); const result: string = converter.toString(42) -const server = new globalThis.Networking.API.HTTPServer(); -const method: Networking.API.Method = globalThis.Networking.API.Method.Get; +const server = new Networking.API.HTTPServer(); +const method: Networking.API.MethodTag = Networking.API.MethodValues.Get; server.call(method) -const testServer = new globalThis.Networking.APIV2.Internal.TestServer(); -const supportedMethod: Internal.SupportedMethod = globalThis.Networking.APIV2.Internal.SupportedMethod.Post; +const testServer = new Networking.APIV2.Internal.TestServer(); +const supportedMethod: Internal.SupportedMethodTag = Networking.APIV2.Internal.SupportedMethodValues.Post; testServer.call(supportedMethod); ``` @@ -437,7 +443,7 @@ enum ComplexResult { **Generated TypeScript Declaration:** ```typescript -export const APIResult: { +export const APIResultValues: { readonly Tag: { readonly Success: 0; readonly Failure: 1; @@ -448,15 +454,15 @@ export const APIResult: { }; }; -export type APIResult = - { tag: typeof APIResult.Tag.Success; param0: string } | - { tag: typeof APIResult.Tag.Failure; param0: number } | - { tag: typeof APIResult.Tag.Flag; param0: boolean } | - { tag: typeof APIResult.Tag.Rate; param0: number } | - { tag: typeof APIResult.Tag.Precise; param0: number } | - { tag: typeof APIResult.Tag.Info } +export type APIResultTag = + { tag: typeof APIResultValues.Tag.Success; param0: string } | + { tag: typeof APIResultValues.Tag.Failure; param0: number } | + { tag: typeof APIResultValues.Tag.Flag; param0: boolean } | + { tag: typeof APIResultValues.Tag.Rate; param0: number } | + { tag: typeof APIResultValues.Tag.Precise; param0: number } | + { tag: typeof APIResultValues.Tag.Info } -export const ComplexResult: { +export const ComplexResultValues: { readonly Tag: { readonly Success: 0; readonly Error: 1; @@ -467,31 +473,31 @@ export const ComplexResult: { }; }; -export type ComplexResult = - { tag: typeof ComplexResult.Tag.Success; param0: string } | - { tag: typeof ComplexResult.Tag.Error; param0: string; param1: number } | - { tag: typeof ComplexResult.Tag.Status; param0: boolean; param1: number; param2: string } | - { tag: typeof ComplexResult.Tag.Coordinates; param0: number; param1: number; param2: number } | - { tag: typeof ComplexResult.Tag.Comprehensive; param0: boolean; param1: boolean; param2: number; param3: number; param4: number; param5: number; param6: string; param7: string; param8: string } | - { tag: typeof ComplexResult.Tag.Info } +export type ComplexResultTag = + { tag: typeof ComplexResultValues.Tag.Success; param0: string } | + { tag: typeof ComplexResultValues.Tag.Error; param0: string; param1: number } | + { tag: typeof ComplexResultValues.Tag.Status; param0: boolean; param1: number; param2: string } | + { tag: typeof ComplexResultValues.Tag.Coordinates; param0: number; param1: number; param2: number } | + { tag: typeof ComplexResultValues.Tag.Comprehensive; param0: boolean; param1: boolean; param2: number; param3: number; param4: number; param5: number; param6: string; param7: string; param8: string } | + { tag: typeof ComplexResultValues.Tag.Info } ``` **Usage in TypeScript:** ```typescript -const successResult: APIResult = { - tag: exports.APIResult.Tag.Success, - param0: "Operation completed successfully" +const successResult: APIResultTag = { + tag: APIResultValues.Tag.Success, + param0: "Operation completed successfully" }; -const errorResult: ComplexResult = { - tag: exports.ComplexResult.Tag.Error, +const errorResult: ComplexResultTag = { + tag: ComplexResultValues.Tag.Error, param0: "Network timeout", param1: 503 }; -const statusResult: ComplexResult = { - tag: exports.ComplexResult.Tag.Status, +const statusResult: ComplexResultTag = { + tag: ComplexResultValues.Tag.Status, param0: true, param1: 200, param2: "OK" @@ -500,21 +506,21 @@ const statusResult: ComplexResult = { exports.handle(successResult); exports.handle(errorResult); -const result: APIResult = exports.getResult(); +const result: APIResultTag = exports.getResult(); // Pattern matching with discriminated unions -function processResult(result: APIResult) { +function processResult(result: APIResultTag) { switch (result.tag) { - case exports.APIResult.Tag.Success: + case APIResultValues.Tag.Success: console.log("Success:", result.param0); // TypeScript knows param0 is string break; - case exports.APIResult.Tag.Failure: + case APIResultValues.Tag.Failure: console.log("Failure code:", result.param0); // TypeScript knows param0 is number break; - case exports.APIResult.Tag.Flag: + case APIResultValues.Tag.Flag: console.log("Flag value:", result.param0); // TypeScript knows param0 is boolean break; - case exports.APIResult.Tag.Info: + case APIResultValues.Tag.Info: console.log("Info case has no associated data"); break; // TypeScript will warn about missing cases diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Functions.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Functions.md index 88d93b17..8602d937 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Functions.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Functions.md @@ -32,18 +32,6 @@ Classes can export both `static` and `class` functions: } ``` -JavaScript usage: - -```javascript -// Static methods - no instance needed -const sum = MathUtils.add(5, 3); -const difference = MathUtils.subtract(10, 4); - -// Instance method - requires instance -const utils = new MathUtils(); -const product = utils.multiply(4, 7); -``` - Generated TypeScript definitions: ```typescript @@ -60,6 +48,29 @@ export interface MathUtils extends SwiftHeapObject { } ``` +Usage: + +```typescript +// Static methods +const sum = MathUtils.add(5, 3); +const difference = MathUtils.subtract(10, 4); + +// Instance methods +const utils = new MathUtils(); +const product = utils.multiply(4, 7); +``` + +## Values/Tag/Object Pattern + +For enums with static functions, BridgeJS generates a structured pattern: + +- **Values**: Constants for enum cases (`CalculatorValues: { readonly Scientific: 0; readonly Basic: 1; }`) +- **Tag**: Type alias for enum values (`CalculatorTag = typeof CalculatorValues[keyof typeof CalculatorValues]`) +- **Object**: Intersection type combining Values + methods (`CalculatorObject = typeof CalculatorValues & { methods }`) +- **Exports**: Uses Object type for unified access (`Calculator: CalculatorObject`) + +This allows accessing both enum constants and static functions through a single interface: `exports.Calculator.Scientific` and `exports.Calculator.square(5)`. + ## Enum Static Functions Enums can contain static functions that are exported as properties: @@ -79,44 +90,34 @@ Enums can contain static functions that are exported as properties: } ``` -Generated JavaScript: - -```javascript -export const Calculator = { - Scientific: 0, - Basic: 1, - square: null, - cube: null, -}; - -// Later assigned: -Calculator.square = function(value) { - const ret = instance.exports.bjs_Calculator_static_square(value); - return ret; -} -Calculator.cube = function(value) { - const ret = instance.exports.bjs_Calculator_static_cube(value); - return ret; -} -``` - -JavaScript usage: - -```javascript -const squared = Calculator.square(5); -const cubed = Calculator.cube(3); -``` - Generated TypeScript definitions: ```typescript -export const Calculator: { +export const CalculatorValues: { readonly Scientific: 0; readonly Basic: 1; +}; +export type CalculatorTag = typeof CalculatorValues[keyof typeof CalculatorValues]; + +export type CalculatorObject = typeof CalculatorValues & { square(value: number): number; cube(value: number): number; }; -export type Calculator = typeof Calculator[keyof typeof Calculator]; + +export type Exports = { + Calculator: CalculatorObject +} +``` + +This enables unified access to both enum constants and static functions: + +```typescript +// Access enum constants +const mode: CalculatorTag = exports.Calculator.Scientific; // 0 +const otherMode: CalculatorTag = CalculatorValues.Basic; // 1 + +// Call static functions +const result: number = exports.Calculator.square(5); // 25 ``` ## Namespace Enum Static Functions @@ -133,38 +134,6 @@ Namespace enums organize related utility functions and are assigned to `globalTh } ``` -Generated JavaScript: - -```javascript -// Function exported in exports object -const exports = { - uppercase: function bjs_Utils_String_uppercase(text) { - const textBytes = textEncoder.encode(text); - const textId = swift.memory.retain(textBytes); - instance.exports.bjs_Utils_String_uppercase(textId, textBytes.length); - const ret = tmpRetString; - tmpRetString = undefined; - swift.memory.release(textId); - return ret; - }, -}; - -// Then assigned to globalThis -if (typeof globalThis.Utils === 'undefined') { - globalThis.Utils = {}; -} -if (typeof globalThis.Utils.String === 'undefined') { - globalThis.Utils.String = {}; -} -globalThis.Utils.String.uppercase = exports.uppercase; -``` - -JavaScript usage: - -```javascript -const upper = Utils.String.uppercase("hello"); -``` - Generated TypeScript definitions: ```typescript @@ -177,6 +146,14 @@ declare global { } ``` +Usage: + +```typescript +// Direct access via global namespace (no exports needed) +const upper: string = Utils.String.uppercase("hello"); +const result: string = Utils.String.uppercase("world"); +``` + ## Supported Features | Swift Static Function Feature | Status | diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Properties.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Properties.md index 80b6f580..097f644b 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Properties.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Static-Properties.md @@ -31,17 +31,6 @@ Classes can export both stored and computed static properties: } ``` -JavaScript usage: - -```javascript -console.log(Configuration.version); // "1.0.0" -console.log(Configuration.debugMode); // false -console.log(Configuration.timestamp); // current timestamp - -Configuration.debugMode = true; -Configuration.timestamp = Date.now() / 1000; -``` - Generated TypeScript definitions: ```typescript @@ -61,117 +50,127 @@ export interface Configuration extends SwiftHeapObject { } ``` -## Enum Static Properties +Usage: -Enums can contain static properties that are exported alongside enum cases: +```typescript +console.log(Configuration.version); // "1.0.0" +console.log(Configuration.debugMode); // false +console.log(Configuration.timestamp); // current timestamp -```swift -@JS enum NetworkStatus { - case connected - case disconnected - case connecting - - @JS static var retryCount = 3 - @JS static let maxRetries = 10 -} +Configuration.debugMode = true; +Configuration.timestamp = Date.now() / 1000; ``` -Generated JavaScript: -```javascript -export const NetworkStatus = { - Connected: 0, - Disconnected: 1, - Connecting: 2 -}; +## Values/Tag/Object Pattern -// Properties added via Object.defineProperty -Object.defineProperty(NetworkStatus, 'retryCount', { - get: function() { return wasmCall('bjs_NetworkStatus_static_retryCount_get'); }, - set: function(value) { wasmCall('bjs_NetworkStatus_static_retryCount_set', value); } -}); +For enums with static properties, BridgeJS generates a structured pattern: -Object.defineProperty(NetworkStatus, 'maxRetries', { - get: function() { return wasmCall('bjs_NetworkStatus_static_maxRetries_get'); } -}); -``` +- **Values**: Constants for enum cases (`PropertyEnumValues: { readonly Value1: 0; readonly Value2: 1; }`) +- **Tag**: Type alias for enum values (`PropertyEnumTag = typeof PropertyEnumValues[keyof typeof PropertyEnumValues]`) +- **Object**: Intersection type combining Values + properties (`PropertyEnumObject = typeof PropertyEnumValues & { properties }`) +- **Exports**: Uses Object type for unified access (`PropertyEnum: PropertyEnumObject`) -JavaScript usage: +This allows accessing both enum constants and static properties through a single interface: `exports.PropertyEnum.Value1` and `exports.PropertyEnum.enumProperty`. + +## Enum Static Properties + +Enums can contain static properties that are exported alongside enum cases: -```javascript -console.log(NetworkStatus.retryCount); // 3 -console.log(NetworkStatus.maxRetries); // 10 -NetworkStatus.retryCount = 5; +```swift +@JS enum PropertyEnum { + case value1 + case value2 + + @JS static var enumProperty = "mutable" + @JS static let enumConstant = 42 + @JS static var computedEnum: String { + return "computed value" + } +} ``` Generated TypeScript definitions: ```typescript -export const NetworkStatus: { - readonly Connected: 0; - readonly Disconnected: 1; - readonly Connecting: 2; - retryCount: number; - readonly maxRetries: number; +export const PropertyEnumValues: { + readonly Value1: 0; + readonly Value2: 1; }; -export type NetworkStatus = typeof NetworkStatus[keyof typeof NetworkStatus]; -``` - -## Namespace Enum Static Properties +export type PropertyEnumTag = typeof PropertyEnumValues[keyof typeof PropertyEnumValues]; -Namespace enums organize related static properties and are assigned to `globalThis`: +export type PropertyEnumObject = typeof PropertyEnumValues & { + enumProperty: string; + readonly enumConstant: number; + computedEnum: string; +}; -```swift -@JS enum Config { - @JS enum API { - @JS static var baseURL = "https://api.example.com" - @JS static let version = "v1" - } +export type Exports = { + PropertyEnum: PropertyEnumObject } ``` -Generated JavaScript: +Usage: -```javascript -if (typeof globalThis.Config === 'undefined') { - globalThis.Config = {}; -} -if (typeof globalThis.Config.API === 'undefined') { - globalThis.Config.API = {}; -} +```typescript +const status: PropertyEnumTag = exports.PropertyEnum.Value1; // 0 +const otherStatus: PropertyEnumTag = PropertyEnumValues.Value2; // 1 + +// Access static properties +console.log(exports.PropertyEnum.enumProperty); // "mutable" +console.log(exports.PropertyEnum.enumConstant); // 42 +console.log(exports.PropertyEnum.computedEnum); // "computed value" -Object.defineProperty(globalThis.Config.API, 'baseURL', { - get: function() { return wasmCall('bjs_Config_API_baseURL_get'); }, - set: function(value) { wasmCall('bjs_Config_API_baseURL_set', value); } -}); +// Modify mutable properties +exports.PropertyEnum.enumProperty = "updated"; -Object.defineProperty(globalThis.Config.API, 'version', { - get: function() { return wasmCall('bjs_Config_API_version_get'); } -}); ``` -JavaScript usage: +## Namespace Enum Static Properties -```javascript -console.log(Config.API.baseURL); // "https://api.example.com" -console.log(Config.API.version); // "v1" +Namespace enums organize related static properties and are assigned to `globalThis`: -Config.API.baseURL = "https://staging.api.example.com"; +```swift +@JS enum PropertyNamespace { + @JS enum Nested { + @JS static var nestedConstant = "constant" + @JS static var nestedDouble = 3.14 + @JS static var nestedProperty = 100 + } +} ``` Generated TypeScript definitions: ```typescript declare global { - namespace Config { - namespace API { - baseURL: string; - readonly version: string; + namespace PropertyNamespace { + namespace Nested { + var nestedConstant: string; + let nestedDouble: number; + let nestedProperty: number; } } } ``` +JavaScript usage: + +```typescript +// Direct access via global namespace (no exports needed) +console.log(PropertyNamespace.Nested.nestedConstant); // "constant" +console.log(PropertyNamespace.Nested.nestedDouble); // 3.14 +console.log(PropertyNamespace.Nested.nestedProperty); // 100 + +// Modify mutable properties +PropertyNamespace.Nested.nestedConstant = "updated"; +PropertyNamespace.Nested.nestedProperty = 200; + +// Type-safe access +const constant: string = PropertyNamespace.Nested.nestedConstant; +const value: number = PropertyNamespace.Nested.nestedProperty; +``` + ## Supported Features | Swift Static Property Feature | Status | @@ -186,4 +185,4 @@ declare global { | All property types (primitives, objects, optionals) | ✅ | | Property observers (`willSet`/`didSet`) | ❌ | | Generic static properties | ❌ | -| Protocol static property requirements | ❌ | \ No newline at end of file +| Protocol static property requirements | ❌ | diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index c9ce04c0..6b877d3a 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -1,7 +1,7 @@ // @ts-check import { - Direction, Status, Theme, HttpStatus, TSDirection, TSTheme, APIResult, ComplexResult, APIOptionalResult, StaticCalculator, StaticPropertyEnum + DirectionValues, StatusValues, ThemeValues, HttpStatusValues, TSDirection, TSTheme, APIResultValues, ComplexResultValues, APIOptionalResultValues, StaticCalculatorValues, StaticPropertyEnumValues } from '../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.js'; /** @type {import('../.build/plugins/PackageToJS/outputs/PackageTests/test.d.ts').SetupOptionsFn} */ @@ -380,34 +380,35 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.fail("Expected no error"); } - assert.equal(Direction.North, 0); - assert.equal(Direction.South, 1); - assert.equal(Direction.East, 2); - assert.equal(Direction.West, 3); - assert.equal(Status.Loading, 0); - assert.equal(Status.Success, 1); - assert.equal(Status.Error, 2); - - assert.equal(exports.setDirection(Direction.North), Direction.North); - assert.equal(exports.setDirection(Direction.South), Direction.South); - assert.equal(exports.getDirection(), Direction.North); - assert.equal(exports.processDirection(Direction.North), Status.Success); - assert.equal(exports.processDirection(Direction.East), Status.Loading); - - assert.equal(Theme.Light, "light"); - assert.equal(Theme.Dark, "dark"); - assert.equal(Theme.Auto, "auto"); - assert.equal(HttpStatus.Ok, 200); - assert.equal(HttpStatus.NotFound, 404); - assert.equal(HttpStatus.ServerError, 500); - - assert.equal(exports.setTheme(Theme.Light), Theme.Light); - assert.equal(exports.setTheme(Theme.Dark), Theme.Dark); - assert.equal(exports.getTheme(), Theme.Light); - assert.equal(exports.setHttpStatus(HttpStatus.Ok), HttpStatus.Ok); - assert.equal(exports.getHttpStatus(), HttpStatus.Ok); - assert.equal(exports.processTheme(Theme.Light), HttpStatus.Ok); - assert.equal(exports.processTheme(Theme.Dark), HttpStatus.NotFound); + // Test enums + assert.equal(exports.Direction.North, 0); + assert.equal(exports.Direction.South, 1); + assert.equal(exports.Direction.East, 2); + assert.equal(DirectionValues.West, 3); + assert.equal(exports.Status.Loading, 0); + assert.equal(exports.Status.Success, 1); + assert.equal(StatusValues.Error, 2); + + assert.equal(exports.setDirection(exports.Direction.North), DirectionValues.North); + assert.equal(exports.setDirection(exports.Direction.South), exports.Direction.South); + assert.equal(exports.getDirection(), exports.Direction.North); + assert.equal(exports.processDirection(exports.Direction.North), exports.Status.Success); + assert.equal(exports.processDirection(DirectionValues.East), StatusValues.Loading); + + assert.equal(exports.Theme.Light, "light"); + assert.equal(exports.Theme.Dark, "dark"); + assert.equal(ThemeValues.Auto, "auto"); + assert.equal(exports.HttpStatus.Ok, 200); + assert.equal(exports.HttpStatus.NotFound, 404); + assert.equal(HttpStatusValues.ServerError, 500); + + assert.equal(exports.setTheme(exports.Theme.Light), exports.Theme.Light); + assert.equal(exports.setTheme(exports.Theme.Dark), exports.Theme.Dark); + assert.equal(exports.getTheme(), ThemeValues.Light); + assert.equal(exports.setHttpStatus(exports.HttpStatus.Ok), HttpStatusValues.Ok); + assert.equal(exports.getHttpStatus(), exports.HttpStatus.Ok); + assert.equal(exports.processTheme(exports.Theme.Light), exports.HttpStatus.Ok); + assert.equal(exports.processTheme(exports.Theme.Dark), HttpStatusValues.NotFound); assert.equal(TSDirection.North, 0); assert.equal(TSDirection.South, 1); @@ -422,28 +423,28 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.equal(exports.setTSTheme(TSTheme.Light), TSTheme.Light); assert.equal(exports.getTSTheme(), TSTheme.Light); - assert.equal(globalThis.Networking.API.Method.Get, 0); - assert.equal(globalThis.Networking.API.Method.Post, 1); - assert.equal(globalThis.Networking.API.Method.Put, 2); - assert.equal(globalThis.Networking.API.Method.Delete, 3); - assert.equal(globalThis.Configuration.LogLevel.Debug, "debug"); - assert.equal(globalThis.Configuration.LogLevel.Info, "info"); - assert.equal(globalThis.Configuration.LogLevel.Warning, "warning"); - assert.equal(globalThis.Configuration.LogLevel.Error, "error"); - assert.equal(globalThis.Configuration.Port.Http, 80); - assert.equal(globalThis.Configuration.Port.Https, 443); - assert.equal(globalThis.Configuration.Port.Development, 3000); - assert.equal(globalThis.Networking.APIV2.Internal.SupportedMethod.Get, 0); - assert.equal(globalThis.Networking.APIV2.Internal.SupportedMethod.Post, 1); - - assert.equal(exports.roundtripNetworkingAPIMethod(globalThis.Networking.API.Method.Get), globalThis.Networking.API.Method.Get); - assert.equal(exports.roundtripConfigurationLogLevel(globalThis.Configuration.LogLevel.Debug), globalThis.Configuration.LogLevel.Debug); - assert.equal(exports.roundtripConfigurationPort(globalThis.Configuration.Port.Http), globalThis.Configuration.Port.Http); - assert.equal(exports.processConfigurationLogLevel(globalThis.Configuration.LogLevel.Debug), globalThis.Configuration.Port.Development); - assert.equal(exports.processConfigurationLogLevel(globalThis.Configuration.LogLevel.Info), globalThis.Configuration.Port.Http); - assert.equal(exports.processConfigurationLogLevel(globalThis.Configuration.LogLevel.Warning), globalThis.Configuration.Port.Https); - assert.equal(exports.processConfigurationLogLevel(globalThis.Configuration.LogLevel.Error), globalThis.Configuration.Port.Development); - assert.equal(exports.roundtripInternalSupportedMethod(globalThis.Networking.APIV2.Internal.SupportedMethod.Get), globalThis.Networking.APIV2.Internal.SupportedMethod.Get); + assert.equal(globalThis.Networking.API.MethodValues.Get, 0); + assert.equal(globalThis.Networking.API.MethodValues.Post, 1); + assert.equal(globalThis.Networking.API.MethodValues.Put, 2); + assert.equal(globalThis.Networking.API.MethodValues.Delete, 3); + assert.equal(globalThis.Configuration.LogLevelValues.Debug, "debug"); + assert.equal(globalThis.Configuration.LogLevelValues.Info, "info"); + assert.equal(globalThis.Configuration.LogLevelValues.Warning, "warning"); + assert.equal(globalThis.Configuration.LogLevelValues.Error, "error"); + assert.equal(globalThis.Configuration.PortValues.Http, 80); + assert.equal(globalThis.Configuration.PortValues.Https, 443); + assert.equal(globalThis.Configuration.PortValues.Development, 3000); + assert.equal(globalThis.Networking.APIV2.Internal.SupportedMethodValues.Get, 0); + assert.equal(globalThis.Networking.APIV2.Internal.SupportedMethodValues.Post, 1); + + assert.equal(exports.roundtripNetworkingAPIMethod(globalThis.Networking.API.MethodValues.Get), globalThis.Networking.API.MethodValues.Get); + assert.equal(exports.roundtripConfigurationLogLevel(globalThis.Configuration.LogLevelValues.Debug), globalThis.Configuration.LogLevelValues.Debug); + assert.equal(exports.roundtripConfigurationPort(globalThis.Configuration.PortValues.Http), globalThis.Configuration.PortValues.Http); + assert.equal(exports.processConfigurationLogLevel(globalThis.Configuration.LogLevelValues.Debug), globalThis.Configuration.PortValues.Development); + assert.equal(exports.processConfigurationLogLevel(globalThis.Configuration.LogLevelValues.Info), globalThis.Configuration.PortValues.Http); + assert.equal(exports.processConfigurationLogLevel(globalThis.Configuration.LogLevelValues.Warning), globalThis.Configuration.PortValues.Https); + assert.equal(exports.processConfigurationLogLevel(globalThis.Configuration.LogLevelValues.Error), globalThis.Configuration.PortValues.Development); + assert.equal(exports.roundtripInternalSupportedMethod(globalThis.Networking.APIV2.Internal.SupportedMethodValues.Get), globalThis.Networking.APIV2.Internal.SupportedMethodValues.Get); const converter = new exports.Converter(); assert.equal(converter.toString(42), "42"); @@ -451,13 +452,13 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { converter.release(); const httpServer = new exports.HTTPServer(); - httpServer.call(globalThis.Networking.API.Method.Get); - httpServer.call(globalThis.Networking.API.Method.Post); + httpServer.call(globalThis.Networking.API.MethodValues.Get); + httpServer.call(globalThis.Networking.API.MethodValues.Post); httpServer.release(); const testServer = new exports.TestServer(); - testServer.call(globalThis.Networking.APIV2.Internal.SupportedMethod.Get); - testServer.call(globalThis.Networking.APIV2.Internal.SupportedMethod.Post); + testServer.call(globalThis.Networking.APIV2.Internal.SupportedMethodValues.Get); + testServer.call(globalThis.Networking.APIV2.Internal.SupportedMethodValues.Post); testServer.release(); const globalConverter = new globalThis.Utils.Converter(); @@ -465,48 +466,48 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { globalConverter.release(); const globalHttpServer = new globalThis.Networking.API.HTTPServer(); - globalHttpServer.call(globalThis.Networking.API.Method.Get); + globalHttpServer.call(globalThis.Networking.API.MethodValues.Get); globalHttpServer.release(); const globalTestServer = new globalThis.Networking.APIV2.Internal.TestServer(); - globalTestServer.call(globalThis.Networking.APIV2.Internal.SupportedMethod.Post); + globalTestServer.call(globalThis.Networking.APIV2.Internal.SupportedMethodValues.Post); globalTestServer.release(); - const s1 = { tag: APIResult.Tag.Success, param0: "Cześć 🙋‍♂️" }; - const f1 = { tag: APIResult.Tag.Failure, param0: 42 }; - const i1 = { tag: APIResult.Tag.Info }; + const s1 = { tag: exports.APIResult.Tag.Success, param0: "Cześć 🙋‍♂️" }; + const f1 = { tag: exports.APIResult.Tag.Failure, param0: 42 }; + const i1 = { tag: APIResultValues.Tag.Info }; assert.deepEqual(exports.roundtripAPIResult(s1), s1); assert.deepEqual(exports.roundtripAPIResult(f1), f1); assert.deepEqual(exports.roundtripAPIResult(i1), i1); - assert.deepEqual(exports.makeAPIResultSuccess("Test"), { tag: APIResult.Tag.Success, param0: "Test" }); - assert.deepEqual(exports.makeAPIResultSuccess("ok"), { tag: APIResult.Tag.Success, param0: "ok" }); - assert.deepEqual(exports.makeAPIResultFailure(123), { tag: APIResult.Tag.Failure, param0: 123 }); - assert.deepEqual(exports.makeAPIResultInfo(), { tag: APIResult.Tag.Info }); + assert.deepEqual(exports.makeAPIResultSuccess("Test"), { tag: exports.APIResult.Tag.Success, param0: "Test" }); + assert.deepEqual(exports.makeAPIResultSuccess("ok"), { tag: exports.APIResult.Tag.Success, param0: "ok" }); + assert.deepEqual(exports.makeAPIResultFailure(123), { tag: exports.APIResult.Tag.Failure, param0: 123 }); + assert.deepEqual(exports.makeAPIResultInfo(), { tag: APIResultValues.Tag.Info }); - const bTrue = { tag: APIResult.Tag.Flag, param0: true }; - const bFalse = { tag: APIResult.Tag.Flag, param0: false }; + const bTrue = { tag: exports.APIResult.Tag.Flag, param0: true }; + const bFalse = { tag: exports.APIResult.Tag.Flag, param0: false }; assert.deepEqual(exports.makeAPIResultFlag(true), bTrue); assert.deepEqual(exports.makeAPIResultFlag(false), bFalse); const rVal = 3.25; - const r1 = { tag: APIResult.Tag.Rate, param0: rVal }; + const r1 = { tag: exports.APIResult.Tag.Rate, param0: rVal }; assert.deepEqual(exports.roundtripAPIResult(r1), r1); assert.deepEqual(exports.makeAPIResultRate(rVal), r1); const pVal = 3.141592653589793; - const p1 = { tag: APIResult.Tag.Precise, param0: pVal }; + const p1 = { tag: APIResultValues.Tag.Precise, param0: pVal }; assert.deepEqual(exports.roundtripAPIResult(p1), p1); assert.deepEqual(exports.makeAPIResultPrecise(pVal), p1); - const cs1 = { tag: ComplexResult.Tag.Success, param0: "All good!" }; - const ce1 = { tag: ComplexResult.Tag.Error, param0: "Network error", param1: 503 }; - const cl1 = { tag: ComplexResult.Tag.Location, param0: 37.7749, param1: -122.4194, param2: "San Francisco" }; - const cst1 = { tag: ComplexResult.Tag.Status, param0: true, param1: 200, param2: "OK" }; - const cc1 = { tag: ComplexResult.Tag.Coordinates, param0: 10.5, param1: 20.3, param2: 30.7 }; - const ccomp1 = { tag: ComplexResult.Tag.Comprehensive, param0: true, param1: false, param2: 42, param3: 100, param4: 3.14, param5: 2.718, param6: "Hello", param7: "World", param8: "Test" }; - const ci1 = { tag: ComplexResult.Tag.Info }; + const cs1 = { tag: exports.ComplexResult.Tag.Success, param0: "All good!" }; + const ce1 = { tag: exports.ComplexResult.Tag.Error, param0: "Network error", param1: 503 }; + const cl1 = { tag: exports.ComplexResult.Tag.Location, param0: 37.7749, param1: -122.4194, param2: "San Francisco" }; + const cst1 = { tag: exports.ComplexResult.Tag.Status, param0: true, param1: 200, param2: "OK" }; + const cc1 = { tag: exports.ComplexResult.Tag.Coordinates, param0: 10.5, param1: 20.3, param2: 30.7 }; + const ccomp1 = { tag: ComplexResultValues.Tag.Comprehensive, param0: true, param1: false, param2: 42, param3: 100, param4: 3.14, param5: 2.718, param6: "Hello", param7: "World", param8: "Test" }; + const ci1 = { tag: ComplexResultValues.Tag.Info }; assert.deepEqual(exports.roundtripComplexResult(cs1), cs1); assert.deepEqual(exports.roundtripComplexResult(ce1), ce1); @@ -516,35 +517,35 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.deepEqual(exports.roundtripComplexResult(ccomp1), ccomp1); assert.deepEqual(exports.roundtripComplexResult(ci1), ci1); - assert.deepEqual(exports.makeComplexResultSuccess("Great!"), { tag: ComplexResult.Tag.Success, param0: "Great!" }); - assert.deepEqual(exports.makeComplexResultError("Timeout", 408), { tag: ComplexResult.Tag.Error, param0: "Timeout", param1: 408 }); - assert.deepEqual(exports.makeComplexResultLocation(40.7128, -74.0060, "New York"), { tag: ComplexResult.Tag.Location, param0: 40.7128, param1: -74.0060, param2: "New York" }); - assert.deepEqual(exports.makeComplexResultStatus(false, 500, "Internal Server Error"), { tag: ComplexResult.Tag.Status, param0: false, param1: 500, param2: "Internal Server Error" }); - assert.deepEqual(exports.makeComplexResultCoordinates(1.1, 2.2, 3.3), { tag: ComplexResult.Tag.Coordinates, param0: 1.1, param1: 2.2, param2: 3.3 }); - assert.deepEqual(exports.makeComplexResultComprehensive(true, false, 10, 20, 1.5, 2.5, "First", "Second", "Third"), { tag: ComplexResult.Tag.Comprehensive, param0: true, param1: false, param2: 10, param3: 20, param4: 1.5, param5: 2.5, param6: "First", param7: "Second", param8: "Third" }); - assert.deepEqual(exports.makeComplexResultInfo(), { tag: ComplexResult.Tag.Info }); + assert.deepEqual(exports.makeComplexResultSuccess("Great!"), { tag: exports.ComplexResult.Tag.Success, param0: "Great!" }); + assert.deepEqual(exports.makeComplexResultError("Timeout", 408), { tag: exports.ComplexResult.Tag.Error, param0: "Timeout", param1: 408 }); + assert.deepEqual(exports.makeComplexResultLocation(40.7128, -74.0060, "New York"), { tag: exports.ComplexResult.Tag.Location, param0: 40.7128, param1: -74.0060, param2: "New York" }); + assert.deepEqual(exports.makeComplexResultStatus(false, 500, "Internal Server Error"), { tag: exports.ComplexResult.Tag.Status, param0: false, param1: 500, param2: "Internal Server Error" }); + assert.deepEqual(exports.makeComplexResultCoordinates(1.1, 2.2, 3.3), { tag: exports.ComplexResult.Tag.Coordinates, param0: 1.1, param1: 2.2, param2: 3.3 }); + assert.deepEqual(exports.makeComplexResultComprehensive(true, false, 10, 20, 1.5, 2.5, "First", "Second", "Third"), { tag: exports.ComplexResult.Tag.Comprehensive, param0: true, param1: false, param2: 10, param3: 20, param4: 1.5, param5: 2.5, param6: "First", param7: "Second", param8: "Third" }); + assert.deepEqual(exports.makeComplexResultInfo(), { tag: exports.ComplexResult.Tag.Info }); - const urSuccess = { tag: globalThis.Utilities.Result.Tag.Success, param0: "Utility operation completed" }; - const urFailure = { tag: globalThis.Utilities.Result.Tag.Failure, param0: "Utility error occurred", param1: 500 }; - const urStatus = { tag: globalThis.Utilities.Result.Tag.Status, param0: true, param1: 200, param2: "Utility status OK" }; + const urSuccess = { tag: globalThis.Utilities.ResultValues.Tag.Success, param0: "Utility operation completed" }; + const urFailure = { tag: globalThis.Utilities.ResultValues.Tag.Failure, param0: "Utility error occurred", param1: 500 }; + const urStatus = { tag: globalThis.Utilities.ResultValues.Tag.Status, param0: true, param1: 200, param2: "Utility status OK" }; assert.deepEqual(exports.roundtripUtilitiesResult(urSuccess), urSuccess); assert.deepEqual(exports.roundtripUtilitiesResult(urFailure), urFailure); assert.deepEqual(exports.roundtripUtilitiesResult(urStatus), urStatus); - assert.deepEqual(exports.makeUtilitiesResultSuccess("Test"), { tag: globalThis.Utilities.Result.Tag.Success, param0: "Test" }); - assert.deepEqual(exports.makeUtilitiesResultSuccess("ok"), { tag: globalThis.Utilities.Result.Tag.Success, param0: "ok" }); - assert.deepEqual(exports.makeUtilitiesResultFailure("Error", 123), { tag: globalThis.Utilities.Result.Tag.Failure, param0: "Error", param1: 123 }); - assert.deepEqual(exports.makeUtilitiesResultStatus(true, 200, "OK"), { tag: globalThis.Utilities.Result.Tag.Status, param0: true, param1: 200, param2: "OK" }); + assert.deepEqual(exports.makeUtilitiesResultSuccess("Test"), { tag: globalThis.Utilities.ResultValues.Tag.Success, param0: "Test" }); + assert.deepEqual(exports.makeUtilitiesResultSuccess("ok"), { tag: globalThis.Utilities.ResultValues.Tag.Success, param0: "ok" }); + assert.deepEqual(exports.makeUtilitiesResultFailure("Error", 123), { tag: globalThis.Utilities.ResultValues.Tag.Failure, param0: "Error", param1: 123 }); + assert.deepEqual(exports.makeUtilitiesResultStatus(true, 200, "OK"), { tag: globalThis.Utilities.ResultValues.Tag.Status, param0: true, param1: 200, param2: "OK" }); - const nrSuccess = { tag: globalThis.API.NetworkingResult.Tag.Success, param0: "Network request successful" }; - const nrFailure = { tag: globalThis.API.NetworkingResult.Tag.Failure, param0: "Network timeout", param1: 408 }; + const nrSuccess = { tag: globalThis.API.NetworkingResultValues.Tag.Success, param0: "Network request successful" }; + const nrFailure = { tag: globalThis.API.NetworkingResultValues.Tag.Failure, param0: "Network timeout", param1: 408 }; assert.deepEqual(exports.roundtripAPINetworkingResult(nrSuccess), nrSuccess); assert.deepEqual(exports.roundtripAPINetworkingResult(nrFailure), nrFailure); - assert.deepEqual(exports.makeAPINetworkingResultSuccess("Connected"), { tag: globalThis.API.NetworkingResult.Tag.Success, param0: "Connected" }); - assert.deepEqual(exports.makeAPINetworkingResultFailure("Timeout", 408), { tag: globalThis.API.NetworkingResult.Tag.Failure, param0: "Timeout", param1: 408 }); + assert.deepEqual(exports.makeAPINetworkingResultSuccess("Connected"), { tag: globalThis.API.NetworkingResultValues.Tag.Success, param0: "Connected" }); + assert.deepEqual(exports.makeAPINetworkingResultFailure("Timeout", 408), { tag: globalThis.API.NetworkingResultValues.Tag.Failure, param0: "Timeout", param1: 408 }); assert.equal(exports.roundTripOptionalString(null), null); assert.equal(exports.roundTripOptionalInt(null), null); @@ -568,12 +569,12 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.equal(exports.roundTripOptionalWithSpaces(1.618), 1.618); assert.equal(exports.roundTripOptionalTypeAlias(null), null); assert.equal(exports.roundTripOptionalTypeAlias(25), 25); - assert.equal(exports.roundTripOptionalStatus(Status.Success), Status.Success); - assert.equal(exports.roundTripOptionalTheme(Theme.Light), Theme.Light); - assert.equal(exports.roundTripOptionalHttpStatus(HttpStatus.Ok), HttpStatus.Ok); + assert.equal(exports.roundTripOptionalStatus(exports.Status.Success), StatusValues.Success); + assert.equal(exports.roundTripOptionalTheme(exports.Theme.Light), ThemeValues.Light); + assert.equal(exports.roundTripOptionalHttpStatus(exports.HttpStatus.Ok), HttpStatusValues.Ok); assert.equal(exports.roundTripOptionalTSDirection(TSDirection.North), TSDirection.North); assert.equal(exports.roundTripOptionalTSTheme(TSTheme.Light), TSTheme.Light); - assert.equal(exports.roundTripOptionalNetworkingAPIMethod(globalThis.Networking.API.Method.Get), globalThis.Networking.API.Method.Get); + assert.equal(exports.roundTripOptionalNetworkingAPIMethod(globalThis.Networking.API.MethodValues.Get), globalThis.Networking.API.MethodValues.Get); assert.deepEqual(exports.roundTripOptionalAPIResult(p1), p1); assert.deepEqual(exports.roundTripOptionalComplexResult(cl1), cl1); @@ -611,16 +612,16 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { testPropertyGreeter.release(); optionalsHolder.release(); - const aor1 = { tag: APIOptionalResult.Tag.Success, param0: "hello world" }; - const aor2 = { tag: APIOptionalResult.Tag.Success, param0: null }; - const aor3 = { tag: APIOptionalResult.Tag.Failure, param0: 404, param1: true }; - const aor4 = { tag: APIOptionalResult.Tag.Failure, param0: 404, param1: null }; - const aor5 = { tag: APIOptionalResult.Tag.Failure, param0: null, param1: null }; - const aor6 = { tag: APIOptionalResult.Tag.Status, param0: true, param1: 200, param2: "OK" }; - const aor7 = { tag: APIOptionalResult.Tag.Status, param0: true, param1: null, param2: "Partial" }; - const aor8 = { tag: APIOptionalResult.Tag.Status, param0: null, param1: null, param2: "Zero" }; - const aor9 = { tag: APIOptionalResult.Tag.Status, param0: false, param1: 500, param2: null }; - const aor10 = { tag: APIOptionalResult.Tag.Status, param0: null, param1: 0, param2: "Zero" }; + const aor1 = { tag: APIOptionalResultValues.Tag.Success, param0: "hello world" }; + const aor2 = { tag: APIOptionalResultValues.Tag.Success, param0: null }; + const aor3 = { tag: APIOptionalResultValues.Tag.Failure, param0: 404, param1: true }; + const aor4 = { tag: APIOptionalResultValues.Tag.Failure, param0: 404, param1: null }; + const aor5 = { tag: APIOptionalResultValues.Tag.Failure, param0: null, param1: null }; + const aor6 = { tag: APIOptionalResultValues.Tag.Status, param0: true, param1: 200, param2: "OK" }; + const aor7 = { tag: APIOptionalResultValues.Tag.Status, param0: true, param1: null, param2: "Partial" }; + const aor8 = { tag: APIOptionalResultValues.Tag.Status, param0: null, param1: null, param2: "Zero" }; + const aor9 = { tag: APIOptionalResultValues.Tag.Status, param0: false, param1: 500, param2: null }; + const aor10 = { tag: APIOptionalResultValues.Tag.Status, param0: null, param1: 0, param2: "Zero" }; assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor1), aor1); assert.deepEqual(exports.roundTripOptionalAPIOptionalResult(aor2), aor2); @@ -636,9 +637,10 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.equal(exports.MathUtils.add(2147483647, 0), 2147483647); assert.equal(exports.StaticCalculator.roundtrip(42), 42); - assert.equal(StaticCalculator.Scientific, 0); - assert.equal(StaticCalculator.Basic, 1); - + assert.equal(StaticCalculatorValues.Scientific, 0); + assert.equal(StaticCalculatorValues.Basic, 1); + assert.equal(StaticCalculatorValues.Scientific, exports.StaticCalculator.Scientific); + assert.equal(StaticCalculatorValues.Basic, exports.StaticCalculator.Basic); assert.equal(globalThis.StaticUtils.Nested.roundtrip("hello world"), "hello world"); }