Coder Social home page Coder Social logo

jsbindings's Introduction

Please note: This project is deprecated at Zynga and is no longer maintained.


JavaScript Bindings for C and Objective-C

Introduction

JavaScript Bindings for C / Objective-C (JSB) is the "glue" code (or wrapper code) that sits between native code (C or Objective-C) and JavaScript (JS) code. JSB allows calling native code from JS and vice-versa.

That means that you can interact with your favorite native library from JS. As an example, you could create a cocos2d particle system in JS, while its logic and rendering will be executed natively. Or you could create a Chipmunk Physics world in JS, while the whole physics simulation, including the collisions detections, would run natively.

JSB layer

The JS code is interpreted by SpiderMonkey, Mozilla's JS virtual machine (VM). It uses the latest stable version of SpiderMonkey (as of this writing it is v19.0). The JS VM is extended by JSB to support custom types, custom structures and Objective-C objects.

JSB has a flexible set of rules that could be used to select the classes, methods, functions and structs to parse or ignore; which methods are callbacks; and renaming rules among some of its features. To ease the creation of these rules, it supports regular expressions.

Major features

Highlights of JSB:

  • Supports any Objective-C / C library
  • Automatically generates the JS bindings ("glue" code)
  • No need to modify the source code of your libraries, or the autogenerated code
  • Powerful set of rules and plugins to customize the generated JS API
  • Automatically converts JS objects/types into Objective-C objects/structs/ types and vice-versa
  • Supports "subclassing" native objects in JS
  • Supports callbacks
  • Automatically generates object oriented JS API from C libraries (provided that the C API is OO, like Chipmunk)

Creating your own bindings

Quick steps: Bindings Generation

  1. Download JSB

     $ git clone git://github.com/zynga/jsbindings.git
    
  2. Generate the BridgeSupport files for your project. Let's assume that your project is CocosDenshion

     $ cd ~/src/CocosDenshion/CocosDenshion
     $ gen_bridge_metadata -F complete --no-64-bit -c '-DNDEBUG -I.' *.h -o ~/some/path/CocosDenshion.bridgesupport
    
  3. Generate complement files for your project

     $ cd ~/src/CocosDenshion/CocosDenshion
     $ ~/jsb/generate_complement.py -o ~/some/path/CocosDenshion-complement.txt *.h
    
  4. Create a JSB config file for your project

     $ vim ~/some/path/CocosDenshion_jsb.ini
    
  5. Run the JSB generator script

     $ ~/jsb/generate_jsb.py -c ~/som/path/CocosDenshion_jsb.ini
    
  6. Include the recently autogenerated files and JSB source files in your Xcode project

  7. Include SpiderMonkey and JRSwizzle in your Xcode project

The CocosDenshion config files could be found here: configs/CocosDenshion

Quick steps: Adding the bindings to your project

  1. Include all the autogenerated files to your Xcode project. eg:

    src/auto/js_bidnings_PROJECTNAME_classes.*, src/auto/jsb_PROJECTNAME_functions.*, src/auto/jsb_PROJECTNAME_auto_classes.*,

  2. Include the following manual files to your Xcode project.

    src/manual/jsb_config.h, src/manual/jsb_core.*, src/manual/basic_conversions.*.

  3. For objective-c projects (not required for C-only projects like Chipmunk), you should include the following files:

    src/manual/js_bindigns_NS_manual.*

  4. Add a registration file. Name it: src/manual/jsb_PROJECTNAME_registration.mm. See src/manual/jsb_chipmunk_registration.mm as an example.

  5. Edit src/manual/jsb_core.mm and add a call your registration code. eg:

     // JSBCore class
     -(id) init
     {
     	// ...
     	JSB_register_PROJECTNAME(_cx, _object);
     	// ...
     }
    
  6. If you need to create manual JS bindings for your project, put those files int the src/manual directory. Name them:

    /src/manual/js_bidnings_PROJECTNAME_manual.*

  7. If you need to further extend your code from JS, create a JavaScript file (eg: see src/js/jsb_constants_chipmunk.js), and place it in the src/js directory.

  8. Add a #define to include your new bindings in src/manual/jsb_config.h. eg:

#ifndef JSB_INCLUDE_PROJECTNAME
#define JSB_INCLUDE_PROJECTNAME 1
#endif // JSB_INCLUDE_PROJECTNAME

Steps in detail

JSB comes with a python script called generate_jsb.py that generates the glue code. It needs a configuration file that contains the parsing rules and the BridgeSupport files.

BridgeSupport files are generated by a script called gen_bridge_metadata that is part of OS X, and generates xml files with information like class names, method names, arguments, return values, internals of the structs, constants, etc.

