JavaScriptWorker.swift 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. import Foundation
  2. import JavaScriptCore
  3. private let contextLock = NSRecursiveLock()
  4. final class JavaScriptWorker {
  5. private let processQueue = DispatchQueue(label: "DispatchQueue.JavaScriptWorker")
  6. private let virtualMachine: JSVirtualMachine
  7. @SyncAccess(lock: contextLock) private var commonContext: JSContext? = nil
  8. init() {
  9. virtualMachine = processQueue.sync { JSVirtualMachine()! }
  10. }
  11. private func createContext() -> JSContext {
  12. let context = JSContext(virtualMachine: virtualMachine)!
  13. context.exceptionHandler = { _, exception in
  14. if let error = exception?.toString() {
  15. print(error)
  16. }
  17. }
  18. return context
  19. }
  20. @discardableResult func evaluate(script: Script) -> JSValue! {
  21. evaluate(string: script.body)
  22. }
  23. private func evaluate(string: String) -> JSValue! {
  24. let ctx = commonContext ?? createContext()
  25. return ctx.evaluateScript(string)
  26. }
  27. private func json(for string: String) -> JSON {
  28. evaluate(string: "JSON.stringify(\(string));")!.toString()!
  29. }
  30. func call(function: String, with arguments: [JSON]) -> JSON {
  31. let joined = arguments.map(\.string).joined(separator: ",")
  32. return json(for: "\(function)(\(joined))")
  33. }
  34. func inCommonContext<Value>(execute: (JavaScriptWorker) -> Value) -> Value {
  35. commonContext = createContext()
  36. defer {
  37. commonContext = nil
  38. }
  39. return execute(self)
  40. }
  41. }