Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 57a6fa2

Browse files
Merge pull request #409 from swiftwasm/yt/bjs-fix-swift-class
BridgeJS: Change `@JS init` to generate `static init()` methods instead of `constructor` in JS
2 parents cc50be3 + ed482fe commit 57a6fa2

File tree

13 files changed

+308
-25
lines changed

13 files changed

+308
-25
lines changed

‎CONTRIBUTING.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@ Tests for `PackageToJS` plugin:
7171
swift test --package-path ./Plugins/PackageToJS
7272
```
7373

74+
Tests for `BridgeJS` plugin:
75+
76+
```bash
77+
swift test --package-path ./Plugins/BridgeJS
78+
```
79+
80+
To update snapshot test files when expected output changes:
81+
82+
```bash
83+
UPDATE_SNAPSHOTS=1 swift test --package-path ./Plugins/BridgeJS
84+
```
85+
7486
### Editing `./Runtime` directory
7587

7688
The `./Runtime` directory contains the JavaScript runtime that interacts with the JavaScript environment and Swift code.

‎Examples/ExportSwift/index.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js";
22
const { exports } = await init({});
33

44
const Greeter = exports.Greeter;
5-
const greeter = newGreeter("World");
5+
const greeter = Greeter.init("World");
66
const circle = exports.renderCircleSVG(100);
77

88
// Display the results

‎Examples/PlayBridgeJS/Sources/JavaScript/app.js‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class BridgeJSPlayground {
5252
createTS2Skeleton: this.createTS2Skeleton
5353
}
5454
});
55-
this.playBridgeJS = newexports.PlayBridgeJS();
55+
this.playBridgeJS = exports.PlayBridgeJS.init();
5656
console.log('BridgeJS initialized successfully');
5757
} catch (error) {
5858
console.error('Failed to initialize BridgeJS:', error);
@@ -162,4 +162,4 @@ export class BridgeJSPlayground {
162162
hideError() {
163163
this.errorDisplay.classList.remove('show');
164164
}
165-
}
165+
}

‎Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift‎

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ struct BridgeJSLink {
4343
let swiftHeapObjectClassJs = """
4444
/// Represents a Swift heap object like a class instance or an actor instance.
4545
class SwiftHeapObject {
46+
static __construct(ptr, deinit) {
47+
return new SwiftHeapObject(ptr, deinit);
48+
}
49+
4650
constructor(pointer, deinit) {
4751
this.pointer = pointer;
4852
this.hasReleased = false;
@@ -401,7 +405,7 @@ struct BridgeJSLink {
401405
bodyLines.append("swift.memory.release(retId);")
402406
returnExpr = "ret"
403407
case .swiftHeapObject(let name):
404-
bodyLines.append("const ret = new \(name)(\(call));")
408+
bodyLines.append("const ret = \(name).__construct(\(call));")
405409
returnExpr = "ret"
406410
}
407411
return returnExpr
@@ -484,23 +488,37 @@ struct BridgeJSLink {
484488
dtsExportEntryLines.append("\(klass.name): {")
485489
jsLines.append("class \(klass.name) extends SwiftHeapObject {")
486490

491+
// Always add __construct and constructor methods for all classes
492+
var constructorLines: [String] = []
493+
constructorLines.append("static __construct(ptr) {")
494+
constructorLines.append(
495+
"return new \(klass.name)(ptr, instance.exports.bjs_\(klass.name)_deinit);".indent(count: 4)
496+
)
497+
constructorLines.append("}")
498+
constructorLines.append("")
499+
constructorLines.append("constructor(pointer, deinit) {")
500+
constructorLines.append("super(pointer, deinit);".indent(count: 4))
501+
constructorLines.append("}")
502+
jsLines.append(contentsOf: constructorLines.map { 0ドル.indent(count: 4) })
503+
487504
if let constructor: ExportedConstructor = klass.constructor {
488505
let thunkBuilder = ExportedThunkBuilder(effects: constructor.effects)
489506
for param in constructor.parameters {
490507
thunkBuilder.lowerParameter(param: param)
491508
}
492509
var funcLines: [String] = []
493-
funcLines.append("constructor(\(constructor.parameters.map { 0ドル.name }.joined(separator: ", "))) {")
510+
funcLines.append("")
511+
funcLines.append("static init(\(constructor.parameters.map { 0ドル.name }.joined(separator: ", "))) {")
494512
let returnExpr = thunkBuilder.callConstructor(abiName: constructor.abiName)
495513
funcLines.append(contentsOf: thunkBuilder.bodyLines.map { 0ドル.indent(count: 4) })
496514
funcLines.append(contentsOf: thunkBuilder.cleanupLines.map { 0ドル.indent(count: 4) })
497515
funcLines.append(contentsOf: thunkBuilder.checkExceptionLines().map { 0ドル.indent(count: 4) })
498-
funcLines.append("super(\(returnExpr), instance.exports.bjs_\(klass.name)_deinit);".indent(count: 4))
516+
funcLines.append("return \(klass.name).__construct(\(returnExpr));".indent(count: 4))
499517
funcLines.append("}")
500518
jsLines.append(contentsOf: funcLines.map { 0ドル.indent(count: 4) })
501519

502520
dtsExportEntryLines.append(
503-
"new\(renderTSSignature(parameters: constructor.parameters, returnType: .swiftHeapObject(klass.name)));"
521+
"init\(renderTSSignature(parameters: constructor.parameters, returnType: .swiftHeapObject(klass.name)));"
504522
.indent(count: 4)
505523
)
506524
}

‎Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.d.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ export interface UUID extends SwiftHeapObject {
5151
}
5252
export type Exports = {
5353
Greeter: {
54-
new(name: string): Greeter;
54+
init(name: string): Greeter;
5555
}
5656
Converter: {
57-
new(): Converter;
57+
init(): Converter;
5858
}
5959
UUID: {
6060
}

‎Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Namespaces.Export.js‎

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ export async function createInstantiator(options, swift) {
6060
const js = swift.memory.heap;
6161
/// Represents a Swift heap object like a class instance or an actor instance.
6262
class SwiftHeapObject {
63+
static __construct(ptr, deinit) {
64+
return new SwiftHeapObject(ptr, deinit);
65+
}
66+
6367
constructor(pointer, deinit) {
6468
this.pointer = pointer;
6569
this.hasReleased = false;
@@ -76,12 +80,20 @@ export async function createInstantiator(options, swift) {
7680
}
7781
}
7882
class Greeter extends SwiftHeapObject {
79-
constructor(name) {
83+
static __construct(ptr) {
84+
return new Greeter(ptr, instance.exports.bjs_Greeter_deinit);
85+
}
86+
87+
constructor(pointer, deinit) {
88+
super(pointer, deinit);
89+
}
90+
91+
static init(name) {
8092
const nameBytes = textEncoder.encode(name);
8193
const nameId = swift.memory.retain(nameBytes);
8294
const ret = instance.exports.bjs_Greeter_init(nameId, nameBytes.length);
8395
swift.memory.release(nameId);
84-
super(ret,instance.exports.bjs_Greeter_deinit);
96+
returnGreeter.__construct(ret);
8597
}
8698
greet() {
8799
instance.exports.bjs_Greeter_greet(this.pointer);
@@ -91,9 +103,17 @@ export async function createInstantiator(options, swift) {
91103
}
92104
}
93105
class Converter extends SwiftHeapObject {
94-
constructor() {
106+
static __construct(ptr) {
107+
return new Converter(ptr, instance.exports.bjs_Converter_deinit);
108+
}
109+
110+
constructor(pointer, deinit) {
111+
super(pointer, deinit);
112+
}
113+
114+
static init() {
95115
const ret = instance.exports.bjs_Converter_init();
96-
super(ret,instance.exports.bjs_Converter_deinit);
116+
returnConverter.__construct(ret);
97117
}
98118
toString(value) {
99119
instance.exports.bjs_Converter_toString(this.pointer, value);
@@ -103,6 +123,13 @@ export async function createInstantiator(options, swift) {
103123
}
104124
}
105125
class UUID extends SwiftHeapObject {
126+
static __construct(ptr) {
127+
return new UUID(ptr, instance.exports.bjs_UUID_deinit);
128+
}
129+
130+
constructor(pointer, deinit) {
131+
super(pointer, deinit);
132+
}
106133
uuidString() {
107134
instance.exports.bjs_UUID_uuidString(this.pointer);
108135
const ret = tmpRetString;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export interface Greeter extends SwiftHeapObject {
1717
}
1818
export type Exports = {
1919
Greeter: {
20-
new(name: string): Greeter;
20+
init(name: string): Greeter;
2121
}
2222
takeGreeter(greeter: Greeter): void;
2323
}

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ export async function createInstantiator(options, swift) {
6060
const js = swift.memory.heap;
6161
/// Represents a Swift heap object like a class instance or an actor instance.
6262
class SwiftHeapObject {
63+
static __construct(ptr, deinit) {
64+
return new SwiftHeapObject(ptr, deinit);
65+
}
66+
6367
constructor(pointer, deinit) {
6468
this.pointer = pointer;
6569
this.hasReleased = false;
@@ -76,12 +80,20 @@ export async function createInstantiator(options, swift) {
7680
}
7781
}
7882
class Greeter extends SwiftHeapObject {
79-
constructor(name) {
83+
static __construct(ptr) {
84+
return new Greeter(ptr, instance.exports.bjs_Greeter_deinit);
85+
}
86+
87+
constructor(pointer, deinit) {
88+
super(pointer, deinit);
89+
}
90+
91+
static init(name) {
8092
const nameBytes = textEncoder.encode(name);
8193
const nameId = swift.memory.retain(nameBytes);
8294
const ret = instance.exports.bjs_Greeter_init(nameId, nameBytes.length);
8395
swift.memory.release(nameId);
84-
super(ret,instance.exports.bjs_Greeter_deinit);
96+
returnGreeter.__construct(ret);
8597
}
8698
greet() {
8799
instance.exports.bjs_Greeter_greet(this.pointer);

‎Sources/JavaScriptKit/Documentation.docc/Articles/Exporting-Swift-to-JavaScript.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ In JavaScript:
133133
import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js";
134134
const { exports } = await init({});
135135

136-
const cart = newexports.ShoppingCart();
136+
const cart = exports.ShoppingCart.init();
137137
cart.addItem("Laptop", 999.99, 1);
138138
cart.addItem("Mouse", 24.99, 2);
139139
console.log(`Items in cart: ${cart.getItemCount()}`);
@@ -158,7 +158,7 @@ export interface ShoppingCart extends SwiftHeapObject {
158158

159159
export type Exports = {
160160
ShoppingCart: {
161-
new(): ShoppingCart;
161+
init(): ShoppingCart;
162162
}
163163
}
164164
```
@@ -175,8 +175,8 @@ You can export functions to specific namespaces by providing a namespace paramet
175175
import JavaScriptKit
176176

