11import _CJavaScriptKit
22
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+ ///
313public 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.
420 @discardableResult
521 public func callAsFunction( this: JSObject ? = nil , arguments: [ JSValueConvertible ] ) -> JSValue {
622 let result = arguments. withRawJSValues { rawValues in
@@ -24,19 +40,22 @@ public class JSFunction: JSObject {
2440 return result. jsValue ( )
2541 }
2642
43+ /// A variadic arguments version of `callAsFunction`.
2744 @discardableResult
2845 public func callAsFunction( this: JSObject ? = nil , _ arguments: JSValueConvertible ... ) -> JSValue {
2946 self ( this: this, arguments: arguments)
3047 }
3148
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.
4059 public func new( arguments: [ JSValueConvertible ] ) -> JSObject {
4160 arguments. withRawJSValues { rawValues in
4261 rawValues. withUnsafeBufferPointer { bufferPointer in
@@ -52,6 +71,11 @@ public class JSFunction: JSObject {
5271 }
5372 }
5473
74+ /// A variadic arguments version of `new`.
75+ public func new( _ arguments: JSValueConvertible ... ) -> JSObject {
76+ new ( arguments: arguments)
77+ }
78+ 5579 @available ( * , unavailable, message: " Please use JSClosure instead " )
5680 public static func from( _: @escaping ( [ JSValue ] ) -> JSValue ) -> JSFunction {
5781 fatalError ( " unavailable " )
@@ -62,33 +86,59 @@ public class JSFunction: JSObject {
6286 }
6387}
6488
89+ /// `JSClosure` represents a JavaScript function the body of which 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+ ///
65108public class JSClosure : JSFunction {
66109 static var sharedFunctions : [ JavaScriptHostFuncRef : ( [ JSValue ] ) -> JSValue ] = [ : ]
67110
68111 private var hostFuncRef : JavaScriptHostFuncRef = 0
69112
70113 private var isReleased = false
71- 114+ 115+ /// Instantiate a new `JSClosure` with given function body.
116+ /// - Parameter body: The body of this function.
72117 public init ( _ body: @escaping ( [ JSValue ] ) -> JSValue ) {
118+ // 1. Fill `id` as zero at first to access `self` to get `ObjectIdentifier`.
73119 super. init ( id: 0 )
74120 let objectId = ObjectIdentifier ( self )
75121 let funcRef = JavaScriptHostFuncRef ( bitPattern: Int32 ( objectId. hashValue) )
122+ // 2. Retain the given body in static storage by `funcRef`.
76123 Self . sharedFunctions [ funcRef] = body
77- 124+ // 3. Create a new JavaScript function which calls the given Swift function.
78125 var objectRef : JavaScriptObjectRef = 0
79126 _create_function ( funcRef, & objectRef)
80127
81128 hostFuncRef = funcRef
82129 id = objectRef
83130 }
84- 131+ 132+ /// A convenience initializer which assumes that the given body function returns `JSValue.undefined`
85133 convenience public init ( _ body: @escaping ( [ JSValue ] ) -> ( ) ) {
86134 self . init { ( arguments: [ JSValue ] ) -> JSValue in
87135 body ( arguments)
88136 return . undefined
89137 }
90138 }
91- 139+ 140+ /// Release this function resource.
141+ /// After calling `release`, calling this function from JavaScript will fail.
92142 public func release( ) {
93143 Self . sharedFunctions [ hostFuncRef] = nil
94144 isReleased = true
0 commit comments