@@ -19,15 +19,17 @@ public class JSOneshotClosure: JSObject, JSClosureProtocol {
1919 // 1. Fill `id` as zero at first to access `self` to get `ObjectIdentifier`.
2020 super. init ( id: 0 )
2121
22- // 2. Retain the given body in static storage
23- // Leak the self object globally and release once it's called
24- hostFuncRef = JSClosure . sharedClosures. register ( ObjectIdentifier ( self ) , object: . strong( self ) , body: {
25- defer { self . release ( ) }
26- return body ( 0ドル)
27- } )
22+ // 2. Create a new JavaScript function which calls the given Swift function.
23+ hostFuncRef = JavaScriptHostFuncRef ( bitPattern: ObjectIdentifier ( self ) )
2824 id = withExtendedLifetime ( JSString ( file) ) { file in
2925 _create_function ( hostFuncRef, line, file. asInternalJSRef ( ) )
3026 }
27+ 28+ // 3. Retain the given body in static storage by `funcRef`.
29+ JSClosure . sharedClosures [ hostFuncRef] = ( self , {
30+ defer { self . release ( ) }
31+ return body ( 0ドル)
32+ } )
3133 }
3234
3335 #if compiler(>=5.5)
@@ -40,7 +42,7 @@ public class JSOneshotClosure: JSObject, JSClosureProtocol {
4042 /// Release this function resource.
4143 /// After calling `release`, calling this function from JavaScript will fail.
4244 public func release( ) {
43- JSClosure . sharedClosures. unregister ( hostFuncRef)
45+ JSClosure . sharedClosures [ hostFuncRef] = nil
4446 }
4547}
4648
@@ -60,13 +62,13 @@ public class JSOneshotClosure: JSObject, JSClosureProtocol {
6062/// ```
6163///
6264public class JSClosure : JSFunction , JSClosureProtocol {
63- fileprivate static var sharedClosures = SharedJSClosureRegistry ( )
65+ 66+ // Note: Retain the closure object itself also to avoid funcRef conflicts
67+ fileprivate static var sharedClosures : [ JavaScriptHostFuncRef : ( object: JSObject , body: ( [ JSValue ] ) -> JSValue ) ] = [ : ]
6468
6569 private var hostFuncRef : JavaScriptHostFuncRef = 0
6670
6771 #if JAVASCRIPTKIT_WITHOUT_WEAKREFS
68- private let file : String
69- private let line : UInt32
7072 private var isReleased : Bool = false
7173 #endif
7274
@@ -80,35 +82,30 @@ public class JSClosure: JSFunction, JSClosureProtocol {
8082 }
8183
8284 public init ( _ body: @escaping ( [ JSValue ] ) -> JSValue , file: String = #fileID, line: UInt32 = #line) {
83- #if JAVASCRIPTKIT_WITHOUT_WEAKREFS
84- self . file = file
85- self . line = line
86- #endif
8785 // 1. Fill `id` as zero at first to access `self` to get `ObjectIdentifier`.
8886 super. init ( id: 0 )
8987
90- // 2. Retain the given body in static storage
91- hostFuncRef = Self . sharedClosures. register (
92- ObjectIdentifier ( self ) , object: . weak( self ) , body: body
93- )
94- 88+ // 2. Create a new JavaScript function which calls the given Swift function.
89+ hostFuncRef = JavaScriptHostFuncRef ( bitPattern: ObjectIdentifier ( self ) )
9590 id = withExtendedLifetime ( JSString ( file) ) { file in
9691 _create_function ( hostFuncRef, line, file. asInternalJSRef ( ) )
9792 }
9893
94+ // 3. Retain the given body in static storage by `funcRef`.
95+ Self . sharedClosures [ hostFuncRef] = ( self , body)
9996 }
10097
10198 #if compiler(>=5.5)
10299 @available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
103- public static func async ( _ body: @escaping ( [ JSValue ] ) async throws -> JSValue , file : String = #fileID , line : UInt32 = #line ) -> JSClosure {
104- JSClosure ( makeAsyncClosure ( body) , file : file , line : line )
100+ public static func async ( _ body: @escaping ( [ JSValue ] ) async throws -> JSValue ) -> JSClosure {
101+ JSClosure ( makeAsyncClosure ( body) )
105102 }
106103 #endif
107104
108105 #if JAVASCRIPTKIT_WITHOUT_WEAKREFS
109106 deinit {
110107 guard isReleased else {
111- fatalError ( " release() must be called on JSClosure object ( \( file ) : \( line ) ) manually before they are deallocated" )
108+ fatalError ( " release() must be called on JSClosure objects manually before they are deallocated " )
112109 }
113110 }
114111 #endif
@@ -136,60 +133,6 @@ private func makeAsyncClosure(_ body: @escaping ([JSValue]) async throws -> JSVa
136133}
137134#endif
138135
139- /// Registry for Swift closures that are referenced from JavaScript.
140- private struct SharedJSClosureRegistry {
141- struct ClosureEntry {
142- // Note: Retain the closure object itself also to avoid funcRef conflicts.
143- var object : AnyObjectReference
144- var body : ( [ JSValue ] ) -> JSValue
145- 146- init ( object: AnyObjectReference , body: @escaping ( [ JSValue ] ) -> JSValue ) {
147- self . object = object
148- self . body = body
149- }
150- }
151- enum AnyObjectReference {
152- case strong( AnyObject )
153- case weak( WeakObject )
154- 155- static func `weak`( _ object: AnyObject ) -> AnyObjectReference {
156- . weak( SharedJSClosureRegistry . WeakObject ( underlying: object) )
157- }
158- }
159- struct WeakObject {
160- weak var underlying : AnyObject ?
161- init ( underlying: AnyObject ) {
162- self . underlying = underlying
163- }
164- }
165- private var closures : [ JavaScriptHostFuncRef : ClosureEntry ] = [ : ]
166- 167- /// Register a Swift closure to be called from JavaScript.
168- /// - Parameters:
169- /// - hint: A hint to identify the closure.
170- /// - object: The object should be retained until the closure is released from JavaScript.
171- /// - body: The closure to be called from JavaScript.
172- /// - Returns: An unique identifier for the registered closure.
173- mutating func register(
174- _ hint: ObjectIdentifier ,
175- object: AnyObjectReference , body: @escaping ( [ JSValue ] ) -> JSValue
176- ) -> JavaScriptHostFuncRef {
177- let ref = JavaScriptHostFuncRef ( bitPattern: hint)
178- closures [ ref] = ClosureEntry ( object: object, body: body)
179- return ref
180- }
181- 182- /// Unregister a Swift closure from the registry.
183- mutating func unregister( _ ref: JavaScriptHostFuncRef ) {
184- closures [ ref] = nil
185- }
186- 187- /// Get the Swift closure from the registry.
188- subscript( _ ref: JavaScriptHostFuncRef ) -> ( ( [ JSValue ] ) -> JSValue ) ? {
189- closures [ ref] ? . body
190- }
191- }
192- 193136// MARK: - `JSClosure` mechanism note
194137//
195138// 1. Create a thunk in the JavaScript world, which has a reference
@@ -231,7 +174,7 @@ func _call_host_function_impl(
231174 _ argv: UnsafePointer < RawJSValue > , _ argc: Int32 ,
232175 _ callbackFuncRef: JavaScriptObjectRef
233176) -> Bool {
234- guard let hostFunc = JSClosure . sharedClosures [ hostFuncRef] else {
177+ guard let ( _ , hostFunc) = JSClosure . sharedClosures [ hostFuncRef] else {
235178 return true
236179 }
237180 let arguments = UnsafeBufferPointer ( start: argv, count: Int ( argc) ) . map ( \. jsValue)
@@ -252,7 +195,7 @@ func _call_host_function_impl(
252195extension JSClosure {
253196 public func release( ) {
254197 isReleased = true
255- Self . sharedClosures. unregister ( hostFuncRef)
198+ Self . sharedClosures [ hostFuncRef] = nil
256199 }
257200}
258201
@@ -270,6 +213,6 @@ extension JSClosure {
270213
271214@_cdecl ( " _free_host_function_impl " )
272215func _free_host_function_impl( _ hostFuncRef: JavaScriptHostFuncRef ) {
273- JSClosure . sharedClosures. unregister ( hostFuncRef)
216+ JSClosure . sharedClosures [ hostFuncRef] = nil
274217}
275218#endif
0 commit comments