Search code examples
javascriptiosobjective-ccjavascriptcore

Is there a way to move a JSValue into a new JSContext ignoring its original context?


I have two JSContexts, and I want to swap JSValues between them from time to time. However, I'm having difficulty moving a JSValue to a new context, if that's possible.

I'm trying this:

newContext[@"newValue"] = [JSValue valueWithObject:newValue inContext:newContext];

While the new context now has that value, the value still retains its old context. Unfortunately, it still retains its old context. Any suggestions?


Solution

  • I suggest you extract the value of the JSValue from its old javascript context into an ordinary objective-c object before creating the new JSValue in the new context. Looking at JSValue.h shows that the JSValue class has a read-only property that holds the JSContext that the value originates from.

    I can't tell what type of value you are dealing with from your code above, but for example (for simple types):

    NSString *newString = [newValue toString]; // Extract from old JSValue
    newContext[@"newValue"] = newString;
    

    or for a more complex object:

    @protocol MyPointExports <JSExport>
    @property double x;
    @property double y;
    @end
    
    @interface MyPoint : NSObject <MyPointExports>
    // Put methods and properties not visible to JavaScript code here.
    @end
    
    newcontext[@"MyPoint"] = [MyPoint class]; // Define the class in Javascript
    
    ...
    
    MyPoint *p = [newValue toObject]; // Extract from old JSValue
    newContext[@"newValue"] = p;
    

    Note that the value will still exist in the old JSContext (the old JSContext will remain alive whilst the old value is retained). You may want to remove this reference by:

    oldContext[@"oldValue"] = nil; // Assuming the var in the oldContext was called oldValue
    

    Note also that you don't need to use the constructor:

    + (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context;
    

    since JavaScriptCore has built-in conversion for the following (see JSValue.h):

      Objective-C type  |   JavaScript type
    --------------------+---------------------
            nil         |     undefined
           NSNull       |        null
          NSString      |       string
          NSNumber      |   number, boolean
        NSDictionary    |   Object object
          NSArray       |    Array object
           NSDate       |     Date object
          NSBlock *     |   Function object *
             id **      |   Wrapper object **
           Class ***    | Constructor object ***
    
    * Instances of NSBlock with supported arguments types will be presented to
    JavaScript as a callable Function object. For more information on supported
    argument types see JSExport.h. If a JavaScript Function originating from an
    Objective-C block is converted back to an Objective-C object the block will
    be returned. All other JavaScript functions will be converted in the same
    manner as a JavaScript object of type Object.
    
    ** For Objective-C instances that do not derive from the set of types listed
    above, a wrapper object to provide a retaining handle to the Objective-C
    instance from JavaScript. For more information on these wrapper objects, see
    JSExport.h. When a JavaScript wrapper object is converted back to Objective-C
    the Objective-C instance being retained by the wrapper is returned.
    
    *** For Objective-C Class objects a constructor object containing exported
    class methods will be returned. See JSExport.h for more information on
    constructor objects.