JavaScriptCoreとSwiftの間で配列メモリを共有する方法は?

2020-02-15 swift memory javascriptcore

JavaScriptCoreを介してJSを実行するSwiftプログラムを作成しようとしています。 JSがSwiftで作成された型付き配列バッファーに書き込み、その後Swiftが読み書きするように、プログラムの両方の部分でメモリを共有したいと思います。これは一種のコマンドバッファになります。

たとえば、ここに私がやろうとしていることをおよそ表す擬似コードがあります:

// js
let buf;
let i = 0;
setup() {
   buf = new Uint8Array(mem.alloc(N_BYTES));
}

frame() {
   i = 0;
   buf[i++] = some_command_enum;
}

// swift
func alloc(bytes : Int) -> the_memory {
    // allocate bytes uints and save the memory here
    // save a reference to the memory here
    // return the memory to use in JS
}

問題は、実装を実際にallocに追加しようとするたびに、JSが例外を介して関数が未定義であると報告することです。戻り値のない関数は問題ありませんので、これを停止します。

これは私の欠陥のある実装です(コメントをご覧ください):

// swift
@objc protocol JSMemoryExports: JSExport {
    static func alloc(_ byte_count: Int) -> JSObjectRef
    static func free(_ memory: JSObjectRef)
}

class JSMemory: NSObject, JSMemoryExports {
                                           // What is the correct return type?
    class func alloc(_ byte_count: Int) -> JSObjectRef {
        // temp
        let jsContext = JS_Controller.js.ctx!

        print("BYTE_COUNT", byte_count)

        // allocating a typed array
        let arr = JSObjectMakeTypedArray(jsContext.jsGlobalContextRef!, kJSTypedArrayTypeUint8Array, byte_count, nil)

        // just testing here to see how I'd write to this buffer (Note: is this the fastest way, or is all this memory binding slow?:
        // getting the raw bytes
        let ptr = JSObjectGetTypedArrayBytesPtr(jsContext.jsGlobalContextRef!, arr, nil)
        //let buf = JSObjectGetTypedArrayBuffer(jsContext.jsGlobalContextRef, arr, nil)
        let u8Ptr = ptr!.bindMemory(to: UInt8.self, capacity: byte_count)
        //u8Ptr[0] = 5
        return arr!
    }
}

...

jsContext["mem"] = JSMemory.self

// js
const buf = new Uint8Array(mem.alloc(8)) // JS Exception: TypeError: mem.alloc is not a function. (In 'mem.alloc(8)', 'mem.alloc' is undefined)

ある種の@convention属性を使用する関数バインディングのバリアントを見てきました。代わりにそれを使用するつもりですか?

正しいことは何ですか?

Answers

別のソースから多くの情報をつなぎ合わせない限り、ドキュメントはあまり役に立ちません。一見JSValue?ソリューションでは、Swiftで呼び出し可能な古いC APIの一部を使用し、安全でないポインターを使用し、バインドされた関数の戻り値がJSValue?であることを確認しJSValue? s。 JavaScript関数はすべてオブジェクトnull または undefined返すため、これは理にかなっています。オプションのタイプは、この動作を反映しています。

リードが必要な人のための私の進行中のコードは次のとおりです。

更新のために、古いC APIと、より限定された新しいSwift固有のAPIを組み合わせる方法を見つけました。私はまだメモリがリークしていないことを確認していませんが、うまくいけば、必要なものを見つけたようです。

あなたが知りたいと思った場合:

@objc protocol JSMemoryExports: JSExport {
    // note that I'm returning an optional
    static func Uint8ArrayMake(_ count : JSValue) -> JSValue?
}

class JSMemory: NSObject, JSMemoryExports { 
    class func UInt8ArrayMake(_ count : JSValue) -> JSValue? {
        guard !count.isUndefined && !count.isNull else {
            return nil
        }

        let ref : JSValueRef = JSObjectMakeTypedArray(
            JS_Controller.js.ctx.jsGlobalContextRef!,
            kJSTypedArrayTypeUint8Array,
            Int(count.toInt32()),
            nil
        )!

        // if you want to modify the data
        // let ptr = JSObjectGetTypedArrayBytesPtr(
        //    JS_Controller.js.ctx.jsGlobalContextRef!, ref, nil
        // )

        return JSValue(jsValueRef: ref, in: JS_Controller.js.ctx)    
    }
}

役立つ参考資料を次に示します。

Swiftのポインター
Swiftの手動メモリ管理

Related