Movatterモバイル変換


[0]ホーム

URL:


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

BridgeJS: Change@JS init to generatestatic init() methods instead ofconstructor in JS#409

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
kateinoigakukun merged 10 commits intomainfromyt/bjs-fix-swift-class
Aug 15, 2025
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
10 commits
Select commitHold shift + click to select a range
1279f9b
Test: Kill the wrong SwiftHeapObject returning issue
kateinoigakukunAug 15, 2025
8ebf07e
Add BridgeJS testing documentation
kateinoigakukunAug 15, 2025
fb7d54f
BridgeJS: Change @JS init to generate static init() methods instead o…
kateinoigakukunAug 15, 2025
326e593
BridgeJS: Update tests to use new static init() API
kateinoigakukunAug 15, 2025
ea03d75
BridgeJS: Update BridgeJS test snapshots
kateinoigakukunAug 15, 2025
94ec790
BridgeJS: Fix classes without @JS init constructors
kateinoigakukunAug 15, 2025
dff6231
BridgeJS: Update snapshots for consistent constructor generation
kateinoigakukunAug 15, 2025
9acbb8c
Test: Cover Swift class without @JS init
kateinoigakukunAug 15, 2025
7df801c
./Utilities/format.swift
kateinoigakukunAug 15, 2025
ed482fe
BridgeJS: Update examples and documentation for `@JS init`
kateinoigakukunAug 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletionsCONTRIBUTING.md
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -71,6 +71,18 @@ Tests for `PackageToJS` plugin:
swift test --package-path ./Plugins/PackageToJS
```

Tests for `BridgeJS` plugin:

```bash
swift test --package-path ./Plugins/BridgeJS
```

To update snapshot test files when expected output changes:

```bash
UPDATE_SNAPSHOTS=1 swift test --package-path ./Plugins/BridgeJS
```

### Editing `./Runtime` directory

The `./Runtime` directory contains the JavaScript runtime that interacts with the JavaScript environment and Swift code.
Expand Down
2 changes: 1 addition & 1 deletionExamples/ExportSwift/index.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,7 +2,7 @@ import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js";
const { exports } = await init({});

const Greeter = exports.Greeter;
const greeter =newGreeter("World");
const greeter = Greeter.init("World");
const circle = exports.renderCircleSVG(100);

// Display the results
Expand Down
4 changes: 2 additions & 2 deletionsExamples/PlayBridgeJS/Sources/JavaScript/app.js
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -52,7 +52,7 @@ export class BridgeJSPlayground {
createTS2Skeleton: this.createTS2Skeleton
}
});
this.playBridgeJS =newexports.PlayBridgeJS();
this.playBridgeJS = exports.PlayBridgeJS.init();
console.log('BridgeJS initialized successfully');
} catch (error) {
console.error('Failed to initialize BridgeJS:', error);
Expand DownExpand Up@@ -162,4 +162,4 @@ export class BridgeJSPlayground {
hideError() {
this.errorDisplay.classList.remove('show');
}
}
}
26 changes: 22 additions & 4 deletionsPlugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -43,6 +43,10 @@ struct BridgeJSLink {
let swiftHeapObjectClassJs = """
/// Represents a Swift heap object like a class instance or an actor instance.
class SwiftHeapObject {
static __construct(ptr, deinit) {
return new SwiftHeapObject(ptr, deinit);
}

constructor(pointer, deinit) {
this.pointer = pointer;
this.hasReleased = false;
Expand DownExpand Up@@ -401,7 +405,7 @@ struct BridgeJSLink {
bodyLines.append("swift.memory.release(retId);")
returnExpr = "ret"
case .swiftHeapObject(let name):
bodyLines.append("const ret =new\(name)(\(call));")
bodyLines.append("const ret = \(name).__construct(\(call));")
returnExpr = "ret"
}
return returnExpr
Expand DownExpand Up@@ -484,23 +488,37 @@ struct BridgeJSLink {
dtsExportEntryLines.append("\(klass.name): {")
jsLines.append("class \(klass.name) extends SwiftHeapObject {")

// Always add __construct and constructor methods for all classes
var constructorLines: [String] = []
constructorLines.append("static __construct(ptr) {")
constructorLines.append(
"return new \(klass.name)(ptr, instance.exports.bjs_\(klass.name)_deinit);".indent(count: 4)
)
constructorLines.append("}")
constructorLines.append("")
constructorLines.append("constructor(pointer, deinit) {")
constructorLines.append("super(pointer, deinit);".indent(count: 4))
constructorLines.append("}")
jsLines.append(contentsOf: constructorLines.map { $0.indent(count: 4) })

if let constructor: ExportedConstructor = klass.constructor {
let thunkBuilder = ExportedThunkBuilder(effects: constructor.effects)
for param in constructor.parameters {
thunkBuilder.lowerParameter(param: param)
}
var funcLines: [String] = []
funcLines.append("constructor(\(constructor.parameters.map { $0.name }.joined(separator: ", "))) {")
funcLines.append("")
funcLines.append("static init(\(constructor.parameters.map { $0.name }.joined(separator: ", "))) {")
let returnExpr = thunkBuilder.callConstructor(abiName: constructor.abiName)
funcLines.append(contentsOf: thunkBuilder.bodyLines.map { $0.indent(count: 4) })
funcLines.append(contentsOf: thunkBuilder.cleanupLines.map { $0.indent(count: 4) })
funcLines.append(contentsOf: thunkBuilder.checkExceptionLines().map { $0.indent(count: 4) })
funcLines.append("super(\(returnExpr), instance.exports.bjs_\(klass.name)_deinit);".indent(count: 4))
funcLines.append("return\(klass.name).__construct(\(returnExpr));".indent(count: 4))
funcLines.append("}")
jsLines.append(contentsOf: funcLines.map { $0.indent(count: 4) })

dtsExportEntryLines.append(
"new\(renderTSSignature(parameters: constructor.parameters, returnType: .swiftHeapObject(klass.name)));"
"init\(renderTSSignature(parameters: constructor.parameters, returnType: .swiftHeapObject(klass.name)));"
.indent(count: 4)
)
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,10 +51,10 @@ export interface UUID extends SwiftHeapObject {
}
export type Exports = {
Greeter: {
new(name: string): Greeter;
init(name: string): Greeter;
}
Converter: {
new(): Converter;
init(): Converter;
}
UUID: {
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,10 @@ export async function createInstantiator(options, swift) {
const js = swift.memory.heap;
/// Represents a Swift heap object like a class instance or an actor instance.
class SwiftHeapObject {
static __construct(ptr, deinit) {
return new SwiftHeapObject(ptr, deinit);
}

constructor(pointer, deinit) {
this.pointer = pointer;
this.hasReleased = false;
Expand All@@ -76,12 +80,20 @@ export async function createInstantiator(options, swift) {
}
}
class Greeter extends SwiftHeapObject {
constructor(name) {
static __construct(ptr) {
return new Greeter(ptr, instance.exports.bjs_Greeter_deinit);
}

constructor(pointer, deinit) {
super(pointer, deinit);
}

static init(name) {
const nameBytes = textEncoder.encode(name);
const nameId = swift.memory.retain(nameBytes);
const ret = instance.exports.bjs_Greeter_init(nameId, nameBytes.length);
swift.memory.release(nameId);
super(ret, instance.exports.bjs_Greeter_deinit);
return Greeter.__construct(ret);
}
greet() {
instance.exports.bjs_Greeter_greet(this.pointer);
Expand All@@ -91,9 +103,17 @@ export async function createInstantiator(options, swift) {
}
}
class Converter extends SwiftHeapObject {
constructor() {
static __construct(ptr) {
return new Converter(ptr, instance.exports.bjs_Converter_deinit);
}

constructor(pointer, deinit) {
super(pointer, deinit);
}

static init() {
const ret = instance.exports.bjs_Converter_init();
super(ret, instance.exports.bjs_Converter_deinit);
return Converter.__construct(ret);
}
toString(value) {
instance.exports.bjs_Converter_toString(this.pointer, value);
Expand All@@ -103,6 +123,13 @@ export async function createInstantiator(options, swift) {
}
}
class UUID extends SwiftHeapObject {
static __construct(ptr) {
return new UUID(ptr, instance.exports.bjs_UUID_deinit);
}

constructor(pointer, deinit) {
super(pointer, deinit);
}
uuidString() {
instance.exports.bjs_UUID_uuidString(this.pointer);
const ret = tmpRetString;
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,7 +17,7 @@ export interface Greeter extends SwiftHeapObject {
}
export type Exports = {
Greeter: {
new(name: string): Greeter;
init(name: string): Greeter;
}
takeGreeter(greeter: Greeter): void;
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,6 +60,10 @@ export async function createInstantiator(options, swift) {
const js = swift.memory.heap;
/// Represents a Swift heap object like a class instance or an actor instance.
class SwiftHeapObject {
static __construct(ptr, deinit) {
return new SwiftHeapObject(ptr, deinit);
}

constructor(pointer, deinit) {
this.pointer = pointer;
this.hasReleased = false;
Expand All@@ -76,12 +80,20 @@ export async function createInstantiator(options, swift) {
}
}
class Greeter extends SwiftHeapObject {
constructor(name) {
static __construct(ptr) {
return new Greeter(ptr, instance.exports.bjs_Greeter_deinit);
}

constructor(pointer, deinit) {
super(pointer, deinit);
}

static init(name) {
const nameBytes = textEncoder.encode(name);
const nameId = swift.memory.retain(nameBytes);
const ret = instance.exports.bjs_Greeter_init(nameId, nameBytes.length);
swift.memory.release(nameId);
super(ret, instance.exports.bjs_Greeter_deinit);
return Greeter.__construct(ret);
}
greet() {
instance.exports.bjs_Greeter_greet(this.pointer);
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -133,7 +133,7 @@ In #"d6fc44ff4a9d868feefff852956d365796ffc4eaa0f1e6ab548ba920904454ed">import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js";
const { exports } = await init({});

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

export type Exports = {
ShoppingCart: {
new(): ShoppingCart;
init(): ShoppingCart;
}
}
```
Expand All@@ -175,8 +175,8 @@ You can export functions to specific namespaces by providing a namespace paramet
import JavaScriptKit

