While official documentation is pretty good to get started, there are few pitfalls when using this tool in real project. So, lets explore the process of binding generation step by step and allow JS coders to use CCScrollView. At this tutorial we’ll explore automatic binding process and bind most of the CCScrollView methods to JS, next time we’ll focus on manual bindings and complete the rest.
We will be using cocos2d-iphone 2.1-beta3. Download it and install Xcode templates if you haven’t yet. Create new “cocos2d iOS with JavaScript” project.
Next download CCBReader project and copy CCScrollView directory to libs
folder in your project (same directory that contains cocos2d, CocosDension and other 3rd party libraries).
Finally, download JSBindings 0.3 and place it
somewhere on your computer. In following examples I’ll assume that its installed in home directory. If you have
installed it somewhere else, don’t forget to replace ~/jsbingings
with your path when following further
instructions.
As documentation suggests, step 1 is generating bridge support files for your project. Open terminal in
YOUR_PROJECT/libs/CCScrollView
directory and execute following command:
1
|
|
BridgeSupport is an xml file that describes C functions, ObjC classes, methods and their parameters, etc.
gen_bridge_metadata
script uses libclang to parse Objective-C code and generate BridgeSupport files.
If you look carefully through generated file you’ll see the first problems: all cocos2d classes like CCNode
replaced with id
. To fix this you’ll need to tell gen_bridge_metadata
script to include cocos2d headers
in search path. The -c
options in above command allows to pass additional compiler arguments to libclang and
-I
compiler flag adds directory to compiler path. So, fixed version of the command will be:
1
|
|
Now BridgeSupport file should contain right types of parameters and properties.
Second step is generating a complement file. Complement is a file with additional metadata not provided by BridgeSupport such as class hierarchy, protocols and properties. The command is:
1
|
|
Script will tell you that it completed successfully, but don’t trust him: if you open the file it generated you’ll see that it contains no data.
The problem is that CCScrollView files for some reason have Mac OS 9 line endings (CR) and generate_complement
scripts expects Unix (LF). We can fix either the script, or convert file line endings. I choose the second path.
Execute following commands from CCScrollView directory:
1 2 |
|
Let’s run a generate_complement
command again:
1 2 3 4 5 6 7 |
|
Now the problem is the following: to parse code generate_complement
uses set of regular expressions and they are
very specific about code formatting. Again, the solution would be either to patch utility (the ideal variant would
be using libclang instead of regexps) or fix the code formatting. Again, I choose the second variant.
Open CCScrollView.h header and find CCScrollViewDelegate
protocol declaration. Change it from this:
1 2 3 4 |
|
to this:
1
|
|
Then, change CCScrollView
class declaration from this:
1 2 |
|
to this:
1
|
|
Run the same command third time and you’ll finally get a complement file.
Finally, lets write a config file and generate some bindings. Create CCScrollView.jsb.ini
file in
libs/CCScrollView
directory with a following contents:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
Explanations:
cc
namespace, so CCScrollView
will be accessible as cc.ScrollView
in JS code;CCLayer
, CCNode
and NSObject
. Cocos2d bindings already
covers this classes, but without this line generator will try to generate them again. manual
option prevents
generator from this behaviour while still allowing remaining code to use this classes;CCScrollView.h
to access our native class;js_bindings_cocos2d_ios_classes.h
to access bindings for CCLayer
, CCNode
and NSObject
.name
option. This allows to use JS-friendly names in
our script code and brings comatability with
cocos2d-html5 implementation;merge
option. This makes sense for similar
methods with different number of arguments. Glue code will choose appropriate native implementation based on
the number of arguments passed to JS function. Note: method with largest number of argments should be on a
left side of expression.manual
and write binding ourself, but for now we
will just ignore the methods.CGPoint
, CGRect
and CGSize
. Similar to the manual
classes above, cocos2d will provide this bindings. Without this bindings properties of this types could not be set
or read from JavaScript.Now lets run binding generator:
1 2 3 4 5 6 7 |
|
For some reason it reports ignored methods as errors. Looks like a bug in generate_js_bindings
script. Just
ignore this errors for now, correct binding files will be generated anyway.
Now we need to create a function that will register our bindings with JS engine. This part is done manually.
Create js_bindings_CCScrollView_registration.h
with the following content:
1 2 3 4 5 6 7 |
|
Create Create js_bindings_CCScrollView_registration.mm
with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Some knowledge of Spider Monkey API required to understand this code. We dive more deeply into JSAPI in the next article when we’ll be discussing manual bindings, now I give only breif explanation:
window
inside web browser is a global object for it.cc
property from global object. This property is a namespace for cocos2d, it contains all other
functions, classes and constants of the engine. We want to add ScrollView to the same namespace, so we need to get
a reference to it. This code assumes that it will be called after cocos2d has been registered.CCScrollView
(generally,
namespace variable should be the same as section header in config file). So, we convert the value to satisfy
requirements.Open Xcode project and add following files to it:
* js_bindings_CCScrollView_classes.h
;
* js_bindings_CCScrollView_classes.mm
;
* js_bindings_CCScrollView_classes_registration.h
;
* js_bindings_CCScrollView_registration.mm
;
* js_bindings_CCScrollView_registration.mm
.
Open libs/jsbindings/src/manual/js_bindings_config.h
and add following lines to it:
1 2 3 |
|
This will enable compilation of our bindings.
Open libs/jsbindings/src/manual/js_bindings_core.mm
and add followig import to it:
1
|
|
Then find createRuntime
method and add following lines to it
after cocos2d registration:
1 2 3 |
|
Step 6: Constants file and test:
Add jsb_constants_ccscrollview.js file to resources of your application:
1 2 3 |
|
Its just the same constants that defined in CCScrollViewDirection
enum. jsbindings doesn’t support enums,
so all constants should be redefined in JS.
Now its time to test the results. Replace Resources/main.js
file content with the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
If everything was done right, you should see nice scrolling and bouncing gradient.
First part of tutorial is over, code can be found on GitHub, but it uses different folder structure. Next time we’ll dive into manual binding process and allow CCScrollView to have JavaScript delegate.
]]>Get a JS code and generate some other JS code from it.
The first part was most difficult. Info we need from code was too complex to get using regular expressions. So the search for JS parser began. It ended pretty soon when I found Esprima parser. Now I want to share my experience with it in a form of tutorial. We won’t be building code generator - I think simpler task will be enough to dive in.
We will build a very simple static analyzer which will run from command line. It will warn about:
Of course, it’s not intended for compelling with excellent JSHint or any other static analyzer. The only purpose it serves is to show you the basics of JS parsing using Esprima. The things that our analyzer will NOT do:
1 2 3 |
|
1
|
|
This is not the flaws of a parser. All this features can be easily implemented, they are just out of scope of tutorial.
Example will be built using NodeJS, but Esprima works also in a browser.
I will be using node v0.8.10 and Esprima v0.9.9. Each of following commands can also be done with GUI or Windows Shell, but I’ll give examples only for Unix-like OS terminal. Create a directory for your project and install the library:
mkdir esprima-tutorial
cd esprima-tutorial
npm install esprima@0.9.9
Create a script named analyze.js with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
What happens here:
The first element will be ‘node’, the second element will be the name of the JavaScript file. The next elements will be any additional command line arguments.
analyzeCode
. For simplicity, I use sync version of
fs.readFile
.Parsing can be done with a single line of code:
1 2 3 |
|
esprima.parse
accepts string or node’s Buffer
object. You can also pass additional options as the second
parameter to include comments, line and column numbers, tokens, etc but this is out of the scope of this tutorial.
The result of the esprima.parse
code will be an abstract syntax tree (AST) of your program in
Spider Monkey Parser API format.
AST is a representation of a program code in a tree structure. For example, if we have the expression:
1
|
|
AST can be graphically represented as:
Same expression in Parser API format will look like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
The main entity of esprima.parse
output is node. Each node has a type (root node is always has Program
type),
0 or more properties and 0 or more subnodes. type
is the only common property for nodes - names of the
other properties and subnodes depends on it.
In above example, Program has the only one subnode - body
of ExpressionStatement
type which too contains only
expression
subnode of type BinaryExpression
. BinaryExpression
has property operator
with value of “*” and
left
and right
Literal
subnodes.
To be able to analyze the code we need a way to loop throught AST. Add following code before analyzeCode
function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
This function accepts root node and a function and calls it recursively for each node in a tree.
typeof null === 'object'
;traverse
recursively
for each subnode in it.traverse
recurively on it.To test the function replace analyzeCode
function with the following code:
1 2 3 4 5 6 |
|
When you execute you script on some JS code you should see types of all nodes in a tree.
To do the analysis we need to loop through an AST and count number of calls and declarations for each function. So, we need to know format of two node types. The first one is function declaration and it looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Type of a node is FunctionDeclaration
. Identifier is stored in id
subnode and name of the function is in a
name
property of this node. params
and body
contain parameters and body of the function. Rest of the node
properties is omitted.
Second node is CallExpression:
1 2 3 4 5 6 7 8 |
|
callee
is an object being called. We are interested only in callee with Identifier
type. Few other possible
types are MemberExpression
(call of the object method) and FunctionExpression
(self invoking function).
Now we have all information to perform the analysis. Replace analyzeCode
function with:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
functionStats
object,
if we haven’t done this previously;Now the final part, processing the results we gathered and reporting all found issues.
Add following function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
I think the code is pretty self-explanatory and doesn’t need my comments.
Finally, add call to processResults
to the bottom of analyzeCode
function:
1
|
|
Run the script on this meaningless code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
You should see something like this:
Inf:esprima-tutorial$ node analyze.js test.js
Reading test.js
Function declaredTwice decalred multiple times
Function undeclared undeclared
Function unused declared but not called
Done
Of course, if you run it on some real code you will see all the flaws we discussed at the beginning of the article. Again, point of the article is to teach how to use Esprima, not how to write static analyzers.
Its time to end the tutorial. We’ve learnt how to parse JS code using Esprima, the format of SpiderMonkey Parse API syntax tree. Also, we’ve learn how to traverse through AST and how to search for syntax constructions we interested in.
So the blog will mostly about it - mobile development, javascript, node and simple game development. I don’t expect to post frequently - I rarely have an idea for a good technical article. But hope you find something useful here anyway.
]]>