Skip to content

Commit 88ac0b3

Browse files
committed
chore: Minor updates to BridgeJS documentation
1 parent 5946e80 commit 88ac0b3

File tree

11 files changed

+248
-242
lines changed

11 files changed

+248
-242
lines changed

Plugins/BridgeJS/README.md

Lines changed: 77 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
> This feature is still experimental, and the API may change frequently. Use at your own risk with `JAVASCRIPTKIT_EXPERIMENTAL_BRIDGEJS=1` environment variable.
55
66
> [!NOTE]
7-
> This documentation is intended for JavaScriptKit developers, not JavaScriptKit users.
7+
> This documentation is intended for JavaScriptKit developers, not JavaScriptKit users. For user documentation, see [Exporting Swift to JavaScript](https://swiftpackageindex.com/swiftwasm/JavaScriptKit/documentation/javascriptkit/exporting-swift-to-javascript) and [Importing TypeScript into Swift](https://swiftpackageindex.com/swiftwasm/JavaScriptKit/documentation/javascriptkit/importing-typescript-into-swift).
88
99
## Overview
1010

@@ -14,6 +14,7 @@ BridgeJS provides easy interoperability between Swift and JavaScript/TypeScript.
1414
2. **Exporting Swift APIs to JavaScript**: Make your Swift APIs available to JavaScript code
1515

1616
The workflow is:
17+
1718
1. `ts2swift` converts TypeScript definitions (`bridge-js.d.ts`) to macro-annotated Swift declarations (`BridgeJS.Macros.swift`)
1819
2. `bridge-js generate` processes both Swift source files (for export) and macro-annotated Swift files (for import) to generate:
1920
- `BridgeJS.swift` (Swift glue code)
@@ -59,27 +60,53 @@ graph LR
5960

6061
## Type Mapping
6162

62-
### Primitive Type Conversions
63-
64-
TBD
65-
66-
| Swift Type | JS Type | Wasm Core Type |
67-
|:--------------|:-----------|:---------------|
68-
| `Int` | `number` | `i32` |
69-
| `UInt` | `number` | `i32` |
70-
| `Int8` | `number` | `i32` |
71-
| `UInt8` | `number` | `i32` |
72-
| `Int16` | `number` | `i32` |
73-
| `UInt16` | `number` | `i32` |
74-
| `Int32` | `number` | `i32` |
75-
| `UInt32` | `number` | `i32` |
76-
| `Int64` | `bigint` | `i64` |
77-
| `UInt64` | `bigint` | `i64` |
78-
| `Float` | `number` | `f32` |
79-
| `Double` | `number` | `f64` |
80-
| `Bool` | `boolean` | `i32` |
81-
| `Void` | `void` | - |
82-
| `String` | `string` | `i32` |
63+
### Primitive Types
64+
65+
| Swift Type | TypeScript Type | Wasm Core Type |
66+
|:--------------|:----------------|:---------------|
67+
| `Int` | `number` | `i32` |
68+
| `UInt` | `number` | `i32` |
69+
| `Int8` | `number` | `i32` |
70+
| `UInt8` | `number` | `i32` |
71+
| `Int16` | `number` | `i32` |
72+
| `UInt16` | `number` | `i32` |
73+
| `Int32` | `number` | `i32` |
74+
| `UInt32` | `number` | `i32` |
75+
| `Int64` | `bigint` | `i64` |
76+
| `UInt64` | `bigint` | `i64` |
77+
| `Float` | `number` | `f32` |
78+
| `Double` | `number` | `f64` |
79+
| `Bool` | `boolean` | `i32` |
80+
| `Void` | `void` | - |
81+
82+
### Complex Types
83+
84+
| Swift Type | TypeScript Type | Semantics | Status |
85+
|:-----------|:----------------|:----------|:-------|
86+
| `String` | `string` | Copy ||
87+
| `@JS class` | `interface` + constructor | Reference (pointer) ||
88+
| `@JS struct` | `interface` | Copy (fields via stacks) ||
89+
| `@JS enum` (case) | const object + tag type | Copy (integer) ||
90+
| `@JS enum` (raw value) | const object + tag type | Copy (raw value) ||
91+
| `@JS enum` (associated) | discriminated union | Copy (fields via stacks) ||
92+
| `@JS protocol` | `interface` | Reference (wrapper) ||
93+
| `Optional<T>` | `T \| null` | Depends on T ||
94+
| `(T) -> U` | `(arg: T) => U` | Reference (boxed) ||
95+
| `JSObject` | `any` / `object` | Reference ||
96+
| `Array<T>` | `T[]` | Copy ||
97+
| `Array<Array<T>>` | `T[][]` | Copy ||
98+
| `Dictionary<K, V>` | `Record<K, V>` | - | [#495](https://github.com/swiftwasm/JavaScriptKit/issues/495) |
99+
| `Set<T>` | `Set<T>` | - | [#397](https://github.com/swiftwasm/JavaScriptKit/issues/397) |
100+
| `Foundation.URL` | `string` | - | [#496](https://github.com/swiftwasm/JavaScriptKit/issues/496) |
101+
| Generics | - | - | [#398](https://github.com/swiftwasm/JavaScriptKit/issues/398) |
102+
103+
### Import-specific (TypeScript → Swift)
104+
105+
| TypeScript Type | Swift Type | Status |
106+
|:----------------|:-----------|:-------|
107+
| `T \| null` | `Optional<T>` | [#475](https://github.com/swiftwasm/JavaScriptKit/issues/475) |
108+
| `T \| undefined` | `Optional<T>` | [#475](https://github.com/swiftwasm/JavaScriptKit/issues/475) |
109+
| `enum` | `@JS enum` | [#489](https://github.com/swiftwasm/JavaScriptKit/issues/489) |
83110

84111
## Type Modeling
85112

@@ -109,23 +136,37 @@ The ABI will not be stable, and not meant to be interposed by other tools.
109136

110137
### Parameter Passing
111138

112-
Parameter passing follows Wasm calling conventions, with custom handling for complex types like strings and objects.
139+
Parameter passing follows Wasm calling conventions, with custom handling for complex types:
113140

114-
TBD
141+
- **Primitives**: Passed directly as Wasm arguments (`i32`, `i64`, `f32`, `f64`)
142+
- **Strings**: UTF-8 bytes stored in `swift.memory`, ID + length passed as Wasm arguments
143+
- **Swift Classes**: Raw Swift heap pointer passed as `i32`
144+
- **JSObjects**: Object stored in `swift.memory.heap`, object ID passed as `i32`
145+
- **Structs/Arrays**: Fields/elements pushed to type-specific stacks, Swift pops in reverse order
146+
- **Closures**: Boxed and retained in memory, handle passed as `i32`
115147

116148
### Return Values
117149

118-
TBD
150+
Return values use direct Wasm returns for primitives, and imported intrinsic functions for complex types:
151+
152+
- **Primitives**: Returned directly via Wasm return value
153+
- **Strings**: Swift writes UTF-8 bytes to shared memory, JS decodes
154+
- **Swift Classes**: Pointer returned directly, JS wraps in `SwiftHeapObject` with `FinalizationRegistry`
155+
- **Structs/Arrays**: Swift pushes fields/elements to type-specific stacks, JS reconstructs
156+
157+
### Memory Management
158+
159+
- **Swift Classes**: Live on Swift heap. JS holds pointer wrapped in `SwiftHeapObject`. `FinalizationRegistry` calls `deinit` on GC. Optional `release()` for deterministic cleanup.
160+
- **JSObjects**: Live in `swift.memory.heap` (JS side). Swift holds ID wrapped in `JSObject`. Reference counted via `retain`/`release`.
161+
- **Structs/Arrays/Enums**: Copy semantics - data serialized across boundary. No cleanup needed.
162+
- **Closures**: Boxed on source side, released when GC'd on either side.
163+
164+
For detailed semantics, see the [How It Works sections](https://swiftpackageindex.com/swiftwasm/JavaScriptKit/documentation/javascriptkit/exporting-swift-class#How-It-Works) in the user documentation.
119165

120166
## Future Work
121167

122-
- [ ] Struct on parameter or return type
123-
- [ ] Throws functions
124-
- [ ] Async functions
125168
- [ ] Cast between TS interface
126-
- [ ] Closure support
127-
- [ ] Simplify constructor pattern
128-
* https://github.com/ocsigen/ts2ocaml/blob/main/docs/js_of_ocaml.md#feature-immediate-constructor
169+
- [ ] Simplify constructor pattern ([reference](https://github.com/ocsigen/ts2ocaml/blob/main/docs/js_of_ocaml.md#feature-immediate-constructor))
129170
```typescript
130171
interface Foo = {
131172
someMethod(value: number): void;
@@ -141,3 +182,8 @@ TBD
141182
```
142183
- [ ] Use `externref` once it's widely available
143184
- [ ] Test SwiftObject roundtrip
185+
- [ ] Import TS `enum` as Swift enum ([#489](https://github.com/swiftwasm/JavaScriptKit/issues/489))
186+
- [ ] Support `T | null` and `T | undefined` imports ([#475](https://github.com/swiftwasm/JavaScriptKit/issues/475))
187+
- [ ] Support `@JS var` for global scope imports ([#466](https://github.com/swiftwasm/JavaScriptKit/issues/466))
188+
- [ ] Support `export { thing } from 'pkg'` form ([#437](https://github.com/swiftwasm/JavaScriptKit/issues/437))
189+
- [ ] Support imported TS type usage on exported interface ([#497](https://github.com/swiftwasm/JavaScriptKit/issues/497))

Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Class.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,17 @@ export type Exports = {
7676
}
7777
```
7878
79+
## How It Works
80+
81+
Classes use **reference semantics** when crossing the Swift/JavaScript boundary:
82+
83+
1. **Object Creation**: When you create a class instance (via `new` in JS), the object lives on the Swift heap
84+
2. **Reference Passing**: JavaScript receives a reference (handle) to the Swift object, not a copy
85+
3. **Shared State**: Changes made through either Swift or JavaScript affect the same object
86+
4. **Memory Management**: `FinalizationRegistry` automatically releases Swift objects when they're garbage collected in JavaScript. You can optionally call `release()` for deterministic cleanup.
87+
88+
This differs from structs, which use copy semantics and transfer data by value.
89+
7990
## Supported Features
8091
8192
| Swift Feature | Status |

Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Closure.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Exporting Swift Closures
1+
# Exporting Swift Closures to JS
22

33
Learn how to use closure/function types as parameters and return values in BridgeJS.
44

@@ -89,18 +89,20 @@ export type Exports = {
8989
}
9090
```
9191
92-
## Memory Management
92+
## How It Works
9393
94-
When JavaScript passes a function to Swift, it's automatically stored in `swift.memory` with reference counting. When Swift's closure wrapper is deallocated by ARC, the JavaScript function is released.
94+
Closures use **reference semantics** when crossing the Swift/JavaScript boundary:
9595
96-
When Swift returns a closure to JavaScript, the Swift closure is boxed and automatically released when the JavaScript function is garbage collected.
96+
1. **JavaScript → Swift**: When JavaScript passes a function to Swift, it's stored in `swift.memory` with reference counting. When Swift's closure wrapper is deallocated by ARC, the JavaScript function is released.
97+
2. **Swift → JavaScript**: When Swift returns a closure to JavaScript, the Swift closure is boxed and automatically released when the JavaScript function is garbage collected.
98+
3. **Memory Management**: Both directions use automatic memory management via `FinalizationRegistry` - no manual cleanup required.
9799
98-
Both directions use automatic memory management - no manual cleanup required.
100+
This differs from structs and arrays, which use copy semantics and transfer data by value.
99101
100102
## Supported Features
101103
102-
| Feature | Status |
103-
|:--------|:-------|
104+
| Swift Feature | Status |
105+
|:--------------|:-------|
104106
| Closure Parameters with Supported Types `(String, Int) -> Person` | ✅ |
105107
| Closure Return Value with Supported Types `() -> Person` | ✅ |
106108
| `@escaping` closures | ✅ |

Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Default-Parameters.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ custom.release();
143143
```
144144

145145
**Requirements:**
146+
146147
- Constructor/initializer arguments must be literal values (`"text"`, `42`, `true`, `false`, `nil`)
147148
- Struct initializers must use labeled arguments (e.g., `Point(x: 1.0, y: 2.0)`)
148149
- Complex expressions, computed properties, or method calls are not supported

0 commit comments

Comments
 (0)