Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Concurrency in JavaScriptCore.framework

Concurrency in JavaScriptCore.framework

Yuichi Kobayashi

July 24, 2023
Tweet

More Decks by Yuichi Kobayashi

Other Decks in Technology

Transcript

  1. 2 ⾃⼰紹介 ίόϠγ Ϣ΢Πν iOS Engineer@MedPeer Copyright(C) 2 0 2

    3 ALL RIGHTS RESERVED, MedPeer, Inc. @imk 2 o
  2. 6 JavaScriptコードの実⾏⽅法 1. JavaScriptCore.frameworkΛΠϯϙʔτ 2. JSContextΛ༻ҙ 3. evaluateScript()Λίʔϧ Copyright(C) 2

    0 2 3 ALL RIGHTS RESERVED, MedPeer, Inc. import JavaScriptCore let jsContext = JSContext() jsContext.evaluateScript("JavaScript code here...")
  3. 8 Swiftオブジェクトのエクスポート 1. JSExportʹ४ڌͨ͠@objc protocolͰΤΫεϙʔτϝιουΛఆٛ 2. ্هͷϓϩτίϧΛΫϥεͰ࣮૷ 3. JSContext#setObject()ͰΤΫεϙʔτ Copyright(C)

    2 0 2 3 ALL RIGHTS RESERVED, MedPeer, Inc. @objc protocol ModuleJS: NSObjectProtocol, JSExport { static func foo(_ value: Double) -> JSValue } final class Module: NSObject, ModuleJS { static func foo(_ value: Double) -> JSValue { ... } } jsContext.setObject(Module.self, forKeyedSubscript: "module" as NSString) const result = module.foo(9.41); .swift .js
  4. 9 Tips: Swiftクラスのエクスポート 1. ΤΫεϙʔτ͍ͨ͠ΫϥεΛJSExportʹ४ڌ 2. ϑΝΫτϦؔ਺Λ༻ҙ Copyright(C) 2 0

    2 3 ALL RIGHTS RESERVED, MedPeer, Inc. @objc protocol ColorJS: NSObjectProtocol, JSExport { var r: CGFloat { get } var g: CGFloat { get } var b: CGFloat { get } var a: CGFloat { get } } final class ColorImp: NSObject, ColorJS { ... } @objc protocol ModuleJS: NSObjectProtocol, JSExport { static func Color(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat, _ a: CGFloat) -> ColorJS } const color = module.Color(0.5, 0.6, 0.7); const red = color.r; .swift .js
  5. 12 Promise, async/await@JS JavaScript(~ECMAScript 2015)ʹ͓͚Δඇಉظॲཧͷ࢓૊Έ async/await͸Promise/thenͷγϯλοΫεγϡΨʔ Copyright(C) 2 0 2

    3 ALL RIGHTS RESERVED, MedPeer, Inc. const process = new Promise((resolve, reject) => { ... resolve(result); }); process.then((result) => { ... }); .js async function process() { ... return result; } const result = await process();
  6. 13 [email protected] JSValue(newPromiseIn:fromExecutor:)Λ࢖͏ Copyright(C) 2 0 2 3 ALL RIGHTS

    RESERVED, MedPeer, Inc. iOS 1 3 でしれっと追加されていた... final class Module: NSObject, JSModule { static func bar() -> JSValue { return .init(newPromiseIn: .current()) { resolve, reject in ... } } } const result = await module.bar(); .swift .js
  7. 17 Top-level await ECMAScript 2022͔ΒରԠ JavaScriptCore.frameworkͰ͸·ͩରԠ͍ͯ͠ͳ͍ iOS17 SDKͰ΋ରԠͯ͠ͳͦ͞͏😢 Copyright(C) 2

    0 2 3 ALL RIGHTS RESERVED, MedPeer, Inc. async関数の外、つまりメインコード中でもawaitできる仕組み evaluateScript() asyncが出るまでConcurrencyできないのか😇
  8. 20 Top-level awaitの⽣存戦略 Copyright(C) 2 0 2 3 ALL RIGHTS

    RESERVED, MedPeer, Inc. 1. JSͷϝΠϯίʔυͰͷawait͸ఘΊΔ 2. ೖΓޱͱͳΔJSඇಉظؔ਺ΛSwiftଆ͔Βݺͼग़͠ɺͦͷ׬ྃΛ଴ػ 3. ׬ྃ଴ػʹ͸SemaphoreΛ࢖͏ // Swift͔Β͜ͷඇಉظؔ਺Λݺͼग़͠ɺ଴ػͯ͠΋Β͏ async main() { const result = await module.bar(); } // Top-level awaitͰ͖ͳͷͰɺϝΠϯίʔυ͔Βͷݺͼग़͠͸͖͋ΒΊΔ // await main(); .js
  9. 21 SwiftからJS上の⾮同期関数を呼ぶ JSඇಉظؔ਺ΛPromiseͱͯ͠ࢀর͠ɺthen()ΛݺͿ Copyright(C) 2 0 2 3 ALL RIGHTS

    RESERVED, MedPeer, Inc. // main()͸ϩʔυ͞ΕΔ͕ɺಈ࡞͸͠ͳ͍ jsContext.evaluateScript(code) let semaphore = DispatchSemaphore(value: 0) // JavaScript͔Βݺͼग़͠ՄೳͳClosureΛ༻ҙ // (@convention(block)ʹ͍ͭͯ͸JSValueͷυΩϡϝϯτࢀর) let onFulfilled: @convention(block) (JSValue) -> Void = { _ in semaphore.signal() } let onRejected: @convention(block) (JSValue) -> Void = { _ in // TODO: Handle error semaphore.signal() } // main()͔ΒPromiseΦϒδΣΫτΛࢀর͠ɺthen()ΛݺͿ jsContext .objectForKeyedSubscript("main")?.call(withArguments: [])? .invokeMethod("then", withArguments: [ JSValue(object: onFulfilled, in: jsContext)!, JSValue(object: onRejected, in: jsContext)! ]) // signal()͞ΕΔ·ͰεϨουΛఀࢭͯ͠଴ػ _ = semaphore.wait(timeout: .distantFuture)
  10. 22 JSコード実⾏部をContinuationでConcurrency化 εϨουͷϒϩοΫΛ཈ࢭ Copyright(C) 2 0 2 3 ALL RIGHTS

    RESERVED, MedPeer, Inc. func run(code: String) async throws { return try await withCheckedThrowingContinuation { continuation in jsContext.evaluateScript(code) let semaphore = DispatchSemaphore(value: 0) ... _ = semaphore.wait(timeout: .distantFuture) continuation.resume() } }