How to share array memory between JavaScriptCore and Swift? - swift

I am trying to write a Swift program that runs JS via JavaScriptCore. I wish to share memory between both parts of my program such that the JS writes to a typed array buffer created in Swift, and Swift reads and writes to it afterwards. This will be a sort of command buffer.
For example, here is some pseudocode that approximately represents what I'm planning to do:
// 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
}
The problem is that whenever I try actually adding the implementation to alloc, JS reports via exception that the function is undefined, meaning that something is off with the way I'm doing things. Non-returning functions are fine, so I have that down.
This is my faulty implementation (please see the comments):
// 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)
I've seen variants of function binding that uses some sort of #convention attribute. Am I meant to use that instead?
What is the correct thing to do?

The documentation isn't very helpful unless you piece together a lot of information from separate sources. The seemingly working solution involves using parts of the older C API that are callable in Swift, unsafe pointers, and making sure the return values of the bound functions are JSValue?s. That makes sense since JavaScript functions all return an object, null, or undefined. An optional type mirrors this behavior.
Here is my work-in-progress code for anyone who might need some leads:
Just for an update, I've figured out how to mix the old C API with the new more limited Swift-specific APIs. I haven't yet made sure I'm not leaking memory, but it looks like I've found what I needed, hopefully.
In case you ever wanted to know:
#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)
}
}
Here are a couple helpful references:
pointers in Swift
manual memory management in Swift

Related

Issue Using Swift's withUnsafeMutableBytes Wrapper

