@expo/expo-modules-macros-plugin 0.6.2 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apple/ExpoModulesMacros-tool +0 -0
- package/apple/Sources/ExpoModulesMacros/DecorateModuleBuilder.swift +59 -34
- package/apple/Sources/ExpoModulesMacros/JSMacro.swift +0 -8
- package/apple/Sources/ExpoModulesMacros/MacroHelpers.swift +9 -0
- package/apple/Sources/ExpoModulesMacros/Receiver.swift +39 -21
- package/apple/Sources/ExpoModulesMacros/SharedObjectMacro.swift +40 -14
- package/package.json +1 -1
|
Binary file
|
|
@@ -2,7 +2,7 @@ import SwiftSyntax
|
|
|
2
2
|
|
|
3
3
|
/// A `@JS func` collected for **direct JSI binding**. Instead of describing the function with a
|
|
4
4
|
/// `Function(...)` / `AsyncFunction(...)` DSL entry that the runtime interprets per call, the enclosing
|
|
5
|
-
/// macro synthesizes a decorator (`_decorateModule` / `_decorateSharedObject`) that binds each such
|
|
5
|
+
/// macro synthesizes a decorator (`_decorateModule(object:)` / `_decorateSharedObject(prototype:)`) that binds each such
|
|
6
6
|
/// function into the JS object via the closure-taking `JavaScriptObject.setProperty(_:)`, with the
|
|
7
7
|
/// decode-call-encode body inlined into the closure. This omits the `[Any]`/`toTuple` dynamic-call path:
|
|
8
8
|
/// every argument is decoded individually by its static type.
|
|
@@ -177,11 +177,26 @@ internal struct JSFunction {
|
|
|
177
177
|
/// and throws instead of silently encoding an out-of-safe-range value as a lossy number — the
|
|
178
178
|
/// catchable error is the right behavior, and matches how non-primitive integers already encode. A
|
|
179
179
|
/// no-return function returns `.undefined` instead.
|
|
180
|
+
///
|
|
181
|
+
/// For an `async` function the encode must run on the JS thread. An `async` body may suspend and
|
|
182
|
+
/// resume on an arbitrary cooperative-pool thread, and encoding a heap-allocated JS value (a string,
|
|
183
|
+
/// object, array, or typed array) off the JS thread races the engine's garbage collector and
|
|
184
|
+
/// corrupts the heap. `runtime.execute` hops the encode onto the JS thread, running inline when it is
|
|
185
|
+
/// already there (the common case, where the body never truly suspended), so synchronous functions —
|
|
186
|
+
/// which always run on the JS thread — keep encoding directly without the wrapper.
|
|
180
187
|
private func encodeResultLines() -> [String] {
|
|
181
188
|
guard let returnType else {
|
|
182
189
|
return ["return .undefined"]
|
|
183
190
|
}
|
|
184
|
-
|
|
191
|
+
let encode = "try \(expressionType(returnType)).encode(result, in: runtime)"
|
|
192
|
+
if isAsync {
|
|
193
|
+
return [
|
|
194
|
+
"return try await runtime.execute {",
|
|
195
|
+
" return \(encode)",
|
|
196
|
+
"}",
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
return ["return \(encode)"]
|
|
185
200
|
}
|
|
186
201
|
|
|
187
202
|
/// The `setProperty` statement that installs this function on the JS object. The decode-call-encode
|
|
@@ -194,7 +209,7 @@ internal struct JSFunction {
|
|
|
194
209
|
/// the host-function closure is what keeps the native callable alive for as long as JS can invoke
|
|
195
210
|
/// it; its lifetime is bounded by the JS VM's garbage collection of the object. A shared object
|
|
196
211
|
/// captures nothing of the instance: it recovers the typed receiver from the JS `this` per call.
|
|
197
|
-
func decorateStatements(receiver: Receiver) -> String {
|
|
212
|
+
func decorateStatements(object: String, receiver: Receiver) -> String {
|
|
198
213
|
// Synchronous `@JS` bindings bind through the unowned-`this` `setProperty` overload, which hands
|
|
199
214
|
// `this` in as a borrowed `JavaScriptUnownedValue` instead of allocating an owning
|
|
200
215
|
// `JavaScriptValue` and forming its `weak`-runtime reference on every call. A module ignores
|
|
@@ -210,7 +225,6 @@ internal struct JSFunction {
|
|
|
210
225
|
? "this, arguments"
|
|
211
226
|
: "(this: borrowing JavaScriptUnownedValue, arguments: consuming JavaScriptValuesBuffer)"
|
|
212
227
|
|
|
213
|
-
let object = receiver.decoratedObject
|
|
214
228
|
return """
|
|
215
229
|
\(object).setProperty("\(jsName)") { \(captures)\(parameters) in
|
|
216
230
|
\(bodyStatements(receiver: receiver, indent: " "))
|
|
@@ -221,7 +235,7 @@ internal struct JSFunction {
|
|
|
221
235
|
|
|
222
236
|
/// A `@JS var` collected for **direct JSI binding**. Instead of describing the property with a
|
|
223
237
|
/// `Property(...)` DSL entry, the enclosing macro synthesizes a get/set accessor into the JS object
|
|
224
|
-
/// inside its decorator (`_decorateModule` / `_decorateSharedObject`): it builds a descriptor object
|
|
238
|
+
/// inside its decorator (`_decorateModule(object:)` / `_decorateSharedObject(prototype:)`): it builds a descriptor object
|
|
225
239
|
/// (`enumerable` + `get`, and `set` when the property is settable) and installs it with
|
|
226
240
|
/// `object.defineProperty(name, descriptor:)`, mirroring core's `PropertyDefinition.buildDescriptor`.
|
|
227
241
|
/// The `get`/`set` host functions are installed the same way `@JS func`s are — the closure-taking
|
|
@@ -248,10 +262,9 @@ internal struct JSProperty {
|
|
|
248
262
|
/// closure — and installs it with `object.defineProperty(name, descriptor:)`. Capture matches the
|
|
249
263
|
/// function bindings: a module captures `self` strong, a shared object captures nothing of the
|
|
250
264
|
/// instance. Getter and setter are gated independently.
|
|
251
|
-
func decorateStatements(receiver: Receiver) -> String {
|
|
265
|
+
func decorateStatements(object: String, receiver: Receiver) -> String {
|
|
252
266
|
let descriptorName = "\(swiftName)Descriptor"
|
|
253
267
|
let callee = receiver.callee
|
|
254
|
-
let object = receiver.decoratedObject
|
|
255
268
|
// A shared object's accessors unwrap the JS `this` into `_self` before reading/writing; a module
|
|
256
269
|
// reads `self` directly. The unwrap leads each accessor body. Property accessors are synchronous, so
|
|
257
270
|
// they take the borrowed unowned `this`.
|
|
@@ -319,24 +332,30 @@ internal struct JSProperty {
|
|
|
319
332
|
}
|
|
320
333
|
}
|
|
321
334
|
|
|
322
|
-
/// The body shared by
|
|
323
|
-
/// and every `@JS var` via a `defineProperty` accessor, joined for the function body.
|
|
324
|
-
///
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
335
|
+
/// The body shared by every decorator phase: every `@JS func` bound via an inlined `setProperty`
|
|
336
|
+
/// closure and every `@JS var` via a `defineProperty` accessor, joined for the function body. `object`
|
|
337
|
+
/// is the local the bindings decorate (matching the entry point's argument label); `receiver` selects
|
|
338
|
+
/// how each binding reaches its Swift value (module `self`, shared-object instance `_self`, or the
|
|
339
|
+
/// metatype for a static member).
|
|
340
|
+
private func decorateBody(
|
|
341
|
+
functions: [JSFunction], properties: [JSProperty], object: String, receiver: Receiver
|
|
342
|
+
) -> String {
|
|
343
|
+
let functionBody = functions.map { $0.decorateStatements(object: object, receiver: receiver) }
|
|
344
|
+
let propertyBody = properties.map { $0.decorateStatements(object: object, receiver: receiver) }
|
|
328
345
|
return (functionBody + propertyBody).joined(separator: "\n")
|
|
329
346
|
}
|
|
330
347
|
|
|
331
|
-
/// The
|
|
332
|
-
///
|
|
333
|
-
///
|
|
334
|
-
///
|
|
335
|
-
///
|
|
336
|
-
/// the
|
|
337
|
-
///
|
|
348
|
+
/// The module decorator: a `_decorateModule(object:)` that binds every `@JS func` (via an inlined
|
|
349
|
+
/// `setProperty` closure) and every `@JS var` (via a `defineProperty` accessor) into the module's own
|
|
350
|
+
/// JS object. Core supplies the object; the bindings call into the module `self`. It's an *instance*
|
|
351
|
+
/// method (a module is a singleton, satisfying the `AnyModule` requirement of the same name), mirroring
|
|
352
|
+
/// core's `ObjectDefinition.decorate(object:)` including the `borrowing` object parameter (it mutates
|
|
353
|
+
/// through the reference without reassigning or taking ownership). Only emitted when there's at least
|
|
354
|
+
/// one member to bind. Uses the shared body generation with the module `object:` phase and `self`
|
|
355
|
+
/// receiver; the shared-object counterpart is `buildDecorateSharedObjectPhase`.
|
|
338
356
|
internal func buildDecorateJavaScriptObject(functions: [JSFunction], properties: [JSProperty]) -> DeclSyntax {
|
|
339
|
-
let body = decorateBody(
|
|
357
|
+
let body = decorateBody(
|
|
358
|
+
functions: functions, properties: properties, object: Phase.object.rawValue, receiver: .module)
|
|
340
359
|
return """
|
|
341
360
|
@JavaScriptActor
|
|
342
361
|
public func _decorateModule(object: borrowing JavaScriptObject, in runtime: JavaScriptRuntime) throws {
|
|
@@ -345,22 +364,28 @@ internal func buildDecorateJavaScriptObject(functions: [JSFunction], properties:
|
|
|
345
364
|
"""
|
|
346
365
|
}
|
|
347
366
|
|
|
348
|
-
///
|
|
349
|
-
///
|
|
350
|
-
///
|
|
351
|
-
///
|
|
352
|
-
///
|
|
353
|
-
///
|
|
354
|
-
///
|
|
355
|
-
///
|
|
356
|
-
/// `@JS
|
|
357
|
-
|
|
358
|
-
|
|
367
|
+
/// A shared-object decorator phase, a label overload of `_decorateSharedObject` overriding the matching
|
|
368
|
+
/// `open class func` on the `SharedObject` base so core can dispatch through the concrete type's
|
|
369
|
+
/// metatype. The `prototype:` overload binds the type's instance `@JS func`/`var` members onto the class
|
|
370
|
+
/// prototype (the original hook, unchanged); the `constructor:` overload binds the `static`/`class` ones
|
|
371
|
+
/// onto the constructor function itself (a new, additive overload, so introducing it isn't a breaking
|
|
372
|
+
/// core change). Both are *static* (a shared object has no singleton `self`). An instance binding
|
|
373
|
+
/// recovers its typed receiver from the JS `this` per call (`SharedObject.native(from:as:)`); a static
|
|
374
|
+
/// binding calls the Swift member on the type and ignores `this` (which, on the static side, is the
|
|
375
|
+
/// constructor). The constructor *object* the static members decorate is distinct from the `@JS init`,
|
|
376
|
+
/// which builds an instance and is emitted separately (see `JSConstructor.buildConstructor`). Each phase
|
|
377
|
+
/// is emitted only when the type has a member for it.
|
|
378
|
+
internal func buildDecorateSharedObjectPhase(
|
|
379
|
+
phase: Phase, functions: [JSFunction], properties: [JSProperty], typeName: String
|
|
359
380
|
) -> DeclSyntax {
|
|
360
|
-
let
|
|
381
|
+
let receiver: Receiver = phase == .constructor
|
|
382
|
+
? .staticMember(typeName: typeName)
|
|
383
|
+
: .sharedObject(typeName: typeName)
|
|
384
|
+
let body = decorateBody(
|
|
385
|
+
functions: functions, properties: properties, object: phase.rawValue, receiver: receiver)
|
|
361
386
|
return """
|
|
362
387
|
@JavaScriptActor
|
|
363
|
-
public override class func _decorateSharedObject(
|
|
388
|
+
public override class func _decorateSharedObject(\(raw: phase.rawValue): borrowing JavaScriptObject, in runtime: JavaScriptRuntime) throws {
|
|
364
389
|
\(raw: body)
|
|
365
390
|
}
|
|
366
391
|
"""
|
|
@@ -98,14 +98,6 @@ private func boundaryMember(of declaration: some DeclSyntaxProtocol) -> Boundary
|
|
|
98
98
|
return nil
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
/// True when the modifiers make the member type-level (`static` or `class`), so its assertion peer
|
|
102
|
-
/// must be emitted in the same metatype context rather than as an instance member.
|
|
103
|
-
private func isTypeLevel(_ modifiers: DeclModifierListSyntax) -> Bool {
|
|
104
|
-
return modifiers.contains {
|
|
105
|
-
$0.name.tokenKind == .keyword(.static) || $0.name.tokenKind == .keyword(.class)
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
101
|
/// True when a return clause is written as `Void` / `()` — nothing crosses the boundary, so it needs
|
|
110
102
|
/// no conformance assertion. (A missing return clause never reaches here: `returnClause` is `nil`.)
|
|
111
103
|
private func isVoidType(_ type: TypeSyntax) -> Bool {
|
|
@@ -43,6 +43,15 @@ internal func isOptionalType(_ type: TypeSyntax) -> Bool {
|
|
|
43
43
|
return false
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/// True when the modifiers make the member type-level (`static` or `class`). A shared object routes
|
|
47
|
+
/// such members to its constructor (JS-static) and instance members to its prototype; the `@JS`
|
|
48
|
+
/// conformance assertion also uses this to emit its peer in the matching metatype context.
|
|
49
|
+
internal func isTypeLevel(_ modifiers: DeclModifierListSyntax) -> Bool {
|
|
50
|
+
return modifiers.contains {
|
|
51
|
+
$0.name.tokenKind == .keyword(.static) || $0.name.tokenKind == .keyword(.class)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
46
55
|
/// True if a trailing occurrence of this parameter may be omitted by the JS caller: it either has a
|
|
47
56
|
/// default value (Swift applies it) or is an optional type (an absent slot becomes `nil`). The arity
|
|
48
57
|
/// range and the per-arity call branches are derived from this.
|
|
@@ -1,47 +1,50 @@
|
|
|
1
1
|
import SwiftSyntax
|
|
2
2
|
|
|
3
|
-
/// Where a directly-bound closure gets the Swift value it calls into.
|
|
4
|
-
///
|
|
5
|
-
///
|
|
3
|
+
/// Where a directly-bound closure gets the Swift value it calls into. This is one of the two
|
|
4
|
+
/// orthogonal axes of a binding; the other is the `Phase` (which JS object the member is installed
|
|
5
|
+
/// on). The two are independent: choosing the prototype phase does not by itself decide whether the
|
|
6
|
+
/// receiver is `self`, `_self`, or the metatype.
|
|
7
|
+
///
|
|
8
|
+
/// - A module is a singleton, so its bindings call `self` and ignore the JS `this`.
|
|
9
|
+
/// - A shared-object *instance* member has a distinct native instance per JS object, so it recovers
|
|
10
|
+
/// the typed receiver from `this`.
|
|
11
|
+
/// - A `static`/`class` member has no instance at all; it calls the Swift member on the metatype
|
|
12
|
+
/// (`Cache.open(…)`) and ignores `this` (which, on the static side, is the constructor).
|
|
6
13
|
internal enum Receiver {
|
|
7
14
|
/// The module singleton; the closure captures `self` strong.
|
|
8
15
|
case module
|
|
9
16
|
/// A shared object of the given concrete type; the closure captures nothing and recovers the receiver
|
|
10
17
|
/// from `this` per call.
|
|
11
18
|
case sharedObject(typeName: String)
|
|
19
|
+
/// A `static`/`class` member of the given concrete type; the closure captures nothing and calls the
|
|
20
|
+
/// Swift member on the type itself, ignoring `this`.
|
|
21
|
+
case staticMember(typeName: String)
|
|
12
22
|
|
|
13
23
|
/// The expression the body calls members on: `self` for a module, `_self` (bound by `unwrapStatement`)
|
|
14
|
-
/// for a shared
|
|
24
|
+
/// for a shared-object instance, the type name for a static member. The leading underscore on `_self`
|
|
25
|
+
/// avoids colliding with a user member like `var owner`.
|
|
15
26
|
var callee: String {
|
|
16
27
|
switch self {
|
|
17
28
|
case .module:
|
|
18
29
|
return "self"
|
|
19
30
|
case .sharedObject:
|
|
20
31
|
return "_self"
|
|
32
|
+
case .staticMember(let typeName):
|
|
33
|
+
return typeName
|
|
21
34
|
}
|
|
22
35
|
}
|
|
23
36
|
|
|
24
|
-
/// The
|
|
25
|
-
///
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
case .module:
|
|
29
|
-
return "object"
|
|
30
|
-
case .sharedObject:
|
|
31
|
-
return "prototype"
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/// The leading body line binding the receiver, or `nil` for a module (it reads `self` directly). For a
|
|
36
|
-
/// shared object, `native(from:as:)` recovers the typed instance from `this`, throwing on a foreign
|
|
37
|
-
/// object or a type mismatch.
|
|
37
|
+
/// The leading body line binding the receiver, or `nil` when nothing needs to be unwrapped (a module
|
|
38
|
+
/// reads `self` directly; a static member calls the type directly). For a shared-object instance,
|
|
39
|
+
/// `native(from:as:)` recovers the typed instance from `this`, throwing on a foreign object or a type
|
|
40
|
+
/// mismatch.
|
|
38
41
|
///
|
|
39
42
|
/// The `this` object comes from the borrowed `JavaScriptUnownedValue` in a sync binding (`asObject(in:)`)
|
|
40
43
|
/// and from the owning `JavaScriptValue` in an async one (`asObject()`). An async binding must take an
|
|
41
44
|
/// owning `this` because a borrowed unowned value can't survive the closure's suspension points.
|
|
42
45
|
func unwrapStatement(isAsync: Bool) -> String? {
|
|
43
46
|
switch self {
|
|
44
|
-
case .module:
|
|
47
|
+
case .module, .staticMember:
|
|
45
48
|
return nil
|
|
46
49
|
case .sharedObject(let typeName):
|
|
47
50
|
let thisObject = isAsync ? "this.asObject()" : "this.asObject(in: runtime)"
|
|
@@ -50,13 +53,28 @@ internal enum Receiver {
|
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
/// The capture-clause fragment (with a trailing space, or empty when nothing is captured). A module
|
|
53
|
-
/// captures `self` strong; a shared
|
|
56
|
+
/// captures `self` strong; a shared-object instance and a static member capture nothing.
|
|
54
57
|
var captureClause: String {
|
|
55
58
|
switch self {
|
|
56
59
|
case .module:
|
|
57
60
|
return "[self] "
|
|
58
|
-
case .sharedObject:
|
|
61
|
+
case .sharedObject, .staticMember:
|
|
59
62
|
return ""
|
|
60
63
|
}
|
|
61
64
|
}
|
|
62
65
|
}
|
|
66
|
+
|
|
67
|
+
/// Which JS object a set of bindings is installed on: the second orthogonal axis alongside `Receiver`.
|
|
68
|
+
/// It selects the decorator entry point's argument label and the local name the body binds members onto,
|
|
69
|
+
/// mirroring JS class semantics (a class has a constructor function whose `.prototype` carries instance
|
|
70
|
+
/// members) and core's `ClassDefinition.decorate`.
|
|
71
|
+
/// The raw value is the argument label of the decorator entry point, which is also the local name the
|
|
72
|
+
/// body binds members onto.
|
|
73
|
+
internal enum Phase: String {
|
|
74
|
+
/// A concrete JS object: a singleton's own object (the module). Instance method; receiver `self`.
|
|
75
|
+
case object
|
|
76
|
+
/// The class constructor's `prototype`, carrying per-instance members. Static; receiver `_self`.
|
|
77
|
+
case prototype
|
|
78
|
+
/// The class constructor function itself, carrying `static`/`class` members. Static; receiver the type.
|
|
79
|
+
case constructor
|
|
80
|
+
}
|
|
@@ -42,13 +42,20 @@ public struct SharedObjectMacro: MemberMacro {
|
|
|
42
42
|
let jsName = jsNameArgument(of: node) ?? typeName
|
|
43
43
|
|
|
44
44
|
// `@JS func`s/`var`s and the `@JS init` are bound directly into the shared object's JS object by
|
|
45
|
-
// the synthesized `_decorateSharedObject` / `
|
|
46
|
-
// `Function(...)` / `Property(...)` / `Constructor { … }` DSL entry, so
|
|
47
|
-
// instead of appended to the `Class` block. The block keeps only non-`@JS`
|
|
48
|
-
// collected today), so it's empty when every member is `@JS`.
|
|
45
|
+
// the synthesized `_decorateSharedObject(prototype:)` / `_decorateSharedObject(constructor:)` / `_constructSharedObject`
|
|
46
|
+
// rather than described with a `Function(...)` / `Property(...)` / `Constructor { … }` DSL entry, so
|
|
47
|
+
// they're collected here instead of appended to the `Class` block. The block keeps only non-`@JS`
|
|
48
|
+
// definitions (none are collected today), so it's empty when every member is `@JS`.
|
|
49
|
+
//
|
|
50
|
+
// Members split by the `static`/`class` modifier onto two different JS objects: instance members
|
|
51
|
+
// decorate the prototype (receiver recovered from JS `this`), static members decorate the
|
|
52
|
+
// constructor (called on the metatype). A JS instance and static member may share a name without
|
|
53
|
+
// colliding: they live on different objects.
|
|
49
54
|
let entries: [String] = []
|
|
50
|
-
var
|
|
51
|
-
var
|
|
55
|
+
var instanceFunctions: [JSFunction] = []
|
|
56
|
+
var instanceProperties: [JSProperty] = []
|
|
57
|
+
var staticFunctions: [JSFunction] = []
|
|
58
|
+
var staticProperties: [JSProperty] = []
|
|
52
59
|
var constructor: JSConstructor?
|
|
53
60
|
|
|
54
61
|
for member in classDecl.memberBlock.members {
|
|
@@ -66,13 +73,23 @@ public struct SharedObjectMacro: MemberMacro {
|
|
|
66
73
|
|
|
67
74
|
if let funcDecl = decl.as(FunctionDeclSyntax.self),
|
|
68
75
|
let attribute = funcDecl.attributes.firstAttribute(named: "JS") {
|
|
69
|
-
|
|
76
|
+
let function = JSFunction(funcDecl: funcDecl, attribute: attribute)
|
|
77
|
+
if isTypeLevel(funcDecl.modifiers) {
|
|
78
|
+
staticFunctions.append(function)
|
|
79
|
+
} else {
|
|
80
|
+
instanceFunctions.append(function)
|
|
81
|
+
}
|
|
70
82
|
continue
|
|
71
83
|
}
|
|
72
84
|
|
|
73
85
|
if let varDecl = decl.as(VariableDeclSyntax.self),
|
|
74
86
|
let attribute = varDecl.attributes.firstAttribute(named: "JS") {
|
|
75
|
-
|
|
87
|
+
let collected = collectProperties(varDecl: varDecl, attribute: attribute)
|
|
88
|
+
if isTypeLevel(varDecl.modifiers) {
|
|
89
|
+
staticProperties.append(contentsOf: collected)
|
|
90
|
+
} else {
|
|
91
|
+
instanceProperties.append(contentsOf: collected)
|
|
92
|
+
}
|
|
76
93
|
}
|
|
77
94
|
}
|
|
78
95
|
|
|
@@ -89,13 +106,22 @@ public struct SharedObjectMacro: MemberMacro {
|
|
|
89
106
|
"""
|
|
90
107
|
]
|
|
91
108
|
|
|
92
|
-
// Direct JSI binding
|
|
93
|
-
//
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
|
|
109
|
+
// Direct JSI binding, split by which JS object each member decorates:
|
|
110
|
+
// `_decorateSharedObject(prototype:)` binds instance `@JS func`/`var`s (unwrapping the per-call receiver from
|
|
111
|
+
// `this`); `_decorateSharedObject(constructor:)` binds `static`/`class` ones (called on the metatype); and
|
|
112
|
+
// `_constructSharedObject` builds an instance from the `@JS init` arguments. Each is emitted only
|
|
113
|
+
// when it has something to bind.
|
|
114
|
+
if !instanceFunctions.isEmpty || !instanceProperties.isEmpty {
|
|
115
|
+
emitted.append(
|
|
116
|
+
buildDecorateSharedObjectPhase(
|
|
117
|
+
phase: .prototype, functions: instanceFunctions, properties: instanceProperties,
|
|
118
|
+
typeName: typeName))
|
|
119
|
+
}
|
|
120
|
+
if !staticFunctions.isEmpty || !staticProperties.isEmpty {
|
|
97
121
|
emitted.append(
|
|
98
|
-
|
|
122
|
+
buildDecorateSharedObjectPhase(
|
|
123
|
+
phase: .constructor, functions: staticFunctions, properties: staticProperties,
|
|
124
|
+
typeName: typeName))
|
|
99
125
|
}
|
|
100
126
|
if let constructor {
|
|
101
127
|
emitted.append(constructor.buildConstructor(typeName: typeName))
|