gen_bridge_metadata, internally, uses clang to parse the native code. The output is very reliable, but unfortunately, it is not complete: class hierarchy, protocols and properties data are missing. That's why JSB comes with another python script, called generate_js_complement.py, that generates the missing information.

Once we have the configuration file setup, we can run the generate_jsb.py script to generate the glue code.

To summarize, the structure of a JSB configuration file is:

  • Parsing rules (optional): renaming rules, classes to ignore / parse, etc...
  • BridgeSupport files (required): Class, methods, functions, structs information
  • Complement files (required for Objective-C projects): Hierarchy, protocol and properties information

"glue" code generation

The configuration file

The configuration has a set of powerful rules that could transform the native API into a customized JS API. Let's take a look at some of them.

Renaming rule

The renaming rule allows us to rename a method names or class names or function names or struct names. As an example, the default JS API for:

// CCAnimation (from cocos2d-iphone v2.0)
+(id) animationWithAnimationFrames:(NSArray*)arrayOfAnimationFrames delayPerUnit:(float)delayPerUnit loops:(NSUInteger)loops;

would be:

// ugly
cc.CCAnimation.animationWithAnimationFrames_delayPerUnit_loops_( frames, delay, loops );

So, with a simple set of rules, we can rename that JS API into this one:

// more JS friendly
cc.Animation.create( frames, delay, loops );

In order to do that, we need to remove the CC prefix from the class name, since it is already using the cc JS namespace:

obj_class_prefix_to_remove = CC

And finally we add a rename rule for that method:

method_properties = CCAnimation # animationWithAnimationFrames:delayPerUnit:loops: = name:"create",

Merge rule

But what happens with the other constructors of CCAnimation ?

// CCAnimation supports 4 different constructors
+(id) animation; //
+(id) animationWithSpriteFrames:(NSArray*)arrayOfSpriteFrameNames;
+(id) animationWithSpriteFrames:(NSArray*)arrayOfSpriteFrameNames delay:(float)delay;
+(id) animationWithAnimationFrames:(NSArray*)arrayOfAnimationFrames delayPerUnit:(float)delayPerUnit loops:(NSUInteger)loops;

What we should do, is to create a rule that merges the 4 constructors into one. JSB will call the correct one depending on the number of arguments. This is how the rule should look:

method_properties =  CCAnimation # animationWithAnimationFrames:delayPerUnit:loops: =  name:"create"; merge: "animation" | "animationWithSpriteFrames:" | "animationWithSpriteFrames:delay:",

And the code in JS will look like:

// calls [CCAnimation animation]
var anim = cc.Animation.create(); 

// calls [CCAnimation animnationWithSpriteFrames:]
var anim = cc.Animation.create(array);

// calls [CCAnimation animnationWithSpriteFrames:delay:]
var anim = cc.Animation.create(array, delay);

// calls [CCAnimation animationWithAnimationFrames:delayPerUnit:loops:]
var anim = cc.Animation.create(array, delay, loops);

Callback rule

JSB supports callback methods. In order to register a method as callback you should add a callback rule in the configuration file. eg:

method_properties = CCNode#onEnter = callback,

You could also rename a callback by doing:

method_properties = CCLayer # ccTouchesBegan:withEvent: = callback; name:"onTouchesBegan",

Configuration examples

In order to learn more about the configuration file, use the following working examples as a guideline:

Internals of the JS bindings ("glue" code)

The JS bindings code allows to call JS code from native and vice-versa. It forwards native callbacks to JS, and JS calls to native. Let's see them in detail:

Calling native functions from JS

The following code will call the the native C function ccpAdd():

var p1 = cc.p(0,0);
var p2 = cc.p(1,1);
// cc.pAdd is a "wrapped" function, and it will call the cocos2d ccpAdd() C function
var ret = cc.pAdd(p1, p2); 

Let's take a look at the native declaration of ccpAdd:

CGPoint ccpAdd(const CGPoint v1, const CGPoint v2);

So when cc.pAdd is executed, it will call the "glue" function code JSB_ccpAdd. And JSB_ccpAdd does:

  • converts the arguments from JS to native
  • calls the native ccpAdd() function
  • converts the return value from native to JS
  • it fails if there are errors converting either the arguments or the return value.

function call flow

Calling native instance / class methods from JS

It is also possible to call instance or class methods from JS. The internal logic is similar to calling native functions. Let's have look:

// Creates a sprite and sets its position to 200,200
var sprite = cc.Sprite.create('image.png');
sprite.setPosition( cc.p(200,200) );

