Как разделить память массива между JavaScriptCore и Swift? - PullRequest
1 голос
/ 15 февраля 2020

Я пытаюсь написать программу Swift, которая запускает JS через JavaScriptCore. Я sh хочу разделить память между обеими частями моей программы так, чтобы 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
}

Проблема в том, что всякий раз, когда я пытаюсь на самом деле добавление реализации к allo c, 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. Должен ли я использовать это вместо этого?

Как правильно поступить?

1 Ответ

1 голос
/ 18 февраля 2020

Документация не очень полезна, если вы не соберете много информации из разных источников. Казалось бы, работающее решение включает в себя использование частей старого API C, которые можно вызывать в Swift, небезопасные указатели и проверку того, что возвращаемые значения связанных функций равны JSValue? s. Это имеет смысл, поскольку все функции JavaScript возвращают объект, null, или undefined. Необязательный тип отражает это поведение.

Вот мой код незавершенного производства для тех, кому могут понадобиться некоторые отведения:

Просто для обновления я понял, как смешать старый C API с новыми более ограниченными * Swift-speci c 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

...