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. @imk
    2
    o
    Concurrency in JavaScriptCore.framework
    potatotips #
    8 3

    View full-size slide

  2. 2
    ⾃⼰紹介
    ίόϠγ Ϣ΢Πν
    iOS Engineer@MedPeer
    Copyright(C)
    2
    0
    2
    3
    ALL RIGHTS RESERVED, MedPeer, Inc.
    @imk
    2
    o

    View full-size slide

  3. 3
    JavaScriptCore.framework使ったことあります?🙋
    Copyright(C)
    2
    0
    2
    3
    ALL RIGHTS RESERVED, MedPeer, Inc.

    View full-size slide

  4. 基本のおさらい
    JavaScriptCore.frameworkͱ͸ʁ

    View full-size slide

  5. 5
    JavaScriptCore.frameworkの今北産業
    ΞϓϦʹJavaScriptίʔυͷ࣮ߦ؀ڥΛ૊ΈࠐΊΔϑϨʔϜϫʔΫ
    Swift/Objective-Cͷܕ΍ؔ਺ΛΤΫεϙʔτͯ͠ར༻Մೳ
    ΞϓϦͷػೳΛJSͰ੍ޚͨ͠ΓɺৼΔ෣͍ΛΧελϚΠζͰ͖Δ
    Copyright(C)
    2
    0
    2
    3
    ALL RIGHTS RESERVED, MedPeer, Inc.

    View full-size slide

  6. 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...")

    View full-size slide

  7. 7
    JSValueについて
    JS؀ڥ্ͷ஋Λද͢ΦϒδΣΫτ
    Swiftͱ૬ޓʹσʔλΛ΍ΓͱΓ͢Δཁ
    Copyright(C)
    2
    0
    2
    3
    ALL RIGHTS RESERVED, MedPeer, Inc.

    View full-size slide

  8. 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

    View full-size slide

  9. 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

    View full-size slide

  10. だいたいわかったところで
    ຊ୊ʹೖΓ·͢ʂ

    View full-size slide

  11. 11
    JavaScriptCore.frameworkでもConcurrencyやれんのか?
    Ͱ͖·͢ʂ
    ͨͩͪ͠ΐͬͱ޻෉͕ཁΓ·͢
    Copyright(C)
    2
    0
    2
    3
    ALL RIGHTS RESERVED, MedPeer, Inc.

    View full-size slide

  12. 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();

    View full-size slide

  13. 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

    View full-size slide

  14. 14
    できたッ!
    Copyright(C)
    2
    0
    2
    3
    ALL RIGHTS RESERVED, MedPeer, Inc.

    View full-size slide

  15. 15
    ...と思ったらRuntime Error!!
    Copyright(C)
    2
    0
    2
    3
    ALL RIGHTS RESERVED, MedPeer, Inc.

    View full-size slide

  16. 16
    なんでよ?
    Copyright(C)
    2
    0
    2
    3
    ALL RIGHTS RESERVED, MedPeer, Inc.

    View full-size slide

  17. 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できないのか😇

    View full-size slide

  18. 18
    じゃあ、Swiftでやればいい。
    Copyright(C)
    2
    0
    2
    3
    ALL RIGHTS RESERVED, MedPeer, Inc.

    View full-size slide

  19. SwiftͰTop-level awaitʹରԠ͢Δʹ͸

    View full-size slide

  20. 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

    View full-size slide

  21. 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)

    View full-size slide

  22. 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()


    }


    }

    View full-size slide

  23. 23
    まとめ
    JavaScriptCore.frameworkͰ΋ConcurrencyʹରԠͰ͖Δ
    JSValueʹ͸ൿີ͕͍ͬͺ͍
    ΤϥʔϋϯυϦϯάʹ͍ͭͯ͸ฐࣾςοΫϒϩάʹॻ͘(༧ఆ)
    Copyright(C)
    2
    0
    2
    3
    ALL RIGHTS RESERVED, MedPeer, Inc.

    View full-size slide

  24. 24
    Copyright(C)
    2
    0
    2
    3
    ALL RIGHTS RESERVED, MedPeer, Inc.

    View full-size slide