cc.Sprite.create("image.png") will call the "glue" function JSB_CCSprite_spriteWithFile_rect__static, which does:

  • converts the JS String into a native string
  • creates a native instance of CCSprite by calling [CCSprite spriteWithFile:@"image.png"]
  • converts the native instance into a JS Object
  • Adds the newly created instance into a dictionary using the JS object as key
  • returns the converted instance to JS.

And sprite.setPosition(cc.p(200,200)) will call the "glue" function JSB_CCNode_setPosition_, which does:

  • Obtains the native instance from the dictionary. The JS Object is used as key.
  • Converts the point JS object to CGPoint
  • calls [instance setPosition:p]
  • Since setPosition: has no return value, it returns a "void" object to JS

class instantiation flow

instance method flow

Calling JS code from native

Callbacks

There are 2 types of calls that can be generated:

  • Calls that are originated in JS and executes native code (the ones presented earlier)
  • Calls that are originated in native and executes JS code. These ones are the "callbacks".

As an example, cocos2d-iphone's onEnter, onExit, udpate are callback functions. onEnter is originated on native, and it should also call possible "overrides" in JS. On the following example, onEnter will be called from native, but only if we declare onEnter as "callback":

var MyLayer = cc.Layer.extend({
    ctor:function () {
        cc.associateWithNative( this, cc.Layer );
        this.init();
    },
    onEnter:function() {
    	cc.log("onEnter called");
   	},
});

JSB supports callbacks without the need to modify the source code of the parsed library. What JSB does, is to swap (AKA "swizzle") the original callback method with one provided by JSB. This is the full flow:

  • At registration time, JSB swizzles all methods registered as callbacks
  • When a swizzle method is called:
    • It calls the native callback function
    • Then it calls the JS callback function (if available)

Callbacks flow

Executing scripts

In order to run a JS script from native, you should do the following

// JSBCore is responsible registering the native objects in JS, among other things
if( ! [[JSBCore sharedInstance] runScript:@"my_js_script.js"] )
	NSLog(@"Error running script");

Who is using JSB?

JSB has been used successfully in the following open source projects:

cocos2d-iphone v2.1 comes with 5 demo projects that uses it.

Demo projects:

It is noteworthy that thanks to the powerful set of rules, the JS API generated for cocos2d-iphone + Chipmunk + OpenGL ES 2.0 is exactly the same as the cocos2d-html5 + ChipmunkJS + WebGL API.

Bugs / Limitations

As of this writing, these are the current bugs and/or limitations. For an updated list of bugs/limitations, please visit JSB homepage

  • No JS debugger. Remote debugging capabilities will be added soon.
  • No JS profiler for performance analysis.
  • Native objects control the life of JS objects (only on bindings generated after Objective-C code)
    • It means that native objects might get released while their JS counterpart is still live
    • This logic is flawed since a JS object might point to an already released native object under certain not-so-common situations
    • The proper solution is to control the life of native objects from JS objects. Fix in progress
    • The workaround is to send the retain message to the object. eg: sprite.retain();
  • Callbacks support only BOOL or void return values.
  • BridgeSupport constants are not being parsed by the script.
  • The gen_bridge_metadata file that is bundled with OS X 10.6 (or older) should be avoided. It is recommended to generate the BridgeSupport files in OS X 10.8 (Mountain Lion).

jsbindings's People

Contributors

carlomorgantinizynga avatar colesbury avatar folecr avatar joshgarnett avatar many20 avatar mvarie avatar ricardoquesada avatar rohankuruvilla avatar rolandoam avatar wu-hao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jsbindings's Issues

sre_constants.error: nothing to repeat

I keep getting this error when Parsing the Parse SDK for iOS, this is my config file

[Parse]

You can extend the functionality by using plugins

Basically you can override the functionality of the converters

Ignore constants for CocosDenshion

plugin = constant_class = JSBGenerateConstants_Ignore,

JS namespace. By default it is the same as config file namespace.

This namespace is used then the script autogenerates JS code. eg: with the constants

Not used in CocosDenshion

js_namespace = parse

prefix that will be removed from the ObjC classes in order to generate the JS classes

ex: If the class name is CCNode, then on JavaScript it will be Node

CocosDenshion classes start with 'CD'

obj_class_prefix_to_remove = CD

Classes to generate. Use '*' to generate all the parsed classes.

Subclasses will be parsed as well.

ex: If CCSprite is parsed, then CCNode and NSObject will be parsed as well

It supports regular expressions to match class names.

eg: all cocos2d actions ending in 'By': CC(.*)By

