quickjs.swift (5417B)
1 /* 2 * This file is part of GNU Taler, ©2022-26 Taler Systems S.A. 3 * See LICENSE.md 4 */ 5 /** 6 * @author Marc Stibane 7 */ 8 import Foundation 9 import os.log 10 11 import FTalerWalletcore 12 13 public protocol QuickjsMessageHandler: AnyObject { 14 func handleMessage(message: String) 15 func handleLog(message: String) 16 } 17 // MARK: - 18 func notification_callback(userdata: Optional<UnsafeMutableRawPointer>, 19 payload: Optional<UnsafePointer<Int8>>) { 20 let quickjs = Unmanaged<Quickjs>.fromOpaque(userdata!).takeUnretainedValue() 21 let string = String(cString: payload!) 22 quickjs.internalOnNotify(payload: string) 23 } 24 25 func logging_callback(userdata: Optional<UnsafeMutableRawPointer>, 26 level: TALER_WALLET_LogLevel, 27 tag: Optional<UnsafePointer<Int8>>, 28 message: Optional<UnsafePointer<Int8>>) { 29 let quickjs = Unmanaged<Quickjs>.fromOpaque(userdata!).takeUnretainedValue() 30 let logger = quickjs.logger 31 let swiftTag = String(cString: tag!) 32 let swiftMessage = String(cString: message!) 33 34 switch level { 35 case TALER_WALLET_LOG_ERROR: 36 logger.error("\(swiftTag, privacy: .public) \(swiftMessage, privacy: .public)") 37 case TALER_WALLET_LOG_WARN: 38 logger.warning("\(swiftTag, privacy: .public) \(swiftMessage, privacy: .public)") 39 case TALER_WALLET_LOG_MESSAGE: 40 logger.notice("\(swiftTag, privacy: .public) \(swiftMessage, privacy: .public)") 41 case TALER_WALLET_LOG_INFO: 42 logger.info("\(swiftTag, privacy: .public) \(swiftMessage, privacy: .public)") 43 case TALER_WALLET_LOG_TRACE: 44 logger.trace("\(swiftTag, privacy: .public) \(swiftMessage, privacy: .public)") 45 default: break 46 } 47 quickjs.internalOnLog(message: swiftMessage) 48 } 49 // MARK: - 50 public class Quickjs { // acts as singleton, since only one instance ever exists 51 var talerWalletInstance: OpaquePointer! 52 public weak var messageHandler: QuickjsMessageHandler? 53 var logger: Logger 54 55 @Atomic(default: 0) // boilerPlate around NSLock… 56 private var lastRequestID: Int32 // …to safeguard increments 57 private var requests: [Int32: QuickDataTask] = [:] 58 59 private lazy var urlSession: URLSession = { 60 return URLSession(configuration: .ephemeral) // we don't want the filesystem cache - keep it in RAM 61 }() 62 63 public init() { 64 self.logger = Logger(subsystem: "net.taler.gnu", category: "QuickJS") 65 self.talerWalletInstance = TALER_WALLET_create() 66 TALER_WALLET_set_message_handler(talerWalletInstance, 67 notification_callback, 68 Unmanaged.passUnretained(self).toOpaque()) 69 TALER_WALLET_set_log_handler(talerWalletInstance, 70 logging_callback, 71 Unmanaged.passUnretained(self).toOpaque()) 72 let http_impl = TALER_pack_http_client_implementation(request_create, request_cancel, 73 Unmanaged.passUnretained(self).toOpaque()) 74 // http_impl got malloc'd, and could possibly be free'd when the app terminates 75 TALER_set_http_client_implementation(talerWalletInstance, http_impl) 76 // - but we never free anything on termination, thus we don't save http_impl here 77 TALER_WALLET_run(talerWalletInstance) 78 } 79 80 func reqCreate(_ request: URLRequest, 81 _ responseCb: JSHttpResponseCb?, 82 _ responseCbCls: Optional<UnsafeMutableRawPointer>) -> Int32 { 83 let requestID = $lastRequestID.atomicIncrement() // ensure requestID is unique 84 let quickDataTask = QuickDataTask(urlSession: urlSession, 85 request: request, 86 requestID: requestID, 87 requests: &requests, 88 responseCb: responseCb, 89 responseCbCls: responseCbCls) 90 quickDataTask.run() 91 requests[requestID] = quickDataTask 92 return requestID 93 } 94 95 func reqCancel(_ requestID: Int32) -> Int32 { 96 if let quickDataTask = requests[requestID] { 97 if let dataTask = quickDataTask.dataTask { 98 dataTask.cancel() 99 } 100 } 101 requests[requestID] = nil 102 return 0 103 } 104 105 deinit { 106 // No need to call TALER_WALLET_destroy - memory gets purged anyway 107 } 108 109 func internalOnNotify(payload: String) { 110 if let handler = messageHandler { 111 handler.handleMessage(message: payload) 112 } 113 } 114 115 public func internalOnLog(message: String) { 116 if let handler = messageHandler { 117 handler.handleLog(message: message) 118 } 119 } 120 121 // public func notifyNative() { 122 // __notifyNative(instance) 123 // } 124 125 // public func evalNodeCode(source: String) { 126 // scheduleNodeThreadAsync { 127 // __makeCallbackNative(self.instance, source.cString(using: .utf8)) 128 // } 129 // } 130 131 public func sendMessage(message: String) { 132 TALER_WALLET_send_request(talerWalletInstance, message) 133 } 134 }