I'm trying to write an extension to Data that allows me to extract it in various casts.
I'm running into a strange problem that I can't quite figure out.
Before I get into generics, I'm trying out fixed data types, and I created this method:
func intValueFromData(_ inData: Data) -> Int64? {
var number = Int64(0)
let len = Swift.min(MemoryLayout<Int64>.size, inData.count)
_ = withUnsafeMutableBytes(of: &number) {
inData.copyBytes(to: $0, from: 0..<len)
}
return number
}
That works. If I do this:
var int_64 = Int64(12345)
var data = Data(bytes: &int_64, count: MemoryLayout<Int64>.size)
let fetched = intValueFromData(data)
fetched becomes "12345" as an Int64.
However, when I try to embed the same method into the Data type, like so:
extension Data {
mutating func intValueFromData() -> Int64? {
var number = Int64(0)
let len = Swift.min(MemoryLayout<Int64>.size, self.count)
_ = withUnsafeMutableBytes(of: &number) {
self.copyBytes(to: $0, from: 0..<len)
}
return number
}
}
I get a compile-time error that says that "withUnsafeMutableBytes(of: &number)" is not supposed to have an argument.
The last time that I encountered something like this, it turned out that Apple explicitly blocked a functionality, but neglected to tell us in a straightforward manner.
I am not an expert at this kind of thing, but I am wondering if anyone could shed any light on why withUnsafeMutableBytes(of: &number) behaves differently inside the extension.
In the context of Data, withUnsafeMutableBytes refers to Data.withUnsafeMutableBytes(_:).
To disambiguate this and refer to the global function, explicitly prefix the module name: _ = Swift.withUnsafeMutableBytes(of: &number) {
Just to complete the set, here's the generic function I created:
/* ###################################################################################################################################### */
// MARK: - Data Extension -
/* ###################################################################################################################################### */
/**
This extension adds the ability to extract data fron a Data instance, cast into various types.
*/
public extension Data {
/* ################################################################## */
/**
This method allows a Data instance to be cast into various standard types.
- parameter inValue: This is an inout parameter, and the type will be used to determine the cast.
- returns: the cast value (the parameter will also be set to the cast value). Can be ignored.
*/
#discardableResult
mutating func castInto<T>(_ inValue: inout T) -> T {
// Makes sure that we don't try to read past the end of the data.
let len = Swift.min(MemoryLayout<T>.size, self.count)
_ = Swift.withUnsafeMutableBytes(of: &inValue) {
self.copyBytes(to: $0, from: 0..<len)
}
return inValue
}
}

Is there an Kotlin equivalent `with` function in Swift?

In Kotlin, we could change the below
// Original code
var commonObj = ClassCommonObj()
commonObj.data1 = dataA
commonObj.data2 = dataB
commonObj.data3 = dataC
// Improved code
var commonObj = ClassCommonObj()
with(commonObj) {
data1 = dataA
data2 = dataB
data3 = dataC
}
However in Swift as below, do I have equivalent with function to use?
// Original code
var commonObj = ClassCommonObj()
commonObj.data1 = dataA
commonObj.data2 = dataB
commonObj.data3 = dataC
Unfortunately, no such functionality so far in Swift. However, similar functionality can be reached with the power of extensions:
protocol ScopeFunc {}
extension ScopeFunc {
#inline(__always) func apply(block: (Self) -> ()) -> Self {
block(self)
return self
}
#inline(__always) func with<R>(block: (Self) -> R) -> R {
return block(self)
}
}
This protocol and extension provides two inline functions, where one can be served to return processed object, and the other is strictly similar to with in Kotlin and other languages (Visual Basic supported in 90s).
Usage
Specify types which these functions should apply to:
extension NSObject: ScopeFunc {}
apply:
let imageView = UIImageView().apply {
$0.contentMode = .scaleAspectFit
$0.isOpaque = true
}
Here we create an object and once the closure is executed, modified object is returned.
with:
imageView.with {
$0.isHidden = true
}
Works equal to with in Kotlin.
Originaly based on this source code.
NOTE:
Swift compiler is generally regarded as smart enough to decide whether or not a function should be inlined. Quite likely, these two would be inlined due to their relative compactness even without strictly specifying #inline (__always). Either way, you should know that this keyword does not affect the logic and the result of these, because inlining is about optimizing the program.
Like #Hexfire said, so far, no built-in Swift equivalent to Kotlin's with(). As he points out, you can more or less write one yourself.
I use a version slightly different than the Kotlin with() that automatically returns the modified element (more akin to Kotlin's apply). I find this clearer, pithier, and more generally useful.
This is the version I use:
#discardableResult
public func with<T>(_ item: T, _ closure: (inout T) -> Void) -> T {
var mutableItem = item
closure(&mutableItem)
return mutableItem
}
It's declared globally (so no dependency on NSObject or extension declarations). It also handles mutability like I expect. In use, it looks like:
let myWellDescribedLabel = with(UILabel()) {
$0.attributedText = attributedStringTitle
$0.isAccessibilityElement = true
$0.numberOfLines = 1
}
Unfortunately (or is it? see comments), Swift does not have self syntax in closures, so you must reference the passed object as $0 (or create a named parameter to the closure).
While we're here, a withLet() that handles optionals is also very useful:
#discardableResult
public func withLet<T>(_ item: Optional<T>, _ closure: (inout T) -> Void) -> Optional<T> {
guard let item = item else { return nil }
return with(item, closure)
}
These are in a gist here.

Delegating to another initializer from a closure

I'm looking at ways to create a DispatchData instance out of a Data instance. I started with a static function:
static func dispatchData(fromData data: Data) -> DispatchData {
return data.withUnsafeBytes { (typedPtr: UnsafePointer<UInt8>) -> DispatchData in
let bufferPtr = UnsafeRawBufferPointer(start: UnsafeRawPointer(typedPtr), count: data.count)
return DispatchData(bytes: bufferPtr)
}
}
This works fine (and is coincidentally very similar to this answer). I then decided I'd like to add this as an initializer to DispatchData in an extension and wrote:
private extension DispatchData {
init(data: Data) {
data.withUnsafeBytes { (typedPtr: UnsafePointer<UInt8>) in // 1
let bufferPtr = UnsafeRawBufferPointer(start: UnsafeRawPointer(typedPtr), count: data.count)
self.init(bytes: bufferPtr)
}
}
}
At this, the compiler balked on the line marked // 1:
Variable 'self.__wrapped' captured by a closure before being initialized
It makes sense — the compiler doesn't want self to be captured before I've delegated to init(bytes: UnsafeRawBufferPointer), since stored variables — like __wrapped, in this case — aren't yet initialized. But I can't see another way to get the UnsafeRawBufferPointer; I can't return it from data.withUnsafeBytes, per the documentation:
Warning
The byte pointer argument should not be stored and used outside of the lifetime of the call to the closure.
I know I can just go back to using a static function to achieve what I want, but I'd prefer it if there were a way to add this initializer. Is there a way to make this initializer work as intended?
The problem with your extension is that although the compiler knows you're passing the closure as non-escaping argument to withUnsafeBytes(_:); it doesn't have any guarantee that it will be called once and only once (which is required for initialisation).
One simple solution is to, rather than chain to another initialiser, just construct a new instance of DispatchData yourself, and then assign this to self, thus completing initialisation:
import Foundation
private extension DispatchData {
init(data: Data) {
self = data.withUnsafeBytes { (typedPtr: UnsafePointer<UInt8>) in
DispatchData(bytes:
UnsafeRawBufferPointer(start: typedPtr, count: data.count)
)
}
}
}
Note also that there's an implicit conversion from UnsafePointer<UInt8> to UnsafeRawPointer? when passing it as an argument; so we don't need to wrap it in an initialiser call.
And in Swift 5, withUnsafeBytes gives you a UnsafeRawBufferPointer directly:
private extension DispatchData {
init(data: Data) {
self = data.withUnsafeBytes(DispatchData.init)
}
}

Store pointer to object in SQLite in Swift

I've a tree of objC/Swift objects. They represent graphical objects, which I want to query based on location and other properties.
Since SQLite supports an RTree table, I store the boundary boxes and relevant properties in tables along side a pointer to the original object. When I retrieve a result I can easily get the original object. This works fine in objC with the following conversions:
pointer to int: (intptr_t)self
int to pointer: (__bridge SomeObject *)(void *)(intptr_t)pointerValue
I'm updating the code and also converting to Swift. Unfortunately I can't find a way to do something equivalent in Swift. I've found withUnsafePointer(), but it states that the pointer is only valid inside the closure. It also doesn't seem to be possible to get the actual pointer value as an Int from UnsafePointer. It only has a hashValue property.
How can I get a pointer in Swift which I can store in SQLite?
Note: The SQLite database is in memory and I can guarantee that the objects will stay around as long as they are 'referenced' from the database. There is no need for any kind of long lived persistence.
You can use Unmanaged. For example (Swift 3):
class MyClass {
let name: String
init(name: String) {
self.name = name
}
}
func makeInt(from obj: MyClass) -> Int {
let unmanaged = Unmanaged.passUnretained(obj)
return Int(bitPattern: unmanaged.toOpaque())
}
func makeObj(from int: Int) -> MyClass? {
guard let pointer = UnsafeRawPointer(bitPattern: int) else {
return nil
}
let unmanaged = Unmanaged<MyClass>.fromOpaque(pointer)
return unmanaged.takeUnretainedValue()
}
let obj = MyClass(name: "foo")
let int = makeInt(from: obj)
if let obj2 = makeObj(from: int) {
print(obj2.name) // "foo"
}
The use of "unretained" values means that no memory management happens: it's your responsibility to keep objects alive.

Swift - access to Dictionary of a singleton causes EXC_BAD_ACCESS

I have an app managing a simple stocks portfolio. Amongst other things, it keeps a record of the required exchange rates in a dictionary, like so:
[ EURUSD=X : 1.267548 ]
This disctionary is a Dictionary property of a singleton called CurrencyRateStore.
When updating the stocks quotations, it checks for an updated exchange rate and updates the dictionary with the following code:
CurrencyRateStore.sharedStore()[symbol] = fetchedRate.doubleValue
That calls:
subscript(index: String) -> Double? {
get {
return dictionary[index]
}
set {
// FIXME: crashes when getting out of the app (Home button) and then relaunching it
dictionary[index] = newValue!
println("CurrencyRateStore - updated rate for \(index) : \(newValue!)")
}
}
The first time the app is started, it works fine.
But if I quit the app (with the Home button) and then relaunch it, the currency rates are updated again, but this time, I get a EXC_BAD_ACCESS at the line
dictionary[index] = newValue!
Here is a screenshot:
[EDIT] Here is the thread in the debug navigator:
I tried to update the dictionary without a subscript, like so:
CurrencyRateStore.sharedStore().dictionary[symbol] = fetchedRate.doubleValue
but without more success. Same if I use the function updateValue:forKey:
I didn't have the issue in Objective-C.
Thanks for your help !
[EDIT] Here is the whole class CurrencyRateStore:
class CurrencyRateStore {
// MARK: Singleton
class func sharedStore() -> CurrencyRateStore! {
struct Static {
static var instance: CurrencyRateStore?
static var token: dispatch_once_t = 0
}
dispatch_once(&Static.token) {
Static.instance = CurrencyRateStore()
}
return Static.instance!
}
// MARK: Properties
/** Dictionary of currency rates used by the portfolio, presented like [ EURUSD=X : 1.3624 ] */
var dictionary = [String : Double]()
/** Returns a sorted array of all the keys on the currency rates dictionary */
var allKeys: [String] {
var keysArray = Array(dictionary.keys)
keysArray.sort {$0 < $1}
return keysArray
}
init() {
if let currencyRateDictionary: AnyObject = NSKeyedUnarchiver.unarchiveObjectWithFile(currencyRateArchivePath) {
dictionary = currencyRateDictionary as [String : Double]
}
}
subscript(index: String) -> Double? {
get {
return dictionary[index]
}
set {
// FIXME: crashes when getting out of the app (Home button) and then relaunching it
// (ApplicationWillEnterForeground triggers updateStocks)
dictionary[index] = newValue!
println("CurrencyRateStore - updated rate for \(index) : \(newValue!)")
}
}
func deleteRateForKey(key: String) {
dictionary.removeValueForKey(key)
}
/** Removes all currency rates from the Currency rate store */
func deleteAllRates()
{
dictionary.removeAll()
}
// MARK: Archive items in CurrencyRateStore
var currencyRateArchivePath: String { // Archive path
var documentDirectories: Array = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
// Get the only document directory from that list
let documentDirectory: AnyObject = documentDirectories.first!
return documentDirectory.stringByAppendingPathComponent("currencyRates.archive")
}
func saveChanges()-> Bool
{
// return success or failure
return NSKeyedArchiver.archiveRootObject(dictionary, toFile: currencyRateArchivePath)
}
}
This looks to me like a concurrency issue. Swift dictionaries aren't thread safe, and using them from a singleton can lead to multiple reader/writer issues.
Edit: I am pretty sure this is the real answer, based on the given source/debugging dump. To correct what I wrote, specifically MUTABLE dictionaries and arrays (as well as NSMutableDictionary and NSMutableArray) aren't thread safe, and problems arise when using them within Singletons that are accessed from multiple threads, and that appears to be what the sample source code is doing, or enabling other parts of the code to do.
I don't have an Apple link discussing Swift collection class thread safety, but I"m pretty sure common knowledge. But the following tutorial on Grand Central Dispatch discusses the problem in depth and how to solve it using GCD.
http://www.raywenderlich.com/79149/grand-central-dispatch-tutorial-swift-part-1
The error, and the line itself:
dictionary[index] = newValue!
makes me think the problem is newValue being nil - and the error is caused by the forced unwrapping.
I would suggest to set a breakpoint and check its value, or otherwise print it before adding to the dict.
Moreover, it wouldn't be a bad idea to protect that statement with an optional binding:
if let value = newValue {
dictionary[index] = value
}
because if the value type is optional, it can be nil.
So in the end, I contacted Apple Technical Support.
They couldn't reproduce the issue.
I thought that maybe I don't need to save the currency rates, because during the quotes update, the function will check which currency rates it needs anyway, and repopulate the dictionary as needed.
So I deactivated the methods i created to save the CurrencyRateStore and to reload it again with NSKeyedUnarchiver.
Apparently, the crash is gone!