|
|
@@ -1,11 +1,36 @@
|
|
|
import Foundation
|
|
|
import JavaScriptCore
|
|
|
|
|
|
+private let contextLock = NSRecursiveLock()
|
|
|
+
|
|
|
+extension String {
|
|
|
+ var lowercasingFirst: String { prefix(1).lowercased() + dropFirst() }
|
|
|
+ var uppercasingFirst: String { prefix(1).uppercased() + dropFirst() }
|
|
|
+ var camelCased: String {
|
|
|
+ guard !isEmpty else { return "" }
|
|
|
+ let parts = components(separatedBy: .alphanumerics.inverted)
|
|
|
+ let first = parts.first!.lowercasingFirst
|
|
|
+ let rest = parts.dropFirst().map(\.uppercasingFirst)
|
|
|
+ return ([first] + rest).joined()
|
|
|
+ }
|
|
|
+
|
|
|
+ var pascalCased: String {
|
|
|
+ guard !isEmpty else { return "" }
|
|
|
+ let parts = components(separatedBy: .alphanumerics.inverted)
|
|
|
+ let first = parts.first!.uppercasingFirst
|
|
|
+ let rest = parts.dropFirst().map(\.uppercasingFirst)
|
|
|
+ return ([first] + rest).joined()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
final class JavaScriptWorker {
|
|
|
private let processQueue = DispatchQueue(label: "DispatchQueue.JavaScriptWorker", attributes: .concurrent)
|
|
|
private let virtualMachine: JSVirtualMachine
|
|
|
private var contextPool: [JSContext] = []
|
|
|
private let contextPoolLock = NSLock()
|
|
|
+ @SyncAccess(lock: contextLock) private var commonContext: JSContext? = nil
|
|
|
+ private var consoleLogs: [String] = []
|
|
|
+ private var logContext: String = ""
|
|
|
|
|
|
init(poolSize: Int = 5) {
|
|
|
virtualMachine = JSVirtualMachine()!
|
|
|
@@ -23,7 +48,10 @@ final class JavaScriptWorker {
|
|
|
}
|
|
|
}
|
|
|
let consoleLog: @convention(block) (String) -> Void = { message in
|
|
|
- debug(.openAPS, "JavaScript log: \(message)")
|
|
|
+ let trimmedMessage = message.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
+ if !trimmedMessage.isEmpty {
|
|
|
+ self.consoleLogs.append("\(trimmedMessage)")
|
|
|
+ }
|
|
|
}
|
|
|
context.setObject(consoleLog, forKeyedSubscript: "_consoleLog" as NSString)
|
|
|
return context
|
|
|
@@ -42,8 +70,37 @@ final class JavaScriptWorker {
|
|
|
contextPoolLock.unlock()
|
|
|
}
|
|
|
|
|
|
+ // New method to flush aggregated logs
|
|
|
+ private func outputLogs() {
|
|
|
+ var outputLogs = consoleLogs.joined(separator: "\n").trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
+ consoleLogs.removeAll()
|
|
|
+
|
|
|
+ if outputLogs.isEmpty { return }
|
|
|
+
|
|
|
+ if logContext == "autosens.js" {
|
|
|
+ outputLogs = outputLogs.split(separator: "\n").map { logLine in
|
|
|
+ logLine.replacingOccurrences(
|
|
|
+ of: "^[-+=x!]|u\\(|\\)|\\d{1,2}h$",
|
|
|
+ with: "",
|
|
|
+ options: .regularExpression
|
|
|
+ )
|
|
|
+ }.joined(separator: "\n")
|
|
|
+ }
|
|
|
+
|
|
|
+ if !outputLogs.isEmpty {
|
|
|
+ outputLogs.split(separator: "\n").forEach { logLine in
|
|
|
+ if !"\(logLine)".trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
|
|
|
+ debug(.openAPS, "\(logContext): \(logLine)")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
@discardableResult func evaluate(script: Script) -> JSValue! {
|
|
|
- evaluate(string: script.body)
|
|
|
+ logContext = URL(fileURLWithPath: script.name).lastPathComponent
|
|
|
+ let result = evaluate(string: script.body)
|
|
|
+ outputLogs()
|
|
|
+ return result
|
|
|
}
|
|
|
|
|
|
private func evaluate(string: String) -> JSValue! {
|
|
|
@@ -63,15 +120,23 @@ final class JavaScriptWorker {
|
|
|
|
|
|
func inCommonContext<Value>(execute: (JavaScriptWorker) -> Value) -> Value {
|
|
|
let context = getContext()
|
|
|
- defer { returnContext(context) }
|
|
|
+ defer {
|
|
|
+ returnContext(context)
|
|
|
+ outputLogs()
|
|
|
+ }
|
|
|
return execute(self)
|
|
|
}
|
|
|
|
|
|
func evaluateBatch(scripts: [Script]) {
|
|
|
- let ctx = getContext()
|
|
|
- defer { returnContext(ctx) } // Ensure the context is returned to the pool
|
|
|
+ let context = getContext()
|
|
|
+ defer {
|
|
|
+ // Ensure the context is returned to the pool
|
|
|
+ returnContext(context)
|
|
|
+ }
|
|
|
scripts.forEach { script in
|
|
|
- ctx.evaluateScript(script.body)
|
|
|
+ logContext = URL(fileURLWithPath: script.name).lastPathComponent
|
|
|
+ context.evaluateScript(script.body)
|
|
|
+ outputLogs()
|
|
|
}
|
|
|
}
|
|
|
}
|