Only parse 'SimpleAudioEngine'

classes_to_parse = *

Classes to ignore.

It is a good idea to add classes that you don't want to have a proxy object. Usually internal classes,

or classes that are already native on JavaScript should be added here.

It supports regular expressions to match class names.

eg: all NS classes: NS(.*)

Ignore the rest of the classes

classes_to_ignore = NS(.*)

Class properties

Options

manual Means that the class is manually bound. No binding info will generated, but it can be used.

class_properties = NSObject = manual,
NSEvent = manual

Whether or not to generate constructors from base classes for the current class.

In JavaScript, the "constructors" or "static methods" are not "inherited".

It is recommended to set it to "auto".

Options:

False: Don't inherited

True: Inherit all class methods from all ancestors

Auto: Inherit only if it has no class constructors. Stop when the fist class constructor is added.

inherit_class_methods = Auto

Files that are going to be imported by the autogenerated files.

Useful to declare subclasses of an already bound project

eg: manual bindigns for NSObject

import_files = jsb_NS_manual.h

The prefix will be removed from function name.

eg: if the C function is "cpBodyAddShape", then the JS name will be "bodyWithAddShape"

For cocos2d add "cc"

function_prefix_to_remove = PF

Free functions to be parsed

functions_to_parse =

Free functions not to parse

functions_to_ignore =

Rules used for methods:

options:

name:"js_name" Rename methods.

ignore This method will be ignored.

callback This method is a callback. It will be invoked from Objective-C

no_swizzle It means that the callback function is an optional protocol and should not be "swizzled" because there is no implementation for it.

variadic_2_array It means that all arguments are variadic, and an Array will be created with them

merge:name | name... List of methods names that should be merged with this method as well.

manual It means that its implementation won't be autogenerated. It is implemented manually

Regular expressions are supported in the Class names, but not in the Method names

method_properties =

Struct properties

options:

opaque The structure will be treated as 'opaque' and can't not be inspected from JS.

Opaque structures are much faster to generate than 'manual' or 'automatic' structures

manual Manual conversion is provided for this structures. jsval_to_structname() and structname_to_jsval shall exists in your project

struct_properties =

BridgeSupport files

add as many as you need. Usually you will only one.

bridge_support_file = Parse.bridgesupport

File that contains information that complements BridgeSupport (not present on BridgeSupport file)

complement_file = Parse-complement.txt

memory leak in JSB_core_log

JSB_core_log in src/manual/jsb_core.mm has a memory leak. It calls JS_EncodeString whose return value, according to the docs, should be JS_free'd.

wrong SpriteBatchNode initializer name

in jsbindings for cocos2d, the initializer name is initWithFileCapacity(file, capacity), instead of initWithFile(file, capacity), causing incompatibility with cocos2d-html5.

JSB_jsval_to_NSObject not converts to some types: NSArray, NSValue, etc.

Some Obj-C methods takes arguments of type id or NSObject, but in both cases this also may be such subclasses as NSArray, NSValue, NSDicationary, etc.
As i understand JSB_jsval_to_NSObject doesn't convert values to that types, and instead of it should be used JSB_jsval_to_unknown. But in all autogenerated methods is used JSB_jsval_to_NSObject.

Example of such method, where conversion to NSObject is insufficiently: [CCBAnimationManager setBaseValue:forNode:propertyName:] - it may take NSArrays, NSNumbers as first argument.

Info: Thesis on glue code generation for Spidermonkey (and C++)

During my master's thesis I created a general-purpose application for generating glue code, Bound. I started with binding code for bridging C++ and SpiderMonkey, all explained in the thesis (pdf!!). It is also based on Clang.

I didn't do anything on it the last months as I had to take a little break after the thesis. Also, it may be hacky at some points due to the limited time. Also, for simplicity of learning, I created a GUI application (though command-line was always planned).

I just thought it might be of interest to you, as you deal with the exact same topic. When I go back working on Bound, I will definitely take I a look at your project.

Good luck, with your project!!

how to convert own framework to JS as same as those in cocos2d etc.

I have some project that written in object-c,and I want to convert those to JS with JSB so that I can reuse them in cocos2d+JS.I have read the introductions and also followed, But I get troubles in the progress. I post the troubles as follows .Do i get something wrong or the JSB could't convert my own project. My mac os is 10.7.5.Looking for help.
o7njl2flmfre20tfntrip

Give better error descriptions

When there's an error compiling a script, all info I get is that it happened at the 'require' statement. In case of the typical cocos2d setup where files are included from an array, that doesn't even tell which file is causing the problem. It would be nice if we got some sort of error description from the compiler.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.