177177
// Export a function to a custom namespace
178-
@JS(namespace: "MyModule.Utils") func namespacedFunction() -> String {
179-
return "namespaced"
178+
@JS(namespace: "MyModule.Utils") func namespacedFunction() -> String {
179+
return "namespaced"
180180
}
181181
```
182182

‎Tests/BridgeJSRuntimeTests/ExportAPITests.swift‎

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,47 @@ struct TestError: Error {
7474
g.changeName(name: name)
7575
}
7676

77+
// Test class without @JS init constructor
78+
@JS class Calculator {
79+
nonisolated(unsafe) static var onDeinit: () -> Void = {}
80+
81+
@JS func square(value: Int) -> Int {
82+
return value * value
83+
}
84+
85+
@JS func add(a: Int, b: Int) -> Int {
86+
return a + b
87+
}
88+
89+
deinit {
90+
Self.onDeinit()
91+
}
92+
}
93+
94+
@JS func createCalculator() -> Calculator {
95+
return Calculator()
96+
}
97+
98+
@JS func useCalculator(calc: Calculator, x: Int, y: Int) -> Int {
99+
return calc.add(a: calc.square(value: x), b: y)
100+
}
101+
77102
class ExportAPITests: XCTestCase {
78103
func testAll() {
79104
var hasDeinitGreeter = false
105+
var hasDeinitCalculator = false
106+
80107
Greeter.onDeinit = {
81108
hasDeinitGreeter = true
82109
}
110+
111+
Calculator.onDeinit = {
112+
hasDeinitCalculator = true
113+
}
114+
83115
runJsWorks()
84-
XCTAssertTrue(hasDeinitGreeter)
116+
117+
XCTAssertTrue(hasDeinitGreeter, "Greeter (with @JS init) should have been deinitialized")
118+
XCTAssertTrue(hasDeinitCalculator, "Calculator (without @JS init) should have been deinitialized")
85119
}
86120
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /