From 8cedac70eac91bc6c2197984cec783dc8a7a5688 Mon Sep 17 00:00:00 2001 From: Yuta Saito Date: Thu, 21 Aug 2025 18:47:21 +0900 Subject: [PATCH] BridgeJS: Add property support Add support for Swift properties in BridgeJS with proper readonly detection. - Support stored, lazy, computed, and observed properties - Proper readonly property detection - Generate correct JavaScript property descriptors - Add comprehensive runtime and snapshot tests --- .../JavaScript/BridgeJS.ExportSwift.json | 6 + .../Sources/BridgeJSCore/ExportSwift.swift | 212 +++++--- .../Sources/BridgeJSLink/BridgeJSLink.swift | 57 ++- .../BridgeJSSkeleton/BridgeJSSkeleton.swift | 23 + .../Inputs/PropertyTypes.swift | 97 ++++ .../BridgeJSToolTests/Inputs/SwiftClass.swift | 2 +- .../PropertyTypes.Export.d.ts | 48 ++ .../BridgeJSLinkTests/PropertyTypes.Export.js | 242 +++++++++ .../BridgeJSLinkTests/SwiftClass.Export.d.ts | 1 + .../BridgeJSLinkTests/SwiftClass.Export.js | 12 + .../ExportSwiftTests/EnumNamespace.json | 9 + .../ExportSwiftTests/Namespaces.json | 9 + .../ExportSwiftTests/PropertyTypes.json | 334 ++++++++++++ .../ExportSwiftTests/PropertyTypes.swift | 373 ++++++++++++++ .../ExportSwiftTests/SwiftClass.json | 11 + .../ExportSwiftTests/SwiftClass.swift | 27 + .../BridgeJSRuntimeTests/ExportAPITests.swift | 144 +++++- .../Generated/BridgeJS.ExportSwift.swift | 475 ++++++++++++++++++ .../JavaScript/BridgeJS.ExportSwift.json | 433 ++++++++++++++++ Tests/prelude.mjs | 134 ++++- 20 files changed, 2570 insertions(+), 79 deletions(-) create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PropertyTypes.swift create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.d.ts create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.js create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.json create mode 100644 Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.swift diff --git a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json index 2b5ce07d..625556f2 100644 --- a/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Examples/PlayBridgeJS/Sources/PlayBridgeJS/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -47,6 +47,9 @@ } ], "name" : "PlayBridgeJS", + "properties" : [ + + ], "swiftCallName" : "PlayBridgeJS" }, { @@ -117,6 +120,9 @@ } ], "name" : "PlayBridgeJSOutput", + "properties" : [ + + ], "swiftCallName" : "PlayBridgeJSOutput" } ], diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index a5f2e108..56e01290 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -332,6 +332,75 @@ public class ExportSwift { return .skipChildren } + override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind { + guard node.attributes.hasJSAttribute() else { return .skipChildren } + guard case .classBody(let className, let classKey) = state else { + diagnose(node: node, message: "@JS var must be inside a @JS class") + 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 { + diagnose(node: binding.pattern, message: "Complex patterns not supported for @JS properties") + continue + } + + let propertyName = pattern.identifier.text + + guard let typeAnnotation = binding.typeAnnotation else { + diagnose(node: binding, message: "@JS property must have explicit type annotation") + continue + } + + guard let propertyType = self.parent.lookupType(for: typeAnnotation.type) else { + diagnoseUnsupportedType(node: typeAnnotation.type, type: typeAnnotation.type.trimmedDescription) + continue + } + + // Check if property is readonly + let isLet = node.bindingSpecifier.tokenKind == .keyword(.let) + let isGetterOnly = node.bindings.contains(where: { + switch $0.accessorBlock?.accessors { + case .accessors(let accessors): + // Has accessors - check if it only has a getter (no setter, willSet, or didSet) + return !accessors.contains(where: { accessor in + let tokenKind = accessor.accessorSpecifier.tokenKind + return tokenKind == .keyword(.set) || tokenKind == .keyword(.willSet) + || tokenKind == .keyword(.didSet) + }) + case .getter: + // Has only a getter block + return true + case nil: + // No accessor block - this is a stored property, not readonly + return false + } + }) + let isReadonly = isLet || isGetterOnly + + let exportedProperty = ExportedProperty( + name: propertyName, + type: propertyType, + isReadonly: isReadonly + ) + + exportedClassByName[classKey]?.properties.append(exportedProperty) + } + + return .skipChildren + } + override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind { let name = node.name.text @@ -359,6 +428,7 @@ public class ExportSwift { swiftCallName: swiftCallName, constructor: nil, methods: [], + properties: [], namespace: effectiveNamespace ) let uniqueKey = classKey(name: name, namespace: effectiveNamespace) @@ -689,7 +759,8 @@ public class ExportSwift { class ExportedThunkBuilder { var body: [CodeBlockItemSyntax] = [] - var abiParameterForwardings: [LabeledExprSyntax] = [] + var liftedParameterExprs: [ExprSyntax] = [] + var parameters: [Parameter] = [] var abiParameterSignatures: [(name: String, type: WasmCoreType)] = [] var abiReturnType: WasmCoreType? let effects: Effects @@ -708,38 +779,19 @@ public class ExportSwift { } func liftParameter(param: Parameter) { + parameters.append(param) switch param.type { case .bool: - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax("\(raw: param.name) == 1") - ) - ) + liftedParameterExprs.append(ExprSyntax("\(raw: param.name) == 1")) abiParameterSignatures.append((param.name, .i32)) case .int: - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax("\(raw: param.type.swiftType)(\(raw: param.name))") - ) - ) + liftedParameterExprs.append(ExprSyntax("\(raw: param.type.swiftType)(\(raw: param.name))")) abiParameterSignatures.append((param.name, .i32)) case .float: - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax("\(raw: param.name)") - ) - ) + liftedParameterExprs.append(ExprSyntax("\(raw: param.name)")) abiParameterSignatures.append((param.name, .f32)) case .double: - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax("\(raw: param.name)") - ) - ) + liftedParameterExprs.append(ExprSyntax("\(raw: param.name)")) abiParameterSignatures.append((param.name, .f64)) case .string: let bytesLabel = "\(param.name)Bytes" @@ -751,21 +803,11 @@ public class ExportSwift { } """ append(prepare) - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax("\(raw: param.name)") - ) - ) + liftedParameterExprs.append(ExprSyntax("\(raw: param.name)")) abiParameterSignatures.append((bytesLabel, .i32)) abiParameterSignatures.append((lengthLabel, .i32)) case .caseEnum(let enumName): - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax("\(raw: enumName)(bridgeJSRawValue: \(raw: param.name))!") - ) - ) + liftedParameterExprs.append(ExprSyntax("\(raw: enumName)(bridgeJSRawValue: \(raw: param.name))!")) abiParameterSignatures.append((param.name, .i32)) case .rawValueEnum(let enumName, let rawType): if rawType == .string { @@ -778,12 +820,7 @@ public class ExportSwift { } """ append(prepare) - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax("\(raw: enumName)(rawValue: \(raw: param.name))!") - ) - ) + liftedParameterExprs.append(ExprSyntax("\(raw: enumName)(rawValue: \(raw: param.name))!")) abiParameterSignatures.append((bytesLabel, .i32)) abiParameterSignatures.append((lengthLabel, .i32)) } else { @@ -802,12 +839,7 @@ public class ExportSwift { conversionExpr = "\(enumName)(rawValue: \(rawType.rawValue)(\(param.name)))!" } - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax(stringLiteral: conversionExpr) - ) - ) + liftedParameterExprs.append(ExprSyntax(stringLiteral: conversionExpr)) if let wasmType = rawType.wasmCoreType { abiParameterSignatures.append((param.name, wasmType)) } @@ -817,36 +849,35 @@ public class ExportSwift { case .namespaceEnum: break case .jsObject(nil): - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax("JSObject(id: UInt32(bitPattern: \(raw: param.name)))") - ) - ) + liftedParameterExprs.append(ExprSyntax("JSObject(id: UInt32(bitPattern: \(raw: param.name)))")) abiParameterSignatures.append((param.name, .i32)) case .jsObject(let name): - abiParameterForwardings.append( - LabeledExprSyntax( - label: param.label, - expression: ExprSyntax("\(raw: name)(takingThis: UInt32(bitPattern: \(raw: param.name)))") - ) + liftedParameterExprs.append( + ExprSyntax("\(raw: name)(takingThis: UInt32(bitPattern: \(raw: param.name)))") ) abiParameterSignatures.append((param.name, .i32)) case .swiftHeapObject: let objectExpr: ExprSyntax = "Unmanaged<\(raw: param.type.swiftType)>.fromOpaque(\(raw: param.name)).takeUnretainedValue()" - abiParameterForwardings.append( - LabeledExprSyntax(label: param.label, expression: objectExpr) - ) + liftedParameterExprs.append(objectExpr) abiParameterSignatures.append((param.name, .pointer)) case .void: break } } + private func removeFirstLiftedParameter() -> (parameter: Parameter, expr: ExprSyntax) { + let parameter = parameters.removeFirst() + let expr = liftedParameterExprs.removeFirst() + return (parameter, expr) + } + private func renderCallStatement(callee: ExprSyntax, returnType: BridgeType) -> CodeBlockItemSyntax { + let labeledParams = zip(parameters, liftedParameterExprs).map { param, expr in + LabeledExprSyntax(label: param.label, expression: expr) + } var callExpr: ExprSyntax = - "\(raw: callee)(\(raw: abiParameterForwardings.map { $0.description }.joined(separator: ", ")))" + "\(raw: callee)(\(raw: labeledParams.map { $0.description }.joined(separator: ", ")))" if effects.isAsync { callExpr = ExprSyntax( AwaitExprSyntax(awaitKeyword: .keyword(.await).with(\.trailingTrivia, .space), expression: callExpr) @@ -884,14 +915,30 @@ public class ExportSwift { } func callMethod(klassName: String, methodName: String, returnType: BridgeType) { - let _selfParam = self.abiParameterForwardings.removeFirst() + let (_, selfExpr) = removeFirstLiftedParameter() let item = renderCallStatement( - callee: "\(raw: _selfParam).\(raw: methodName)", + callee: "\(raw: selfExpr).\(raw: methodName)", returnType: returnType ) append(item) } + func callPropertyGetter(klassName: String, propertyName: String, returnType: BridgeType) { + let (_, selfExpr) = removeFirstLiftedParameter() + let retMutability = returnType == .string ? "var" : "let" + if returnType == .void { + append("\(raw: selfExpr).\(raw: propertyName)") + } else { + append("\(raw: retMutability) ret = \(raw: selfExpr).\(raw: propertyName)") + } + } + + func callPropertySetter(klassName: String, propertyName: String) { + let (_, selfExpr) = removeFirstLiftedParameter() + let (_, newValueExpr) = removeFirstLiftedParameter() + append("\(raw: selfExpr).\(raw: propertyName) = \(raw: newValueExpr)") + } + func lowerReturnValue(returnType: BridgeType) { if effects.isAsync { // Async functions always return a Promise, which is a JSObject @@ -1157,6 +1204,39 @@ public class ExportSwift { decls.append(builder.render(abiName: method.abiName)) } + // Generate property getters and setters + for property in klass.properties { + // Generate getter + let getterBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) + getterBuilder.liftParameter( + param: Parameter(label: nil, name: "_self", type: .swiftHeapObject(klass.name)) + ) + getterBuilder.callPropertyGetter( + klassName: klass.name, + propertyName: property.name, + returnType: property.type + ) + 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)) + setterBuilder.liftParameter( + param: Parameter(label: nil, name: "_self", type: .swiftHeapObject(klass.name)) + ) + setterBuilder.liftParameter( + param: Parameter(label: "value", name: "value", type: property.type) + ) + setterBuilder.callPropertySetter( + klassName: klass.name, + propertyName: property.name + ) + setterBuilder.lowerReturnValue(returnType: .void) + decls.append(setterBuilder.render(abiName: property.setterAbiName(className: klass.name))) + } + } + do { decls.append( """ diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 046cb92d..2b220e43 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -524,13 +524,12 @@ struct BridgeJSLink { func renderFunction( name: String, parameters: [Parameter], - returnType: BridgeType, returnExpr: String?, - isMethod: Bool + declarationPrefixKeyword: String? ) -> [String] { var funcLines: [String] = [] funcLines.append( - "\(isMethod ? "" : "function ")\(name)(\(parameters.map { $0.name }.joined(separator: ", "))) {" + "\(declarationPrefixKeyword.map { "\($0) "} ?? "")\(name)(\(parameters.map { $0.name }.joined(separator: ", "))) {" ) funcLines.append(contentsOf: bodyLines.map { $0.indent(count: 4) }) funcLines.append(contentsOf: cleanupLines.map { $0.indent(count: 4) }) @@ -688,9 +687,8 @@ struct BridgeJSLink { let funcLines = thunkBuilder.renderFunction( name: function.abiName, parameters: function.parameters, - returnType: function.returnType, returnExpr: returnExpr, - isMethod: false + declarationPrefixKeyword: "function" ) var dtsLines: [String] = [] dtsLines.append( @@ -753,9 +751,8 @@ struct BridgeJSLink { contentsOf: thunkBuilder.renderFunction( name: method.name, parameters: method.parameters, - returnType: method.returnType, returnExpr: returnExpr, - isMethod: true + declarationPrefixKeyword: nil ).map { $0.indent(count: 4) } ) dtsTypeLines.append( @@ -763,6 +760,52 @@ struct BridgeJSLink { .indent(count: 4) ) } + + // 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 = getterThunkBuilder.call( + abiName: property.getterAbiName(className: klass.name), + returnType: property.type + ) + jsLines.append( + contentsOf: getterThunkBuilder.renderFunction( + name: property.name, + parameters: [], + returnExpr: getterReturnExpr, + declarationPrefixKeyword: "get" + ).map { $0.indent(count: 4) } + ) + + // Generate setter if not readonly + if !property.isReadonly { + let setterThunkBuilder = ExportedThunkBuilder(effects: Effects(isAsync: false, isThrows: false)) + setterThunkBuilder.lowerSelf() + setterThunkBuilder.lowerParameter(param: Parameter(label: "value", name: "value", type: property.type)) + _ = setterThunkBuilder.call( + abiName: property.setterAbiName(className: klass.name), + returnType: .void + ) + jsLines.append( + contentsOf: setterThunkBuilder.renderFunction( + name: property.name, + parameters: [.init(label: nil, name: "value", type: property.type)], + returnExpr: nil, + declarationPrefixKeyword: "set" + ).map { $0.indent(count: 4) } + ) + } + + // Add TypeScript property definition + let readonly = property.isReadonly ? "readonly " : "" + dtsTypeLines.append( + "\(readonly)\(property.name): \(property.type.tsType);" + .indent(count: 4) + ) + } + jsLines.append("}") dtsTypeLines.append("}") diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index 0d872160..47083729 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -174,6 +174,7 @@ public struct ExportedClass: Codable { public var swiftCallName: String public var constructor: ExportedConstructor? public var methods: [ExportedFunction] + public var properties: [ExportedProperty] public var namespace: [String]? public init( @@ -181,12 +182,14 @@ public struct ExportedClass: Codable { swiftCallName: String, constructor: ExportedConstructor? = nil, methods: [ExportedFunction], + properties: [ExportedProperty] = [], namespace: [String]? = nil ) { self.name = name self.swiftCallName = swiftCallName self.constructor = constructor self.methods = methods + self.properties = properties self.namespace = namespace } } @@ -205,6 +208,26 @@ public struct ExportedConstructor: Codable { } } +public struct ExportedProperty: Codable { + public var name: String + public var type: BridgeType + public var isReadonly: Bool + + public init(name: String, type: BridgeType, isReadonly: Bool = false) { + self.name = name + self.type = type + self.isReadonly = isReadonly + } + + public func getterAbiName(className: String) -> String { + return "bjs_\(className)_\(name)_get" + } + + public func setterAbiName(className: String) -> String { + return "bjs_\(className)_\(name)_set" + } +} + public struct ExportedSkeleton: Codable { public let moduleName: String public let functions: [ExportedFunction] diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PropertyTypes.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PropertyTypes.swift new file mode 100644 index 00000000..68482501 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PropertyTypes.swift @@ -0,0 +1,97 @@ +@JS class PropertyHolder { + // Primitive properties + @JS var intValue: Int + @JS var floatValue: Float + @JS var doubleValue: Double + @JS var boolValue: Bool + @JS var stringValue: String + + // Readonly primitive properties + @JS let readonlyInt: Int = 42 + @JS let readonlyFloat: Float = 3.14 + @JS let readonlyDouble: Double = 2.718281828 + @JS let readonlyBool: Bool = true + @JS let readonlyString: String = "constant" + + // JSObject property + @JS var jsObject: JSObject + + // SwiftHeapObject property (will be set later) + @JS var sibling: PropertyHolder + + // Lazy stored property - should this be supported or generate an error? + @JS lazy var lazyValue: String = "computed lazily" + + // Computed property with getter only (readonly) + @JS var computedReadonly: Int { + return intValue * 2 + } + + // Computed property with getter and setter + @JS var computedReadWrite: String { + get { + return "Value: \(intValue)" + } + set { + // Parse the number from "Value: X" format + if let range = newValue.range(of: "Value: "), + let number = Int(String(newValue[range.upperBound...])) + { + intValue = number + } + } + } + + // Property with property observers + @JS var observedProperty: Int { + willSet { + print("Will set to \(newValue)") + } + didSet { + print("Did set from \(oldValue)") + } + } + + @JS init( + intValue: Int, + floatValue: Float, + doubleValue: Double, + boolValue: Bool, + stringValue: String, + jsObject: JSObject + ) { + self.intValue = intValue + self.floatValue = floatValue + self.doubleValue = doubleValue + self.boolValue = boolValue + self.stringValue = stringValue + self.jsObject = jsObject + self.sibling = self + } + + @JS func getAllValues() -> String { + return "int:\(intValue),float:\(floatValue),double:\(doubleValue),bool:\(boolValue),string:\(stringValue)" + } +} + +@JS func createPropertyHolder( + intValue: Int, + floatValue: Float, + doubleValue: Double, + boolValue: Bool, + stringValue: String, + jsObject: JSObject +) -> PropertyHolder { + return PropertyHolder( + intValue: intValue, + floatValue: floatValue, + doubleValue: doubleValue, + boolValue: boolValue, + stringValue: stringValue, + jsObject: jsObject + ) +} + +@JS func testPropertyHolder(holder: PropertyHolder) -> String { + return holder.getAllValues() +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/SwiftClass.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/SwiftClass.swift index a803504f..08600d2a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/SwiftClass.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/SwiftClass.swift @@ -1,5 +1,5 @@ @JS class Greeter { - var name: String + @JS var name: String @JS init(name: String) { self.name = name diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.d.ts new file mode 100644 index 00000000..8f65849a --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.d.ts @@ -0,0 +1,48 @@ +// 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`. + +/// 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 PropertyHolder extends SwiftHeapObject { + getAllValues(): string; + intValue: number; + floatValue: number; + doubleValue: number; + boolValue: boolean; + stringValue: string; + readonly readonlyInt: number; + readonly readonlyFloat: number; + readonly readonlyDouble: number; + readonly readonlyBool: boolean; + readonly readonlyString: string; + jsObject: any; + sibling: PropertyHolder; + lazyValue: string; + readonly computedReadonly: number; + computedReadWrite: string; + observedProperty: number; +} +export type Exports = { + PropertyHolder: { + new(intValue: number, floatValue: number, doubleValue: number, boolValue: boolean, stringValue: string, jsObject: any): PropertyHolder; + } + createPropertyHolder(intValue: number, floatValue: number, doubleValue: number, boolValue: boolean, stringValue: string, jsObject: any): PropertyHolder; + testPropertyHolder(holder: PropertyHolder): string; +} +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/PropertyTypes.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.js new file mode 100644 index 00000000..ae72be66 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PropertyTypes.Export.js @@ -0,0 +1,242 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + 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); + } + // Wrapper functions for module: TestModule + if (!importObject["TestModule"]) { + importObject["TestModule"] = {}; + } + importObject["TestModule"]["bjs_PropertyHolder_wrap"] = function(pointer) { + const obj = PropertyHolder.__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 PropertyHolder extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_PropertyHolder_deinit, PropertyHolder.prototype); + } + + + constructor(intValue, floatValue, doubleValue, boolValue, stringValue, jsObject) { + const stringValueBytes = textEncoder.encode(stringValue); + const stringValueId = swift.memory.retain(stringValueBytes); + const ret = instance.exports.bjs_PropertyHolder_init(intValue, floatValue, doubleValue, boolValue, stringValueId, stringValueBytes.length, swift.memory.retain(jsObject)); + swift.memory.release(stringValueId); + return PropertyHolder.__construct(ret); + } + getAllValues() { + instance.exports.bjs_PropertyHolder_getAllValues(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + get intValue() { + const ret = instance.exports.bjs_PropertyHolder_intValue_get(this.pointer); + return ret; + } + set intValue(value) { + instance.exports.bjs_PropertyHolder_intValue_set(this.pointer, value); + } + get floatValue() { + const ret = instance.exports.bjs_PropertyHolder_floatValue_get(this.pointer); + return ret; + } + set floatValue(value) { + instance.exports.bjs_PropertyHolder_floatValue_set(this.pointer, value); + } + get doubleValue() { + const ret = instance.exports.bjs_PropertyHolder_doubleValue_get(this.pointer); + return ret; + } + set doubleValue(value) { + instance.exports.bjs_PropertyHolder_doubleValue_set(this.pointer, value); + } + get boolValue() { + const ret = instance.exports.bjs_PropertyHolder_boolValue_get(this.pointer) !== 0; + return ret; + } + set boolValue(value) { + instance.exports.bjs_PropertyHolder_boolValue_set(this.pointer, value); + } + get stringValue() { + instance.exports.bjs_PropertyHolder_stringValue_get(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + set stringValue(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_PropertyHolder_stringValue_set(this.pointer, valueId, valueBytes.length); + swift.memory.release(valueId); + } + get readonlyInt() { + const ret = instance.exports.bjs_PropertyHolder_readonlyInt_get(this.pointer); + return ret; + } + get readonlyFloat() { + const ret = instance.exports.bjs_PropertyHolder_readonlyFloat_get(this.pointer); + return ret; + } + get readonlyDouble() { + const ret = instance.exports.bjs_PropertyHolder_readonlyDouble_get(this.pointer); + return ret; + } + get readonlyBool() { + const ret = instance.exports.bjs_PropertyHolder_readonlyBool_get(this.pointer) !== 0; + return ret; + } + get readonlyString() { + instance.exports.bjs_PropertyHolder_readonlyString_get(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + get jsObject() { + const retId = instance.exports.bjs_PropertyHolder_jsObject_get(this.pointer); + const ret = swift.memory.getObject(retId); + swift.memory.release(retId); + return ret; + } + set jsObject(value) { + instance.exports.bjs_PropertyHolder_jsObject_set(this.pointer, swift.memory.retain(value)); + } + get sibling() { + const ret = PropertyHolder.__construct(instance.exports.bjs_PropertyHolder_sibling_get(this.pointer)); + return ret; + } + set sibling(value) { + instance.exports.bjs_PropertyHolder_sibling_set(this.pointer, value.pointer); + } + get lazyValue() { + instance.exports.bjs_PropertyHolder_lazyValue_get(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + set lazyValue(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_PropertyHolder_lazyValue_set(this.pointer, valueId, valueBytes.length); + swift.memory.release(valueId); + } + get computedReadonly() { + const ret = instance.exports.bjs_PropertyHolder_computedReadonly_get(this.pointer); + return ret; + } + get computedReadWrite() { + instance.exports.bjs_PropertyHolder_computedReadWrite_get(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + set computedReadWrite(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_PropertyHolder_computedReadWrite_set(this.pointer, valueId, valueBytes.length); + swift.memory.release(valueId); + } + get observedProperty() { + const ret = instance.exports.bjs_PropertyHolder_observedProperty_get(this.pointer); + return ret; + } + set observedProperty(value) { + instance.exports.bjs_PropertyHolder_observedProperty_set(this.pointer, value); + } + } + return { + PropertyHolder, + createPropertyHolder: function bjs_createPropertyHolder(intValue, floatValue, doubleValue, boolValue, stringValue, jsObject) { + const stringValueBytes = textEncoder.encode(stringValue); + const stringValueId = swift.memory.retain(stringValueBytes); + const ret = PropertyHolder.__construct(instance.exports.bjs_createPropertyHolder(intValue, floatValue, doubleValue, boolValue, stringValueId, stringValueBytes.length, swift.memory.retain(jsObject))); + swift.memory.release(stringValueId); + return ret; + }, + testPropertyHolder: function bjs_testPropertyHolder(holder) { + instance.exports.bjs_testPropertyHolder(holder.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + }, + }; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.d.ts index fd376d57..7efbe7c3 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.d.ts +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.d.ts @@ -14,6 +14,7 @@ export interface SwiftHeapObject { export interface Greeter extends SwiftHeapObject { greet(): string; changeName(name: string): void; + name: string; } export type Exports = { Greeter: { diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js index ab4caba3..8ca2e4e5 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js @@ -113,6 +113,18 @@ export async function createInstantiator(options, swift) { instance.exports.bjs_Greeter_changeName(this.pointer, nameId, nameBytes.length); swift.memory.release(nameId); } + get name() { + instance.exports.bjs_Greeter_name_get(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + set name(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_Greeter_name_set(this.pointer, valueId, valueBytes.length); + swift.memory.release(valueId); + } } return { Greeter, diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json index a9483455..2ec852f1 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/EnumNamespace.json @@ -40,6 +40,9 @@ "name" : "Converter", "namespace" : [ "Utils" + ], + "properties" : [ + ], "swiftCallName" : "Utils.Converter" }, @@ -84,6 +87,9 @@ "namespace" : [ "Networking", "API" + ], + "properties" : [ + ], "swiftCallName" : "Networking.API.HTTPServer" }, @@ -129,6 +135,9 @@ "Networking", "APIV2", "Internal" + ], + "properties" : [ + ], "swiftCallName" : "Internal.TestServer" } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.json index a5e960be..bb81e29a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/Namespaces.json @@ -41,6 +41,9 @@ "namespace" : [ "__Swift", "Foundation" + ], + "properties" : [ + ], "swiftCallName" : "Greeter" }, @@ -85,6 +88,9 @@ "namespace" : [ "Utils", "Converters" + ], + "properties" : [ + ], "swiftCallName" : "Converter" }, @@ -111,6 +117,9 @@ "namespace" : [ "__Swift", "Foundation" + ], + "properties" : [ + ], "swiftCallName" : "UUID" } diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.json new file mode 100644 index 00000000..e0c5e812 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.json @@ -0,0 +1,334 @@ +{ + "classes" : [ + { + "constructor" : { + "abiName" : "bjs_PropertyHolder_init", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "intValue", + "name" : "intValue", + "type" : { + "int" : { + + } + } + }, + { + "label" : "floatValue", + "name" : "floatValue", + "type" : { + "float" : { + + } + } + }, + { + "label" : "doubleValue", + "name" : "doubleValue", + "type" : { + "double" : { + + } + } + }, + { + "label" : "boolValue", + "name" : "boolValue", + "type" : { + "bool" : { + + } + } + }, + { + "label" : "stringValue", + "name" : "stringValue", + "type" : { + "string" : { + + } + } + }, + { + "label" : "jsObject", + "name" : "jsObject", + "type" : { + "jsObject" : { + + } + } + } + ] + }, + "methods" : [ + { + "abiName" : "bjs_PropertyHolder_getAllValues", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "getAllValues", + "parameters" : [ + + ], + "returnType" : { + "string" : { + + } + } + } + ], + "name" : "PropertyHolder", + "properties" : [ + { + "isReadonly" : false, + "name" : "intValue", + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "floatValue", + "type" : { + "float" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "doubleValue", + "type" : { + "double" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "boolValue", + "type" : { + "bool" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "stringValue", + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "readonlyInt", + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "readonlyFloat", + "type" : { + "float" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "readonlyDouble", + "type" : { + "double" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "readonlyBool", + "type" : { + "bool" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "readonlyString", + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "jsObject", + "type" : { + "jsObject" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "sibling", + "type" : { + "swiftHeapObject" : { + "_0" : "PropertyHolder" + } + } + }, + { + "isReadonly" : false, + "name" : "lazyValue", + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "computedReadonly", + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "computedReadWrite", + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "observedProperty", + "type" : { + "int" : { + + } + } + } + ], + "swiftCallName" : "PropertyHolder" + } + ], + "enums" : [ + + ], + "functions" : [ + { + "abiName" : "bjs_createPropertyHolder", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "createPropertyHolder", + "parameters" : [ + { + "label" : "intValue", + "name" : "intValue", + "type" : { + "int" : { + + } + } + }, + { + "label" : "floatValue", + "name" : "floatValue", + "type" : { + "float" : { + + } + } + }, + { + "label" : "doubleValue", + "name" : "doubleValue", + "type" : { + "double" : { + + } + } + }, + { + "label" : "boolValue", + "name" : "boolValue", + "type" : { + "bool" : { + + } + } + }, + { + "label" : "stringValue", + "name" : "stringValue", + "type" : { + "string" : { + + } + } + }, + { + "label" : "jsObject", + "name" : "jsObject", + "type" : { + "jsObject" : { + + } + } + } + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "PropertyHolder" + } + } + }, + { + "abiName" : "bjs_testPropertyHolder", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "testPropertyHolder", + "parameters" : [ + { + "label" : "holder", + "name" : "holder", + "type" : { + "swiftHeapObject" : { + "_0" : "PropertyHolder" + } + } + } + ], + "returnType" : { + "string" : { + + } + } + } + ], + "moduleName" : "TestModule" +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.swift new file mode 100644 index 00000000..84609c74 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PropertyTypes.swift @@ -0,0 +1,373 @@ +// 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 + +@_expose(wasm, "bjs_createPropertyHolder") +@_cdecl("bjs_createPropertyHolder") +public func _bjs_createPropertyHolder(intValue: Int32, floatValue: Float32, doubleValue: Float64, boolValue: Int32, stringValueBytes: Int32, stringValueLen: Int32, jsObject: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let stringValue = String(unsafeUninitializedCapacity: Int(stringValueLen)) { b in + _swift_js_init_memory(stringValueBytes, b.baseAddress.unsafelyUnwrapped) + return Int(stringValueLen) + } + let ret = createPropertyHolder(intValue: Int(intValue), floatValue: floatValue, doubleValue: doubleValue, boolValue: boolValue == 1, stringValue: stringValue, jsObject: JSObject(id: UInt32(bitPattern: jsObject))) + return Unmanaged.passRetained(ret).toOpaque() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testPropertyHolder") +@_cdecl("bjs_testPropertyHolder") +public func _bjs_testPropertyHolder(holder: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = testPropertyHolder(holder: Unmanaged.fromOpaque(holder).takeUnretainedValue()) + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_init") +@_cdecl("bjs_PropertyHolder_init") +public func _bjs_PropertyHolder_init(intValue: Int32, floatValue: Float32, doubleValue: Float64, boolValue: Int32, stringValueBytes: Int32, stringValueLen: Int32, jsObject: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let stringValue = String(unsafeUninitializedCapacity: Int(stringValueLen)) { b in + _swift_js_init_memory(stringValueBytes, b.baseAddress.unsafelyUnwrapped) + return Int(stringValueLen) + } + let ret = PropertyHolder(intValue: Int(intValue), floatValue: floatValue, doubleValue: doubleValue, boolValue: boolValue == 1, stringValue: stringValue, jsObject: JSObject(id: UInt32(bitPattern: jsObject))) + return Unmanaged.passRetained(ret).toOpaque() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_getAllValues") +@_cdecl("bjs_PropertyHolder_getAllValues") +public func _bjs_PropertyHolder_getAllValues(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().getAllValues() + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_intValue_get") +@_cdecl("bjs_PropertyHolder_intValue_get") +public func _bjs_PropertyHolder_intValue_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().intValue + return Int32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_intValue_set") +@_cdecl("bjs_PropertyHolder_intValue_set") +public func _bjs_PropertyHolder_intValue_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().intValue = Int(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_floatValue_get") +@_cdecl("bjs_PropertyHolder_floatValue_get") +public func _bjs_PropertyHolder_floatValue_get(_self: UnsafeMutableRawPointer) -> Float32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().floatValue + return Float32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_floatValue_set") +@_cdecl("bjs_PropertyHolder_floatValue_set") +public func _bjs_PropertyHolder_floatValue_set(_self: UnsafeMutableRawPointer, value: Float32) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().floatValue = value + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_doubleValue_get") +@_cdecl("bjs_PropertyHolder_doubleValue_get") +public func _bjs_PropertyHolder_doubleValue_get(_self: UnsafeMutableRawPointer) -> Float64 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().doubleValue + return Float64(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_doubleValue_set") +@_cdecl("bjs_PropertyHolder_doubleValue_set") +public func _bjs_PropertyHolder_doubleValue_set(_self: UnsafeMutableRawPointer, value: Float64) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().doubleValue = value + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_boolValue_get") +@_cdecl("bjs_PropertyHolder_boolValue_get") +public func _bjs_PropertyHolder_boolValue_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().boolValue + return Int32(ret ? 1 : 0) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_boolValue_set") +@_cdecl("bjs_PropertyHolder_boolValue_set") +public func _bjs_PropertyHolder_boolValue_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().boolValue = value == 1 + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_stringValue_get") +@_cdecl("bjs_PropertyHolder_stringValue_get") +public func _bjs_PropertyHolder_stringValue_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().stringValue + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_stringValue_set") +@_cdecl("bjs_PropertyHolder_stringValue_set") +public func _bjs_PropertyHolder_stringValue_set(_self: UnsafeMutableRawPointer, valueBytes: Int32, valueLen: Int32) -> Void { + #if arch(wasm32) + let value = String(unsafeUninitializedCapacity: Int(valueLen)) { b in + _swift_js_init_memory(valueBytes, b.baseAddress.unsafelyUnwrapped) + return Int(valueLen) + } + Unmanaged.fromOpaque(_self).takeUnretainedValue().stringValue = value + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_readonlyInt_get") +@_cdecl("bjs_PropertyHolder_readonlyInt_get") +public func _bjs_PropertyHolder_readonlyInt_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().readonlyInt + return Int32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_readonlyFloat_get") +@_cdecl("bjs_PropertyHolder_readonlyFloat_get") +public func _bjs_PropertyHolder_readonlyFloat_get(_self: UnsafeMutableRawPointer) -> Float32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().readonlyFloat + return Float32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_readonlyDouble_get") +@_cdecl("bjs_PropertyHolder_readonlyDouble_get") +public func _bjs_PropertyHolder_readonlyDouble_get(_self: UnsafeMutableRawPointer) -> Float64 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().readonlyDouble + return Float64(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_readonlyBool_get") +@_cdecl("bjs_PropertyHolder_readonlyBool_get") +public func _bjs_PropertyHolder_readonlyBool_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().readonlyBool + return Int32(ret ? 1 : 0) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_readonlyString_get") +@_cdecl("bjs_PropertyHolder_readonlyString_get") +public func _bjs_PropertyHolder_readonlyString_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().readonlyString + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_jsObject_get") +@_cdecl("bjs_PropertyHolder_jsObject_get") +public func _bjs_PropertyHolder_jsObject_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_jsObject_set") +@_cdecl("bjs_PropertyHolder_jsObject_set") +public func _bjs_PropertyHolder_jsObject_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().jsObject = JSObject(id: UInt32(bitPattern: value)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_sibling_get") +@_cdecl("bjs_PropertyHolder_sibling_get") +public func _bjs_PropertyHolder_sibling_get(_self: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().sibling + return Unmanaged.passRetained(ret).toOpaque() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_sibling_set") +@_cdecl("bjs_PropertyHolder_sibling_set") +public func _bjs_PropertyHolder_sibling_set(_self: UnsafeMutableRawPointer, value: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().sibling = Unmanaged.fromOpaque(value).takeUnretainedValue() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_lazyValue_get") +@_cdecl("bjs_PropertyHolder_lazyValue_get") +public func _bjs_PropertyHolder_lazyValue_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().lazyValue + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_lazyValue_set") +@_cdecl("bjs_PropertyHolder_lazyValue_set") +public func _bjs_PropertyHolder_lazyValue_set(_self: UnsafeMutableRawPointer, valueBytes: Int32, valueLen: Int32) -> Void { + #if arch(wasm32) + let value = String(unsafeUninitializedCapacity: Int(valueLen)) { b in + _swift_js_init_memory(valueBytes, b.baseAddress.unsafelyUnwrapped) + return Int(valueLen) + } + Unmanaged.fromOpaque(_self).takeUnretainedValue().lazyValue = value + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_computedReadonly_get") +@_cdecl("bjs_PropertyHolder_computedReadonly_get") +public func _bjs_PropertyHolder_computedReadonly_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().computedReadonly + return Int32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_computedReadWrite_get") +@_cdecl("bjs_PropertyHolder_computedReadWrite_get") +public func _bjs_PropertyHolder_computedReadWrite_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().computedReadWrite + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_computedReadWrite_set") +@_cdecl("bjs_PropertyHolder_computedReadWrite_set") +public func _bjs_PropertyHolder_computedReadWrite_set(_self: UnsafeMutableRawPointer, valueBytes: Int32, valueLen: Int32) -> Void { + #if arch(wasm32) + let value = String(unsafeUninitializedCapacity: Int(valueLen)) { b in + _swift_js_init_memory(valueBytes, b.baseAddress.unsafelyUnwrapped) + return Int(valueLen) + } + Unmanaged.fromOpaque(_self).takeUnretainedValue().computedReadWrite = value + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_observedProperty_get") +@_cdecl("bjs_PropertyHolder_observedProperty_get") +public func _bjs_PropertyHolder_observedProperty_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().observedProperty + return Int32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_observedProperty_set") +@_cdecl("bjs_PropertyHolder_observedProperty_set") +public func _bjs_PropertyHolder_observedProperty_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().observedProperty = Int(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_deinit") +@_cdecl("bjs_PropertyHolder_deinit") +public func _bjs_PropertyHolder_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension PropertyHolder: ConvertibleToJSValue { + var jsValue: JSValue { + @_extern(wasm, module: "TestModule", name: "bjs_PropertyHolder_wrap") + func _bjs_PropertyHolder_wrap(_: UnsafeMutableRawPointer) -> Int32 + return .object(JSObject(id: UInt32(bitPattern: _bjs_PropertyHolder_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 7f8324ac..5266959a 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json @@ -62,6 +62,17 @@ } ], "name" : "Greeter", + "properties" : [ + { + "isReadonly" : false, + "name" : "name", + "type" : { + "string" : { + + } + } + } + ], "swiftCallName" : "Greeter" } ], diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.swift index 09589de3..bcb9f3d7 100644 --- a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.swift +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.swift @@ -58,6 +58,33 @@ public func _bjs_Greeter_changeName(_self: UnsafeMutableRawPointer, nameBytes: I #endif } +@_expose(wasm, "bjs_Greeter_name_get") +@_cdecl("bjs_Greeter_name_get") +public func _bjs_Greeter_name_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().name + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_Greeter_name_set") +@_cdecl("bjs_Greeter_name_set") +public func _bjs_Greeter_name_set(_self: UnsafeMutableRawPointer, valueBytes: Int32, valueLen: Int32) -> Void { + #if arch(wasm32) + let value = String(unsafeUninitializedCapacity: Int(valueLen)) { b in + _swift_js_init_memory(valueBytes, b.baseAddress.unsafelyUnwrapped) + return Int(valueLen) + } + Unmanaged.fromOpaque(_self).takeUnretainedValue().name = value + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_Greeter_deinit") @_cdecl("bjs_Greeter_deinit") public func _bjs_Greeter_deinit(pointer: UnsafeMutableRawPointer) { diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index bd080623..fc2d8d6a 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -60,7 +60,8 @@ struct TestError: Error { @JS func asyncRoundTripJSObject(v: JSObject) async -> JSObject { return v } @JS class Greeter { - var name: String + @JS var name: String + @JS let prefix: String = "Hello" nonisolated(unsafe) static var onDeinit: () -> Void = {} @@ -69,7 +70,7 @@ struct TestError: Error { } @JS func greet() -> String { - return "Hello, \(name)!" + return "\(prefix), \(name)!" } @JS func changeName(name: String) { self.name = name @@ -270,6 +271,145 @@ enum Internal { } } +// MARK: - Property Tests + +// Simple class for SwiftHeapObject property testing +@JS class SimplePropertyHolder { + @JS var value: Int + + @JS init(value: Int) { + self.value = value + } +} + +// Test class for various property types +@JS class PropertyHolder { + // Primitive properties + @JS var intValue: Int + @JS var floatValue: Float + @JS var doubleValue: Double + @JS var boolValue: Bool + @JS var stringValue: String + + // Readonly primitive properties + @JS let readonlyInt: Int = 42 + @JS let readonlyFloat: Float = 3.14 + @JS let readonlyDouble: Double = 2.718281828 + @JS let readonlyBool: Bool = true + @JS let readonlyString: String = "constant" + + // JSObject property + @JS var jsObject: JSObject + + // SwiftHeapObject property + @JS var sibling: SimplePropertyHolder + + // Lazy stored property + @JS lazy var lazyValue: String = "computed lazily" + + // Computed property with getter only (readonly) + @JS var computedReadonly: Int { + return intValue * 2 + } + + // Computed property with getter and setter + @JS var computedReadWrite: String { + get { + return "Value: \(intValue)" + } + set { + // Parse the number from "Value: X" format + if let range = newValue.range(of: "Value: "), + let number = Int(String(newValue[range.upperBound...])) + { + intValue = number + } + } + } + + // Property with property observers + @JS var observedProperty: Int { + willSet { + Self.willSetCallCount += 1 + Self.lastWillSetOldValue = self.observedProperty + Self.lastWillSetNewValue = newValue + } + didSet { + Self.didSetCallCount += 1 + Self.lastDidSetOldValue = oldValue + Self.lastDidSetNewValue = self.observedProperty + } + } + + // Static properties to track observer calls + nonisolated(unsafe) static var willSetCallCount: Int = 0 + nonisolated(unsafe) static var didSetCallCount: Int = 0 + nonisolated(unsafe) static var lastWillSetOldValue: Int = 0 + nonisolated(unsafe) static var lastWillSetNewValue: Int = 0 + nonisolated(unsafe) static var lastDidSetOldValue: Int = 0 + nonisolated(unsafe) static var lastDidSetNewValue: Int = 0 + + @JS init( + intValue: Int, + floatValue: Float, + doubleValue: Double, + boolValue: Bool, + stringValue: String, + jsObject: JSObject, + sibling: SimplePropertyHolder + ) { + self.intValue = intValue + self.floatValue = floatValue + self.doubleValue = doubleValue + self.boolValue = boolValue + self.stringValue = stringValue + self.jsObject = jsObject + self.sibling = sibling + self.observedProperty = intValue // Initialize observed property + } + + @JS func getAllValues() -> String { + return "int:\(intValue),float:\(floatValue),double:\(doubleValue),bool:\(boolValue),string:\(stringValue)" + } +} + +@JS func createPropertyHolder( + intValue: Int, + floatValue: Float, + doubleValue: Double, + boolValue: Bool, + stringValue: String, + jsObject: JSObject +) -> PropertyHolder { + let sibling = SimplePropertyHolder(value: 999) + return PropertyHolder( + intValue: intValue, + floatValue: floatValue, + doubleValue: doubleValue, + boolValue: boolValue, + stringValue: stringValue, + jsObject: jsObject, + sibling: sibling + ) +} + +@JS func testPropertyHolder(holder: PropertyHolder) -> String { + return holder.getAllValues() +} + +@JS func resetObserverCounts() { + PropertyHolder.willSetCallCount = 0 + PropertyHolder.didSetCallCount = 0 + PropertyHolder.lastWillSetOldValue = 0 + PropertyHolder.lastWillSetNewValue = 0 + PropertyHolder.lastDidSetOldValue = 0 + PropertyHolder.lastDidSetNewValue = 0 +} + +@JS func getObserverStats() -> String { + return + "willSet:\(PropertyHolder.willSetCallCount),didSet:\(PropertyHolder.didSetCallCount),willSetOld:\(PropertyHolder.lastWillSetOldValue),willSetNew:\(PropertyHolder.lastWillSetNewValue),didSetOld:\(PropertyHolder.lastDidSetOldValue),didSetNew:\(PropertyHolder.lastDidSetNewValue)" +} 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 15e1cfc5..d51f954e 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift @@ -771,6 +771,57 @@ public func _bjs_getTSTheme() -> Void { #endif } +@_expose(wasm, "bjs_createPropertyHolder") +@_cdecl("bjs_createPropertyHolder") +public func _bjs_createPropertyHolder(intValue: Int32, floatValue: Float32, doubleValue: Float64, boolValue: Int32, stringValueBytes: Int32, stringValueLen: Int32, jsObject: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let stringValue = String(unsafeUninitializedCapacity: Int(stringValueLen)) { b in + _swift_js_init_memory(stringValueBytes, b.baseAddress.unsafelyUnwrapped) + return Int(stringValueLen) + } + let ret = createPropertyHolder(intValue: Int(intValue), floatValue: floatValue, doubleValue: doubleValue, boolValue: boolValue == 1, stringValue: stringValue, jsObject: JSObject(id: UInt32(bitPattern: jsObject))) + return Unmanaged.passRetained(ret).toOpaque() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testPropertyHolder") +@_cdecl("bjs_testPropertyHolder") +public func _bjs_testPropertyHolder(holder: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = testPropertyHolder(holder: Unmanaged.fromOpaque(holder).takeUnretainedValue()) + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_resetObserverCounts") +@_cdecl("bjs_resetObserverCounts") +public func _bjs_resetObserverCounts() -> Void { + #if arch(wasm32) + resetObserverCounts() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_getObserverStats") +@_cdecl("bjs_getObserverStats") +public func _bjs_getObserverStats() -> Void { + #if arch(wasm32) + var ret = getObserverStats() + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_Greeter_init") @_cdecl("bjs_Greeter_init") public func _bjs_Greeter_init(nameBytes: Int32, nameLen: Int32) -> UnsafeMutableRawPointer { @@ -813,6 +864,46 @@ public func _bjs_Greeter_changeName(_self: UnsafeMutableRawPointer, nameBytes: I #endif } +@_expose(wasm, "bjs_Greeter_name_get") +@_cdecl("bjs_Greeter_name_get") +public func _bjs_Greeter_name_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().name + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_Greeter_name_set") +@_cdecl("bjs_Greeter_name_set") +public func _bjs_Greeter_name_set(_self: UnsafeMutableRawPointer, valueBytes: Int32, valueLen: Int32) -> Void { + #if arch(wasm32) + let value = String(unsafeUninitializedCapacity: Int(valueLen)) { b in + _swift_js_init_memory(valueBytes, b.baseAddress.unsafelyUnwrapped) + return Int(valueLen) + } + Unmanaged.fromOpaque(_self).takeUnretainedValue().name = value + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_Greeter_prefix_get") +@_cdecl("bjs_Greeter_prefix_get") +public func _bjs_Greeter_prefix_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().prefix + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_Greeter_deinit") @_cdecl("bjs_Greeter_deinit") public func _bjs_Greeter_deinit(pointer: UnsafeMutableRawPointer) { @@ -969,4 +1060,388 @@ extension Internal.TestServer: ConvertibleToJSValue { func _bjs_TestServer_wrap(_: UnsafeMutableRawPointer) -> Int32 return .object(JSObject(id: UInt32(bitPattern: _bjs_TestServer_wrap(Unmanaged.passRetained(self).toOpaque())))) } +} + +@_expose(wasm, "bjs_SimplePropertyHolder_init") +@_cdecl("bjs_SimplePropertyHolder_init") +public func _bjs_SimplePropertyHolder_init(value: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = SimplePropertyHolder(value: Int(value)) + return Unmanaged.passRetained(ret).toOpaque() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimplePropertyHolder_value_get") +@_cdecl("bjs_SimplePropertyHolder_value_get") +public func _bjs_SimplePropertyHolder_value_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().value + return Int32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimplePropertyHolder_value_set") +@_cdecl("bjs_SimplePropertyHolder_value_set") +public func _bjs_SimplePropertyHolder_value_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().value = Int(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_SimplePropertyHolder_deinit") +@_cdecl("bjs_SimplePropertyHolder_deinit") +public func _bjs_SimplePropertyHolder_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension SimplePropertyHolder: ConvertibleToJSValue { + var jsValue: JSValue { + @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_SimplePropertyHolder_wrap") + func _bjs_SimplePropertyHolder_wrap(_: UnsafeMutableRawPointer) -> Int32 + return .object(JSObject(id: UInt32(bitPattern: _bjs_SimplePropertyHolder_wrap(Unmanaged.passRetained(self).toOpaque())))) + } +} + +@_expose(wasm, "bjs_PropertyHolder_init") +@_cdecl("bjs_PropertyHolder_init") +public func _bjs_PropertyHolder_init(intValue: Int32, floatValue: Float32, doubleValue: Float64, boolValue: Int32, stringValueBytes: Int32, stringValueLen: Int32, jsObject: Int32, sibling: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let stringValue = String(unsafeUninitializedCapacity: Int(stringValueLen)) { b in + _swift_js_init_memory(stringValueBytes, b.baseAddress.unsafelyUnwrapped) + return Int(stringValueLen) + } + let ret = PropertyHolder(intValue: Int(intValue), floatValue: floatValue, doubleValue: doubleValue, boolValue: boolValue == 1, stringValue: stringValue, jsObject: JSObject(id: UInt32(bitPattern: jsObject)), sibling: Unmanaged.fromOpaque(sibling).takeUnretainedValue()) + return Unmanaged.passRetained(ret).toOpaque() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_getAllValues") +@_cdecl("bjs_PropertyHolder_getAllValues") +public func _bjs_PropertyHolder_getAllValues(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().getAllValues() + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_intValue_get") +@_cdecl("bjs_PropertyHolder_intValue_get") +public func _bjs_PropertyHolder_intValue_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().intValue + return Int32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_intValue_set") +@_cdecl("bjs_PropertyHolder_intValue_set") +public func _bjs_PropertyHolder_intValue_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().intValue = Int(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_floatValue_get") +@_cdecl("bjs_PropertyHolder_floatValue_get") +public func _bjs_PropertyHolder_floatValue_get(_self: UnsafeMutableRawPointer) -> Float32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().floatValue + return Float32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_floatValue_set") +@_cdecl("bjs_PropertyHolder_floatValue_set") +public func _bjs_PropertyHolder_floatValue_set(_self: UnsafeMutableRawPointer, value: Float32) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().floatValue = value + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_doubleValue_get") +@_cdecl("bjs_PropertyHolder_doubleValue_get") +public func _bjs_PropertyHolder_doubleValue_get(_self: UnsafeMutableRawPointer) -> Float64 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().doubleValue + return Float64(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_doubleValue_set") +@_cdecl("bjs_PropertyHolder_doubleValue_set") +public func _bjs_PropertyHolder_doubleValue_set(_self: UnsafeMutableRawPointer, value: Float64) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().doubleValue = value + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_boolValue_get") +@_cdecl("bjs_PropertyHolder_boolValue_get") +public func _bjs_PropertyHolder_boolValue_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().boolValue + return Int32(ret ? 1 : 0) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_boolValue_set") +@_cdecl("bjs_PropertyHolder_boolValue_set") +public func _bjs_PropertyHolder_boolValue_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().boolValue = value == 1 + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_stringValue_get") +@_cdecl("bjs_PropertyHolder_stringValue_get") +public func _bjs_PropertyHolder_stringValue_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().stringValue + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_stringValue_set") +@_cdecl("bjs_PropertyHolder_stringValue_set") +public func _bjs_PropertyHolder_stringValue_set(_self: UnsafeMutableRawPointer, valueBytes: Int32, valueLen: Int32) -> Void { + #if arch(wasm32) + let value = String(unsafeUninitializedCapacity: Int(valueLen)) { b in + _swift_js_init_memory(valueBytes, b.baseAddress.unsafelyUnwrapped) + return Int(valueLen) + } + Unmanaged.fromOpaque(_self).takeUnretainedValue().stringValue = value + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_readonlyInt_get") +@_cdecl("bjs_PropertyHolder_readonlyInt_get") +public func _bjs_PropertyHolder_readonlyInt_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().readonlyInt + return Int32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_readonlyFloat_get") +@_cdecl("bjs_PropertyHolder_readonlyFloat_get") +public func _bjs_PropertyHolder_readonlyFloat_get(_self: UnsafeMutableRawPointer) -> Float32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().readonlyFloat + return Float32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_readonlyDouble_get") +@_cdecl("bjs_PropertyHolder_readonlyDouble_get") +public func _bjs_PropertyHolder_readonlyDouble_get(_self: UnsafeMutableRawPointer) -> Float64 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().readonlyDouble + return Float64(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_readonlyBool_get") +@_cdecl("bjs_PropertyHolder_readonlyBool_get") +public func _bjs_PropertyHolder_readonlyBool_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().readonlyBool + return Int32(ret ? 1 : 0) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_readonlyString_get") +@_cdecl("bjs_PropertyHolder_readonlyString_get") +public func _bjs_PropertyHolder_readonlyString_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().readonlyString + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_jsObject_get") +@_cdecl("bjs_PropertyHolder_jsObject_get") +public func _bjs_PropertyHolder_jsObject_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().jsObject + return _swift_js_retain(Int32(bitPattern: ret.id)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_jsObject_set") +@_cdecl("bjs_PropertyHolder_jsObject_set") +public func _bjs_PropertyHolder_jsObject_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().jsObject = JSObject(id: UInt32(bitPattern: value)) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_sibling_get") +@_cdecl("bjs_PropertyHolder_sibling_get") +public func _bjs_PropertyHolder_sibling_get(_self: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().sibling + return Unmanaged.passRetained(ret).toOpaque() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_sibling_set") +@_cdecl("bjs_PropertyHolder_sibling_set") +public func _bjs_PropertyHolder_sibling_set(_self: UnsafeMutableRawPointer, value: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().sibling = Unmanaged.fromOpaque(value).takeUnretainedValue() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_lazyValue_get") +@_cdecl("bjs_PropertyHolder_lazyValue_get") +public func _bjs_PropertyHolder_lazyValue_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().lazyValue + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_lazyValue_set") +@_cdecl("bjs_PropertyHolder_lazyValue_set") +public func _bjs_PropertyHolder_lazyValue_set(_self: UnsafeMutableRawPointer, valueBytes: Int32, valueLen: Int32) -> Void { + #if arch(wasm32) + let value = String(unsafeUninitializedCapacity: Int(valueLen)) { b in + _swift_js_init_memory(valueBytes, b.baseAddress.unsafelyUnwrapped) + return Int(valueLen) + } + Unmanaged.fromOpaque(_self).takeUnretainedValue().lazyValue = value + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_computedReadonly_get") +@_cdecl("bjs_PropertyHolder_computedReadonly_get") +public func _bjs_PropertyHolder_computedReadonly_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().computedReadonly + return Int32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_computedReadWrite_get") +@_cdecl("bjs_PropertyHolder_computedReadWrite_get") +public func _bjs_PropertyHolder_computedReadWrite_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().computedReadWrite + return ret.withUTF8 { ptr in + _swift_js_return_string(ptr.baseAddress, Int32(ptr.count)) + } + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_computedReadWrite_set") +@_cdecl("bjs_PropertyHolder_computedReadWrite_set") +public func _bjs_PropertyHolder_computedReadWrite_set(_self: UnsafeMutableRawPointer, valueBytes: Int32, valueLen: Int32) -> Void { + #if arch(wasm32) + let value = String(unsafeUninitializedCapacity: Int(valueLen)) { b in + _swift_js_init_memory(valueBytes, b.baseAddress.unsafelyUnwrapped) + return Int(valueLen) + } + Unmanaged.fromOpaque(_self).takeUnretainedValue().computedReadWrite = value + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_observedProperty_get") +@_cdecl("bjs_PropertyHolder_observedProperty_get") +public func _bjs_PropertyHolder_observedProperty_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().observedProperty + return Int32(ret) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_observedProperty_set") +@_cdecl("bjs_PropertyHolder_observedProperty_set") +public func _bjs_PropertyHolder_observedProperty_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + Unmanaged.fromOpaque(_self).takeUnretainedValue().observedProperty = Int(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_PropertyHolder_deinit") +@_cdecl("bjs_PropertyHolder_deinit") +public func _bjs_PropertyHolder_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension PropertyHolder: ConvertibleToJSValue { + var jsValue: JSValue { + @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_PropertyHolder_wrap") + func _bjs_PropertyHolder_wrap(_: UnsafeMutableRawPointer) -> Int32 + return .object(JSObject(id: UInt32(bitPattern: _bjs_PropertyHolder_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 b4642d8a..30b0685f 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -62,6 +62,26 @@ } ], "name" : "Greeter", + "properties" : [ + { + "isReadonly" : false, + "name" : "name", + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "prefix", + "type" : { + "string" : { + + } + } + } + ], "swiftCallName" : "Greeter" }, { @@ -125,6 +145,9 @@ } ], "name" : "Calculator", + "properties" : [ + + ], "swiftCallName" : "Calculator" }, { @@ -167,6 +190,9 @@ "name" : "Converter", "namespace" : [ "Utils" + ], + "properties" : [ + ], "swiftCallName" : "Utils.Converter" }, @@ -211,6 +237,9 @@ "namespace" : [ "Networking", "API" + ], + "properties" : [ + ], "swiftCallName" : "Networking.API.HTTPServer" }, @@ -256,8 +285,287 @@ "Networking", "APIV2", "Internal" + ], + "properties" : [ + ], "swiftCallName" : "Internal.TestServer" + }, + { + "constructor" : { + "abiName" : "bjs_SimplePropertyHolder_init", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "value", + "name" : "value", + "type" : { + "int" : { + + } + } + } + ] + }, + "methods" : [ + + ], + "name" : "SimplePropertyHolder", + "properties" : [ + { + "isReadonly" : false, + "name" : "value", + "type" : { + "int" : { + + } + } + } + ], + "swiftCallName" : "SimplePropertyHolder" + }, + { + "constructor" : { + "abiName" : "bjs_PropertyHolder_init", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "intValue", + "name" : "intValue", + "type" : { + "int" : { + + } + } + }, + { + "label" : "floatValue", + "name" : "floatValue", + "type" : { + "float" : { + + } + } + }, + { + "label" : "doubleValue", + "name" : "doubleValue", + "type" : { + "double" : { + + } + } + }, + { + "label" : "boolValue", + "name" : "boolValue", + "type" : { + "bool" : { + + } + } + }, + { + "label" : "stringValue", + "name" : "stringValue", + "type" : { + "string" : { + + } + } + }, + { + "label" : "jsObject", + "name" : "jsObject", + "type" : { + "jsObject" : { + + } + } + }, + { + "label" : "sibling", + "name" : "sibling", + "type" : { + "swiftHeapObject" : { + "_0" : "SimplePropertyHolder" + } + } + } + ] + }, + "methods" : [ + { + "abiName" : "bjs_PropertyHolder_getAllValues", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "getAllValues", + "parameters" : [ + + ], + "returnType" : { + "string" : { + + } + } + } + ], + "name" : "PropertyHolder", + "properties" : [ + { + "isReadonly" : false, + "name" : "intValue", + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "floatValue", + "type" : { + "float" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "doubleValue", + "type" : { + "double" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "boolValue", + "type" : { + "bool" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "stringValue", + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "readonlyInt", + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "readonlyFloat", + "type" : { + "float" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "readonlyDouble", + "type" : { + "double" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "readonlyBool", + "type" : { + "bool" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "readonlyString", + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "jsObject", + "type" : { + "jsObject" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "sibling", + "type" : { + "swiftHeapObject" : { + "_0" : "SimplePropertyHolder" + } + } + }, + { + "isReadonly" : false, + "name" : "lazyValue", + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : true, + "name" : "computedReadonly", + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "computedReadWrite", + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "name" : "observedProperty", + "type" : { + "int" : { + + } + } + } + ], + "swiftCallName" : "PropertyHolder" } ], "enums" : [ @@ -1484,6 +1792,131 @@ "_1" : "String" } } + }, + { + "abiName" : "bjs_createPropertyHolder", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "createPropertyHolder", + "parameters" : [ + { + "label" : "intValue", + "name" : "intValue", + "type" : { + "int" : { + + } + } + }, + { + "label" : "floatValue", + "name" : "floatValue", + "type" : { + "float" : { + + } + } + }, + { + "label" : "doubleValue", + "name" : "doubleValue", + "type" : { + "double" : { + + } + } + }, + { + "label" : "boolValue", + "name" : "boolValue", + "type" : { + "bool" : { + + } + } + }, + { + "label" : "stringValue", + "name" : "stringValue", + "type" : { + "string" : { + + } + } + }, + { + "label" : "jsObject", + "name" : "jsObject", + "type" : { + "jsObject" : { + + } + } + } + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "PropertyHolder" + } + } + }, + { + "abiName" : "bjs_testPropertyHolder", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "testPropertyHolder", + "parameters" : [ + { + "label" : "holder", + "name" : "holder", + "type" : { + "swiftHeapObject" : { + "_0" : "PropertyHolder" + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_resetObserverCounts", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "resetObserverCounts", + "parameters" : [ + + ], + "returnType" : { + "void" : { + + } + } + }, + { + "abiName" : "bjs_getObserverStats", + "effects" : { + "isAsync" : false, + "isThrows" : false + }, + "name" : "getObserverStats", + "parameters" : [ + + ], + "returnType" : { + "string" : { + + } + } } ], "moduleName" : "BridgeJSRuntimeTests" diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 6954d87c..1459f21a 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -135,17 +135,145 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { const g = new exports.Greeter("John"); assert.equal(g.greet(), "Hello, John!"); - g.changeName("Jane"); + + // Test property getters + assert.equal(g.name, "John"); + assert.equal(g.prefix, "Hello"); + + // Test property setters + g.name = "Jane"; + assert.equal(g.name, "Jane"); assert.equal(g.greet(), "Hello, Jane!"); - exports.takeGreeter(g, "Jay"); + + // Test readonly property (should still be readable) + assert.equal(g.prefix, "Hello"); + + // Test method-based name change still works + g.changeName("Jay"); + assert.equal(g.name, "Jay"); assert.equal(g.greet(), "Hello, Jay!"); + // Test that property changes via JS are reflected in Swift methods + g.name = "Alice"; + assert.equal(g.greet(), "Hello, Alice!"); + exports.takeGreeter(g, "Bob"); + assert.equal(g.name, "Bob"); + assert.equal(g.greet(), "Hello, Bob!"); + const g2 = exports.roundTripSwiftHeapObject(g) - assert.equal(g2.greet(), "Hello, Jay!"); + assert.equal(g2.greet(), "Hello, Bob!"); + assert.equal(g2.name, "Bob"); + assert.equal(g2.prefix, "Hello"); g2.release(); g.release(); + // Test PropertyHolder with various types + const testObj = { testProp: "test" }; + const sibling = new exports.SimplePropertyHolder(999); + const ph = new exports.PropertyHolder( + 123, // intValue + 3.14, // floatValue + 2.718, // doubleValue + true, // boolValue + "test", // stringValue + testObj, // jsObject + sibling // sibling + ); + + // Test primitive property getters + assert.equal(ph.intValue, 123); + assert.equal(ph.floatValue, 3.140000104904175); // Float32 precision + assert.equal(ph.doubleValue, 2.718); + assert.equal(ph.boolValue, true); + assert.equal(ph.stringValue, "test"); + + // Test readonly property getters + assert.equal(ph.readonlyInt, 42); + assert.equal(ph.readonlyFloat, 3.140000104904175); // Float32 precision + assert.equal(ph.readonlyDouble, 2.718281828); + assert.equal(ph.readonlyBool, true); + assert.equal(ph.readonlyString, "constant"); + + // Test JSObject property + assert.equal(ph.jsObject, testObj); + + // Test SwiftHeapObject property (sibling should be a SimplePropertyHolder with value 999) + assert.equal(ph.sibling.value, 999); + + // Test primitive property setters + ph.intValue = 456; + ph.floatValue = 6.28; + ph.doubleValue = 1.414; + ph.boolValue = false; + ph.stringValue = "updated"; + + assert.equal(ph.intValue, 456); + assert.equal(ph.floatValue, 6.280000209808350); // Float32 precision + assert.equal(ph.doubleValue, 1.414); + assert.equal(ph.boolValue, false); + assert.equal(ph.stringValue, "updated"); + + // Test JSObject property setter + const newObj = { newProp: "new" }; + ph.jsObject = newObj; + assert.equal(ph.jsObject, newObj); + + // Test SwiftHeapObject property with different object + const ph2 = exports.createPropertyHolder(999, 1.1, 2.2, false, "other", testObj); + const newSibling = new exports.SimplePropertyHolder(123); + ph.sibling = newSibling; + assert.equal(ph.sibling.value, 123); + + // Test lazy property + assert.equal(ph.lazyValue, "computed lazily"); + ph.lazyValue = "modified lazy"; + assert.equal(ph.lazyValue, "modified lazy"); + + // Test computed read-write property + assert.equal(ph.computedReadWrite, "Value: 456"); + ph.computedReadWrite = "Value: 777"; + assert.equal(ph.intValue, 777); // Should have parsed and set intValue + assert.equal(ph.computedReadWrite, "Value: 777"); + + // Test computed readonly property + assert.equal(ph.computedReadonly, 1554); // intValue * 2 = 777 * 2 + + // Test property with observers + // Sync observedProperty to match current intValue, then reset counters for clean test + ph.observedProperty = 777; // Sync with current intValue after computed property changed it + exports.resetObserverCounts(); // Reset counters to start fresh test + + // Set property from JavaScript and verify observers are called + ph.observedProperty = 100; + assert.equal(ph.observedProperty, 100); + let afterSetStats = exports.getObserverStats(); + + // Verify willSet and didSet were called + // The stats should show: willSet:1,didSet:1,willSetOld:777,willSetNew:100,didSetOld:777,didSetNew:100 + assert(afterSetStats.includes("willSet:1"), `willSet should be called once, got: ${afterSetStats}`); + assert(afterSetStats.includes("didSet:1"), `didSet should be called once, got: ${afterSetStats}`); + assert(afterSetStats.includes("willSetOld:777"), `willSet should see old value 777, got: ${afterSetStats}`); + assert(afterSetStats.includes("willSetNew:100"), `willSet should see new value 100, got: ${afterSetStats}`); + assert(afterSetStats.includes("didSetOld:777"), `didSet should see old value 777, got: ${afterSetStats}`); + assert(afterSetStats.includes("didSetNew:100"), `didSet should see new value 100, got: ${afterSetStats}`); + + // Set property to a different value and verify observers are called again + ph.observedProperty = 200; + assert.equal(ph.observedProperty, 200); + let afterSecondSetStats = exports.getObserverStats(); + + // Now should be: willSet:2,didSet:2,willSetOld:100,willSetNew:200,didSetOld:100,didSetNew:200 + assert(afterSecondSetStats.includes("willSet:2"), `willSet should be called twice, got: ${afterSecondSetStats}`); + assert(afterSecondSetStats.includes("didSet:2"), `didSet should be called twice, got: ${afterSecondSetStats}`); + assert(afterSecondSetStats.includes("willSetOld:100"), `willSet should see old value 100 on second call, got: ${afterSecondSetStats}`); + assert(afterSecondSetStats.includes("willSetNew:200"), `willSet should see new value 200 on second call, got: ${afterSecondSetStats}`); + assert(afterSecondSetStats.includes("didSetOld:100"), `didSet should see old value 100 on second call, got: ${afterSecondSetStats}`); + assert(afterSecondSetStats.includes("didSetNew:200"), `didSet should see new value 200 on second call, got: ${afterSecondSetStats}`); + + ph.release(); + ph2.release(); + // Test class without @JS init constructor const calc = exports.createCalculator(); assert.equal(calc.square(5), 25);