// Export a function to a custom namespace
@JS(namespace: "MyModule.Utils") func namespacedFunction() -> String {
return "namespaced"
@JS(namespace: "MyModule.Utils") func namespacedFunction() -> String {
return "namespaced"
}
```

Expand Down
36 changes: 35 additions & 1 deletionTests/BridgeJSRuntimeTests/ExportAPITests.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,13 +74,47 @@ struct TestError: Error {
g.changeName(name: name)
}

// Test class without @JS init constructor
@JS class Calculator {
nonisolated(unsafe) static var onDeinit: () -> Void = {}

@JS func square(value: Int) -> Int {
return value * value
}

@JS func add(a: Int, b: Int) -> Int {
return a + b
}

deinit {
Self.onDeinit()
}
}

@JS func createCalculator() -> Calculator {
return Calculator()
}

@JS func useCalculator(calc: Calculator, x: Int, y: Int) -> Int {
return calc.add(a: calc.square(value: x), b: y)
}

class ExportAPITests: XCTestCase {
func testAll() {
var hasDeinitGreeter = false
var hasDeinitCalculator = false

Greeter.onDeinit = {
hasDeinitGreeter = true
}

Calculator.onDeinit = {
hasDeinitCalculator = true
}

runJsWorks()
XCTAssertTrue(hasDeinitGreeter)

XCTAssertTrue(hasDeinitGreeter, "Greeter (with @JS init) should have been deinitialized")
XCTAssertTrue(hasDeinitCalculator, "Calculator (without @JS init) should have been deinitialized")
}
}
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -314,6 +314,28 @@ public func _bjs_takeGreeter(g: UnsafeMutableRawPointer, nameBytes: Int32, nameL
#endif
}

@_expose(wasm, "bjs_createCalculator")
@_cdecl("bjs_createCalculator")
public func _bjs_createCalculator() -> UnsafeMutableRawPointer {
#if arch(wasm32)
let ret = createCalculator()
return Unmanaged.passRetained(ret).toOpaque()
#else
fatalError("Only available on WebAssembly")
#endif
}

@_expose(wasm, "bjs_useCalculator")
@_cdecl("bjs_useCalculator")
public func _bjs_useCalculator(calc: UnsafeMutableRawPointer, x: Int32, y: Int32) -> Int32 {
#if arch(wasm32)
let ret = useCalculator(calc: Unmanaged<Calculator>.fromOpaque(calc).takeUnretainedValue(), x: Int(x), y: Int(y))
return Int32(ret)
#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 {
Expand DownExpand Up@@ -360,4 +382,32 @@ public func _bjs_Greeter_changeName(_self: UnsafeMutableRawPointer, nameBytes: I
@_cdecl("bjs_Greeter_deinit")
public func _bjs_Greeter_deinit(pointer: UnsafeMutableRawPointer) {
Unmanaged<Greeter>.fromOpaque(pointer).release()
}

@_expose(wasm, "bjs_Calculator_square")
@_cdecl("bjs_Calculator_square")
public func _bjs_Calculator_square(_self: UnsafeMutableRawPointer, value: Int32) -> Int32 {
#if arch(wasm32)
let ret = Unmanaged<Calculator>.fromOpaque(_self).takeUnretainedValue().square(value: Int(value))
return Int32(ret)
#else
fatalError("Only available on WebAssembly")
#endif
}

@_expose(wasm, "bjs_Calculator_add")
@_cdecl("bjs_Calculator_add")
public func _bjs_Calculator_add(_self: UnsafeMutableRawPointer, a: Int32, b: Int32) -> Int32 {
#if arch(wasm32)
let ret = Unmanaged<Calculator>.fromOpaque(_self).takeUnretainedValue().add(a: Int(a), b: Int(b))
return Int32(ret)
#else
fatalError("Only available on WebAssembly")
#endif
}

@_expose(wasm, "bjs_Calculator_deinit")
@_cdecl("bjs_Calculator_deinit")
public func _bjs_Calculator_deinit(pointer: UnsafeMutableRawPointer) {
Unmanaged<Calculator>.fromOpaque(pointer).release()
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp