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

Concurrency in JavaScriptCore.framework

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Concurrency in JavaScriptCore.framework

Avatar for Yuichi Kobayashi

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() } }