วิธีการแชร์หน่วยความจำอาร์เรย์ระหว่าง JavaScriptCore และ Swift

2020-02-15 swift memory javascriptcore

ฉันพยายามเขียนโปรแกรม Swift ที่รัน JS ผ่าน JavaScriptCore ฉันต้องการแบ่งปันหน่วยความจำระหว่างโปรแกรมทั้งสองส่วนของฉันเพื่อให้ 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
}

ปัญหาคือว่าเมื่อใดก็ตามที่ฉันพยายามเพิ่มการใช้งานจริงเพื่อจัดสรร 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

เอกสารดังกล่าวไม่เป็นประโยชน์เว้นแต่คุณรวบรวมข้อมูลจำนวนมากจากแหล่งข้อมูลแยกต่างหาก โซลูชันที่ใช้งานได้ดูเหมือนเกี่ยวข้องกับการใช้ชิ้นส่วนของ C API รุ่นเก่าที่เรียกได้ในตัวชี้อย่างรวดเร็วไม่ปลอดภัยและทำให้แน่ใจว่าค่าส่งคืนของฟังก์ชันที่ผูกไว้เป็น JSValue? s มันสมเหตุสมผลแล้วเนื่องจากฟังก์ชั่นจาวาทั้งหมดกลับวัตถุเป็น null หรือ undefined ประเภทตัวเลือกสะท้อนลักษณะการทำงานนี้

นี่คือรหัสงานที่กำลังดำเนินการของฉันสำหรับทุกคนที่อาจต้องการโอกาสในการขาย:

สำหรับการอัปเดตฉันได้เรียนรู้วิธีผสม C API เก่ากับ API ที่เฉพาะเจาะจงของ Swift ที่ จำกัด มากขึ้นใหม่ ฉันยังไม่ได้ทำให้แน่ใจว่าฉันไม่รั่วความทรงจำ แต่ดูเหมือนว่าฉันได้พบสิ่งที่ฉันต้องการหวังว่า

ในกรณีที่คุณอยากรู้:

@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