1
1
import _CJavaScriptKit
2
2
3
+ /// `JSFunction` represents a function in JavaScript and supports new object instantiation.
4
+ /// This type can be callable as a function using `callAsFunction`.
5
+ ///
6
+ /// e.g.
7
+ /// ```swift
8
+ /// let alert: JSFunction = JSObject.global.alert.function!
9
+ /// // Call `JSFunction` as a function
10
+ /// alert("Hello, world")
11
+ /// ```
12
+ ///
3
13
public class JSFunction : JSObject {
14
+
15
+ /// Call this function with given `arguments` and binding given `this` as context.
16
+ /// - Parameters:
17
+ /// - this: The value to be passed as the `this` parameter to this function.
18
+ /// - arguments: Arguments to be passed to this function.
19
+ /// - Returns: The result of this call.
4
20
@discardableResult
5
21
public func callAsFunction( this: JSObject ? = nil , arguments: [ JSValueConvertible ] ) -> JSValue {
6
22
let result = arguments. withRawJSValues { rawValues in
@@ -24,19 +40,22 @@ public class JSFunction: JSObject {
24
40
return result. jsValue ( )
25
41
}
26
42
43
+ /// A variadic arguments version of `callAsFunction`.
27
44
@discardableResult
28
45
public func callAsFunction( this: JSObject ? = nil , _ arguments: JSValueConvertible ... ) -> JSValue {
29
46
self ( this: this, arguments: arguments)
30
47
}
31
48
32
- public func new( _ arguments: JSValueConvertible ... ) -> JSObject {
33
- new ( arguments: arguments)
34
- }
35
-
36
- // Guaranteed to return an object because either:
37
- // a) the constructor explicitly returns an object, or
38
- // b) the constructor returns nothing, which causes JS to return the `this` value, or
39
- // c) the constructor returns undefined, null or a non-object, in which case JS also returns `this`.
49
+ /// Instantiate an object from this function as a constructor.
50
+ ///
51
+ /// Guaranteed to return an object because either:
52
+ ///
53
+ /// - a. the constructor explicitly returns an object, or
54
+ /// - b. the constructor returns nothing, which causes JS to return the `this` value, or
55
+ /// - c. the constructor returns undefined, null or a non-object, in which case JS also returns `this`.
56
+ ///
57
+ /// - Parameter arguments: Arguments to be passed to this constructor function.
58
+ /// - Returns: A new instance of this constructor.
40
59
public func new( arguments: [ JSValueConvertible ] ) -> JSObject {
41
60
arguments. withRawJSValues { rawValues in
42
61
rawValues. withUnsafeBufferPointer { bufferPointer in
@@ -52,6 +71,11 @@ public class JSFunction: JSObject {
52
71
}
53
72
}
54
73
74
+ /// A variadic arguments version of `new`.
75
+ public func new( _ arguments: JSValueConvertible ... ) -> JSObject {
76
+ new ( arguments: arguments)
77
+ }
78
+
55
79
@available ( * , unavailable, message: " Please use JSClosure instead " )
56
80
public static func from( _: @escaping ( [ JSValue ] ) -> JSValue ) -> JSFunction {
57
81
fatalError ( " unavailable " )
@@ -62,33 +86,59 @@ public class JSFunction: JSObject {
62
86
}
63
87
}
64
88
89
+ /// `JSClosure` represents a JavaScript function whose body is written in Swift.
90
+ /// This type can be passed as a callback handler to JavaScript functions.
91
+ /// Note that the lifetime of `JSClosure` should be managed by users manually
92
+ /// due to GC boundary between Swift and JavaScript.
93
+ /// For further discussion, see also [swiftwasm/JavaScriptKit #33](https://github.com/swiftwasm/JavaScriptKit/pull/33)
94
+ ///
95
+ /// e.g.
96
+ /// ```swift
97
+ /// let eventListenter = JSClosure { _ in
98
+ /// ...
99
+ /// return JSValue.undefined
100
+ /// }
101
+ ///
102
+ /// button.addEventListener!("click", JSValue.function(eventListenter))
103
+ /// ...
104
+ /// button.removeEventListener!("click", JSValue.function(eventListenter))
105
+ /// eventListenter.release()
106
+ /// ```
107
+ ///
65
108
public class JSClosure : JSFunction {
66
109
static var sharedFunctions : [ JavaScriptHostFuncRef : ( [ JSValue ] ) -> JSValue ] = [ : ]
67
110
68
111
private var hostFuncRef : JavaScriptHostFuncRef = 0
69
112
70
113
private var isReleased = false
71
-
114
+
115
+ /// Instantiate a new `JSClosure` with given function body.
116
+ /// - Parameter body: The body of this function.
72
117
public init ( _ body: @escaping ( [ JSValue ] ) -> JSValue ) {
118
+ // 1. Fill `id` as zero at first to access `self` to get `ObjectIdentifier`.
73
119
super. init ( id: 0 )
74
120
let objectId = ObjectIdentifier ( self )
75
121
let funcRef = JavaScriptHostFuncRef ( bitPattern: Int32 ( objectId. hashValue) )
122
+ // 2. Retain the given body in static storage by `funcRef`.
76
123
Self . sharedFunctions [ funcRef] = body
77
-
124
+ // 3. Create a new JavaScript function which calls the given Swift function.
78
125
var objectRef : JavaScriptObjectRef = 0
79
126
_create_function ( funcRef, & objectRef)
80
127
81
128
hostFuncRef = funcRef
82
129
id = objectRef
83
130
}
84
-
131
+
132
+ /// A convenience initializer which assumes that the given body function returns `JSValue.undefined`
85
133
convenience public init ( _ body: @escaping ( [ JSValue ] ) -> ( ) ) {
86
134
self . init { ( arguments: [ JSValue ] ) -> JSValue in
87
135
body ( arguments)
88
136
return . undefined
89
137
}
90
138
}
91
-
139
+
140
+ /// Release this function resource.
141
+ /// After calling `release`, calling this function from JavaScript will fail.
92
142
public func release( ) {
93
143
Self . sharedFunctions [ hostFuncRef] = nil
94
144
isReleased = true
0 commit comments