Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
1059 lines (868 sloc) 77.4 KB
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Harmless Programmer]]></title>
<link href="http://SevInf.github.com/atom.xml" rel="self"/>
<link href="http://SevInf.github.com/"/>
<updated>2013-08-03T23:51:16+03:00</updated>
<id>http://SevInf.github.com/</id>
<author>
<name><![CDATA[Sergej Tatarincev]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[Extending cocos2d JS bindings. Part 1: automatic bindings]]></title>
<link href="http://SevInf.github.com/blog/2012/12/01/extending-cocos2d-js-bindings-part-1-automatic-bindings/"/>
<updated>2012-12-01T11:07:00+02:00</updated>
<id>http://SevInf.github.com/blog/2012/12/01/extending-cocos2d-js-bindings-part-1-automatic-bindings</id>
<content type="html"><![CDATA[<p>Since version 2.1 <a href="http://www.cocos2d-iphone.org">cocos2d-iphone</a> comes with ability to write code in JavaScript.
This allows you to write your code once and then reuse the code with cocos2d-x or cococs2d-html5.
All cocos2d nodes, CocosDension, CocosBuilder reader and Chipmunk are supported. But what about third parties?
Do we need to rewrite other great libraries in JS to use them? No. And here <a href="https://github.com/zynga/jsbindings">jsbindings</a> project comes for the rescue.</p>
<p>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
<a href="https://github.com/vlidholt/CCBReader/tree/master/CCScrollView">CCScrollView</a>. At this tutorial we&#8217;ll explore
automatic binding process and bind most of the CCScrollView methods to JS, next time we&#8217;ll focus on manual
bindings and complete the rest.</p>
<!-- more -->
<h2>Preparations</h2>
<p>We will be using <a href="http://www.cocos2d-iphone.org/archives/2084">cocos2d-iphone 2.1-beta3</a>.
Download it and install Xcode templates if you haven&#8217;t yet. Create new &#8220;cocos2d iOS with JavaScript&#8221; project.</p>
<p>Next download <a href="https://github.com/vlidholt/CCBReader">CCBReader project</a> and copy CCScrollView directory to <code>libs</code>
folder in your project (same directory that contains cocos2d, CocosDension and other 3rd party libraries).</p>
<p>Finally, download <a href="https://github.com/zynga/jsbindings/archive/release-0.3.zip">JSBindings 0.3</a> and place it
somewhere on your computer. In following examples I&#8217;ll assume that its installed in home directory. If you have
installed it somewhere else, don&#8217;t forget to replace <code>~/jsbingings</code> with your path when following further
instructions.</p>
<h2>Step 1: gen_bridge_metadata</h2>
<p>As documentation suggests, step 1 is generating bridge support files for your project. Open terminal in
<code>YOUR_PROJECT/libs/CCScrollView</code> directory and execute following command:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>gen_bridge_metadata -F complete --no-64-bit -c '-DNDEBUG -I.' *.h -o CCScrollView.bridgesupport</span></code></pre></td></tr></table></div></figure>
<p>BridgeSupport is an xml file that describes C functions, ObjC classes, methods and their parameters, etc.
<code>gen_bridge_metadata</code> script uses <a href="http://clang.llvm.org/doxygen/group__CINDEX.html">libclang</a> to parse Objective-C code and generate BridgeSupport files.</p>
<p>If you look carefully through generated file you&#8217;ll see the first problems: all cocos2d classes like <code>CCNode</code>
replaced with <code>id</code>. To fix this you&#8217;ll need to tell <code>gen_bridge_metadata</code> script to include cocos2d headers
in search path. The <code>-c</code> options in above command allows to pass additional compiler arguments to libclang and
<code>-I</code> compiler flag adds directory to compiler path. So, fixed version of the command will be:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>gen_bridge_metadata -F complete --no-64-bit -c '-DNDEBUG -I. -I../cocos2d' *.h -o CCScrollView.bridgesupport</span></code></pre></td></tr></table></div></figure>
<p>Now BridgeSupport file should contain right types of parameters and properties.</p>
<h2>Step 2: complement file</h2>
<p>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:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>~/jsbindings/generate_complement.py -o CCScrollView-complement.txt *.h</span></code></pre></td></tr></table></div></figure>
<p>Script will tell you that it completed successfully, but don&#8217;t trust him: if you open the file it generated you&#8217;ll
see that it contains no data.</p>
<p>The problem is that CCScrollView files for some reason have Mac OS 9 line endings (CR) and <code>generate_complement</code>
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:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>vim -c ':e ++ff=mac | :setlocal ff=unix | :wq' CCScrollView.h
</span><span class='line'>vim -c ':e ++ff=mac | :setlocal ff=unix | :wq' CCScrollView.m</span></code></pre></td></tr></table></div></figure>
<p>Let&#8217;s run a <code>generate_complement</code> command again:</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Inf:CCScrollView$ ~/jsbindings/generate_complement.py -o CCScrollView-complement.txt *.h
</span><span class='line'>Traceback (most recent call last):
</span><span class='line'> File "/Users/Inf/jsbindings/generate_complement.py", line 183, in &lt;module&gt;
</span><span class='line'> instance.parse()
</span><span class='line'> File "/Users/Inf/jsbindings/generate_complement.py", line 97, in parse
</span><span class='line'> raise Exception("Fatal: Unparented attrib: %s (%s)" % (str(property_attribs.groups()), filename))
</span><span class='line'>Exception: Fatal: Unparented attrib: ('nonatomic, assign', 'CGFloat', None, None, None, 'zoomScale') (CCScrollView.h)</span></code></pre></td></tr></table></div></figure>
<p>Now the problem is the following: to parse code <code>generate_complement</code> 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.</p>
<p>Open CCScrollView.h header and find <code>CCScrollViewDelegate</code> protocol declaration. Change it from this:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="k">@protocol</span> <span class="nc">CCScrollViewDelegate</span>
</span><span class='line'><span class="o">&lt;</span>
</span><span class='line'> <span class="n">NSObject</span>
</span><span class='line'><span class="o">&gt;</span>
</span></code></pre></td></tr></table></div></figure>
<p>to this:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="k">@protocol</span> <span class="nc">CCScrollViewDelegate</span><span class="o">&lt;</span><span class="n">NSObject</span><span class="o">&gt;</span>
</span></code></pre></td></tr></table></div></figure>
<p>Then, change <code>CCScrollView</code> class declaration from this:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="k">@interface</span> <span class="nc">CCScrollView</span>
</span><span class='line'>: <span class="nc">CCLayer</span> <span class="p">{</span>
</span></code></pre></td></tr></table></div></figure>
<p>to this:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="k">@interface</span> <span class="nc">CCScrollView</span>: <span class="nc">CCLayer</span> <span class="p">{</span>
</span></code></pre></td></tr></table></div></figure>
<p>Run the same command third time and you&#8217;ll finally get a complement file.</p>
<h2>Step 3: Config file</h2>
<p>Finally, lets write a config file and generate some bindings. Create <code>CCScrollView.jsb.ini</code> file in
<code>libs/CCScrollView</code> directory with a following contents:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="p">[</span><span class="n">CCScrollView</span><span class="p">]</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#1</span>
</span><span class='line'><span class="n">obj_class_prefix_to_remove</span> <span class="o">=</span> <span class="n">CC</span>
</span><span class='line'>
</span><span class='line'><span class="n">classes_to_parse</span> <span class="o">=</span> <span class="n">CCScrollView</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#2</span>
</span><span class='line'><span class="n">class_properties</span> <span class="o">=</span> <span class="n">CCLayer</span> <span class="o">=</span> <span class="n">manual</span><span class="p">,</span>
</span><span class='line'> <span class="n">CCNode</span> <span class="o">=</span> <span class="n">manual</span><span class="p">,</span>
</span><span class='line'> <span class="n">NSObject</span> <span class="o">=</span> <span class="n">manual</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#3</span>
</span><span class='line'><span class="n">inherit_class_methods</span> <span class="o">=</span> <span class="n">Auto</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#4</span>
</span><span class='line'><span class="n">import_files</span> <span class="o">=</span> <span class="n">CCScrollView</span><span class="p">.</span><span class="n">h</span><span class="p">,</span> <span class="n">js_bindings_cocos2d_ios_classes</span><span class="p">.</span><span class="n">h</span>
</span><span class='line'>
</span><span class='line'><span class="n">method_properties</span> <span class="o">=</span>
</span><span class='line'><span class="cp">#5</span>
</span><span class='line'> <span class="n">CCScrollView</span> <span class="err">#</span> <span class="nl">viewWithViewSize:container:</span> <span class="o">=</span> <span class="nl">name:</span> <span class="s">&quot;create&quot;</span><span class="p">;</span> <span class="nl">merge:</span> <span class="s">&quot;viewWithViewSize:&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="n">CCScrollView</span> <span class="err">#</span> <span class="nl">initWithViewSize:container:</span> <span class="o">=</span> <span class="nl">name:</span> <span class="s">&quot;initWithViewSize&quot;</span><span class="p">;</span> <span class="nl">merge:</span> <span class="s">&quot;initWithViewSize:&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="n">CCScrollView</span> <span class="err">#</span> <span class="nl">setContentOffset:animated:</span> <span class="o">=</span> <span class="nl">name:</span> <span class="s">&quot;setContentOffset&quot;</span><span class="p">;</span> <span class="nl">merge:</span> <span class="s">&quot;setContentOffset:&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="n">CCScrollView</span> <span class="err">#</span> <span class="nl">setZoomScale:animated:</span> <span class="o">=</span> <span class="nl">name:</span> <span class="s">&quot;setZoomScale&quot;</span><span class="p">;</span> <span class="nl">merge:</span> <span class="s">&quot;setZoomScale:&quot;</span><span class="p">,</span>
</span><span class='line'><span class="cp">#6</span>
</span><span class='line'> <span class="n">CCScrollView</span> <span class="err">#</span> <span class="nl">setContentOffset:animatedInDuration:</span> <span class="o">=</span> <span class="nl">name:</span> <span class="s">&quot;setContentOffsetInDuration&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="n">CCScrollView</span> <span class="err">#</span> <span class="nl">setZoomScale:animatedInDuration:</span> <span class="o">=</span> <span class="nl">name:</span> <span class="s">&quot;setZoomScaleInDuration&quot;</span><span class="p">,</span>
</span><span class='line'><span class="cp">#7</span>
</span><span class='line'> <span class="n">CCScrollView</span> <span class="err">#</span> <span class="n">delegate</span> <span class="o">=</span> <span class="n">ignore</span><span class="p">,</span>
</span><span class='line'> <span class="n">CCScrollView</span> <span class="err">#</span> <span class="nl">setDelegate:</span> <span class="o">=</span> <span class="n">ignore</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#8</span>
</span><span class='line'><span class="n">struct_properties</span> <span class="o">=</span> <span class="n">CGPoint</span> <span class="o">=</span> <span class="n">manual</span><span class="p">,</span>
</span><span class='line'> <span class="n">CGRect</span> <span class="o">=</span> <span class="n">manual</span><span class="p">,</span>
</span><span class='line'> <span class="n">CGSize</span> <span class="o">=</span> <span class="n">manual</span>
</span><span class='line'>
</span><span class='line'><span class="cp">#9</span>
</span><span class='line'><span class="n">bridge_support_file</span> <span class="o">=</span> <span class="n">CCScrollView</span><span class="p">.</span><span class="n">bridgesupport</span>
</span><span class='line'><span class="n">complement_file</span> <span class="o">=</span> <span class="n">CCScrollView</span><span class="o">-</span><span class="n">complement</span><span class="p">.</span><span class="n">txt</span>
</span></code></pre></td></tr></table></div></figure>
<p>Explanations:</p>
<ol>
<li>We tell generator to remove CC prefix from class names. Later, we will register binded class under <code>cc</code>
namespace, so <code>CCScrollView</code> will be accessible as <code>cc.ScrollView</code> in JS code;</li>
<li>We tell that we already have manual bindings for <code>CCLayer</code>, <code>CCNode</code> and <code>NSObject</code>. Cocos2d bindings already
covers this classes, but without this line generator will try to generate them again. <code>manual</code> option prevents
generator from this behaviour while still allowing remaining code to use this classes;</li>
<li>Generated bindings will inherit class methods from the base class until virst class constructor encoutered;</li>
<li>All binding files will include two headers:
<ol>
<li><code>CCScrollView.h</code> to access our native class;</li>
<li><code>js_bindings_cocos2d_ios_classes.h</code> to access bindings for <code>CCLayer</code>, <code>CCNode</code> and <code>NSObject</code>.</li>
</ol>
</li>
<li>Two important things happens here:
<ol>
<li>Diffrent name for the methods in JS is set using <code>name</code> option. This allows to use JS-friendly names in
our script code and brings comatability with
<a href="https://github.com/cocos2d/cocos2d-html5/tree/master/extensions/GUI/CCScrollView">cocos2d-html5 implementation</a>;</li>
<li>Multiple native methods are merged into single JS method using <code>merge</code> 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. <strong>Note</strong>: method with largest number of argments should be on a
left side of expression.</li>
</ol>
</li>
<li>Just rename a few methods to be compatable with HTML5 version;</li>
<li>Don&#8217;t generate bindings for delegate getter and setter. Currently, jsbindings can&#8217;t generate glue code for
protocols. In the following article we change this setting to <code>manual</code> and write binding ourself, but for now we
will just ignore the methods.</li>
<li>Tell generator that we&#8217;ll have manual bindings for <code>CGPoint</code>, <code>CGRect</code> and <code>CGSize</code>. 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.</li>
<li>Setting the path to the BridgeSupport and complement files generated on previous steps.</li>
</ol>
<p>Now lets run binding generator:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="o">~/</span><span class="n">jsbindings</span><span class="o">/</span><span class="n">generate_js_bindings</span><span class="p">.</span><span class="n">py</span> <span class="o">-</span><span class="n">c</span> <span class="n">CCScrollView</span><span class="p">.</span><span class="n">jsb</span><span class="p">.</span><span class="n">ini</span>
</span><span class='line'><span class="n">NOT</span> <span class="nl">OK:</span> <span class="s">&quot;CCScrollView#delegate&quot;</span> <span class="nl">Error:</span> <span class="n">Explicitly</span> <span class="n">ignoring</span> <span class="n">method</span>
</span><span class='line'><span class="n">NOT</span> <span class="nl">OK:</span> <span class="s">&quot;CCScrollView#initWithViewSize:&quot;</span> <span class="nl">Error:</span> <span class="n">Explicitly</span> <span class="n">ignoring</span> <span class="n">method</span>
</span><span class='line'><span class="n">NOT</span> <span class="nl">OK:</span> <span class="s">&quot;CCScrollView#setContentOffset:&quot;</span> <span class="nl">Error:</span> <span class="n">Explicitly</span> <span class="n">ignoring</span> <span class="n">method</span>
</span><span class='line'><span class="n">NOT</span> <span class="nl">OK:</span> <span class="s">&quot;CCScrollView#setDelegate:&quot;</span> <span class="nl">Error:</span> <span class="n">Explicitly</span> <span class="n">ignoring</span> <span class="n">method</span>
</span><span class='line'><span class="n">NOT</span> <span class="nl">OK:</span> <span class="s">&quot;CCScrollView#setZoomScale:&quot;</span> <span class="nl">Error:</span> <span class="n">Explicitly</span> <span class="n">ignoring</span> <span class="n">method</span>
</span><span class='line'><span class="n">NOT</span> <span class="nl">OK:</span> <span class="s">&quot;CCScrollView#viewWithViewSize:&quot;</span> <span class="nl">Error:</span> <span class="n">Explicitly</span> <span class="n">ignoring</span> <span class="n">method</span>
</span></code></pre></td></tr></table></div></figure>
<p>For some reason it reports ignored methods as errors. Looks like a bug in <code>generate_js_bindings</code> script. Just
ignore this errors for now, correct binding files will be generated anyway.</p>
<h2>Step 4: Registration file</h2>
<p>Now we need to create a function that will register our bindings with JS engine. This part is done
manually.</p>
<p>Create <code>js_bindings_CCScrollView_registration.h</code> with the following content:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="cp">#ifndef __JSB_CCSCROLLVIEW_REGISTRATION</span>
</span><span class='line'><span class="cp">#define __JSB_CCSCROLLVIEW_REGISTRATION</span>
</span><span class='line'>
</span><span class='line'><span class="kt">void</span> <span class="n">jsb_register_CCScrollView</span><span class="p">(</span> <span class="n">JSContext</span> <span class="o">*</span><span class="n">_cx</span><span class="p">,</span> <span class="n">JSObject</span> <span class="o">*</span><span class="n">globalO</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="cp">#endif</span>
</span></code></pre></td></tr></table></div></figure>
<p>Create Create <code>js_bindings_CCScrollView_registration.mm</code> with the following content:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="cp">#import &quot;js_bindings_config.h&quot;</span>
</span><span class='line'><span class="cp">#import &quot;js_bindings_core.h&quot;</span>
</span><span class='line'><span class="cp">#import &quot;js_bindings_CCScrollView_classes.h&quot;</span>
</span><span class='line'><span class="cp">#import &quot;js_bindings_CCScrollView_registration.h&quot;</span>
</span><span class='line'>
</span><span class='line'><span class="kt">void</span> <span class="nf">jsb_register_CCScrollView</span><span class="p">(</span> <span class="n">JSContext</span> <span class="o">*</span><span class="n">_cx</span><span class="p">,</span> <span class="n">JSObject</span> <span class="o">*</span><span class="n">globalO</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//1</span>
</span><span class='line'> <span class="n">jsval</span> <span class="n">ns</span><span class="p">;</span>
</span><span class='line'> <span class="n">JS_GetProperty</span><span class="p">(</span><span class="n">_cx</span><span class="p">,</span> <span class="n">globalO</span><span class="p">,</span> <span class="s">&quot;cc&quot;</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ns</span><span class="p">);</span> <span class="c1">//2</span>
</span><span class='line'> <span class="n">JSObject</span><span class="o">*</span> <span class="n">CCScrollView</span> <span class="o">=</span> <span class="n">JSVAL_TO_OBJECT</span><span class="p">(</span><span class="n">ns</span><span class="p">);</span> <span class="c1">//3</span>
</span><span class='line'><span class="cp"> </span>
</span><span class='line'><span class="cp">#import &quot;js_bindings_CCScrollView_classes_registration.h&quot; </span><span class="c1">//4</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>Some knowledge of <a href="https://developer.mozilla.org/en-US/docs/SpiderMonkey">Spider Monkey API</a> required to
understand this code. We dive more deeply into JSAPI in the next article when we&#8217;ll be discussing manual bindings,
now I give only breif explanation:</p>
<ol>
<li>Registration function receives two parameters:
<ol>
<li>JSContext - central part of JSAPI. It maintains call stack, contains global object and required by
almost every JSAPI function.</li>
<li>Global object - object, that contains all other objects, avaliable to the scripts. For example, <code>window</code>
inside web browser is a global object for it.</li>
</ol>
</li>
<li>Getting <code>cc</code> 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.</li>
<li>Now, we have a namespace property, but it can have value of any type in JS: number, string, object, etc.
Autegenerated registration functions require namespace to be an object named <code>CCScrollView</code> (generally,
namespace variable should be the same as section header in config file). So, we convert the value to satisfy
requirements.</li>
<li>Importing files with autegenrated functions that register all the classes.</li>
</ol>
<h2>Step 5: Adding files to a project</h2>
<p>Open Xcode project and add following files to it:
* <code>js_bindings_CCScrollView_classes.h</code>;
* <code>js_bindings_CCScrollView_classes.mm</code>;
* <code>js_bindings_CCScrollView_classes_registration.h</code>;
* <code>js_bindings_CCScrollView_registration.mm</code>;
* <code>js_bindings_CCScrollView_registration.mm</code>.</p>
<p>Open <code>libs/jsbindings/src/manual/js_bindings_config.h</code> and add following lines to it:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="cp">#ifndef JSB_INCLUDE_CCSCROLLVIEW</span>
</span><span class='line'><span class="cp">#define JSB_INCLUDE_CCSCROLLVIEW 1</span>
</span><span class='line'><span class="cp">#endif</span>
</span></code></pre></td></tr></table></div></figure>
<p>This will enable compilation of our bindings.</p>
<p>Open <code>libs/jsbindings/src/manual/js_bindings_core.mm</code> and add followig import to it:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="cp">#import &quot;js_bindings_CCScrollView_registration.h&quot;</span>
</span></code></pre></td></tr></table></div></figure>
<p>Then find <code>createRuntime</code> method and add following lines to it
<strong>after</strong> cocos2d registration:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='objective-c'><span class='line'><span class="cp">#if JSB_INCLUDE_CCSCROLLVIEW</span>
</span><span class='line'> <span class="n">jsb_register_CCScrollView</span><span class="p">(</span><span class="n">_cx</span><span class="p">,</span> <span class="n">_object</span><span class="p">);</span>
</span><span class='line'><span class="cp">#endif</span>
</span></code></pre></td></tr></table></div></figure>
<p>Step 6: Constants file and test:</p>
<p>Add jsb_constants_ccscrollview.js file to resources of your application:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">cc</span><span class="p">.</span><span class="nx">SCROLLVIEW_DIRECTION_HORIZONTAL</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span><span class='line'><span class="nx">cc</span><span class="p">.</span><span class="nx">SCROLLVIEW_DIRECTION_VERTICAL</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span><span class='line'><span class="nx">cc</span><span class="p">.</span><span class="nx">SCROLLVIEW_DIRECTION_BOTH</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>
<p>Its just the same constants that defined in <code>CCScrollViewDirection</code> enum. jsbindings doesn&#8217;t support enums,
so all constants should be redefined in JS.</p>
<p>Now its time to test the results. Replace <code>Resources/main.js</code> file content with the following code:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">require</span><span class="p">(</span><span class="s2">&quot;jsb_constants.js&quot;</span><span class="p">);</span>
</span><span class='line'><span class="nx">require</span><span class="p">(</span><span class="s2">&quot;jsb_constants_ccscrollview.js&quot;</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="kd">var</span> <span class="nx">MainLayer</span> <span class="o">=</span> <span class="nx">cc</span><span class="p">.</span><span class="nx">Layer</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
</span><span class='line'> <span class="nx">ctor</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">cc</span><span class="p">.</span><span class="nx">associateWithNative</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">cc</span><span class="p">.</span><span class="nx">Layer</span><span class="p">);</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">init</span><span class="p">();</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">winSize</span> <span class="o">=</span> <span class="nx">cc</span><span class="p">.</span><span class="nx">Director</span><span class="p">.</span><span class="nx">getInstance</span><span class="p">().</span><span class="nx">getWinSize</span><span class="p">();</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">container</span> <span class="o">=</span> <span class="nx">cc</span><span class="p">.</span><span class="nx">LayerGradient</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">cc</span><span class="p">.</span><span class="nx">c4b</span><span class="p">(</span><span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">255</span><span class="p">),</span>
</span><span class='line'> <span class="nx">cc</span><span class="p">.</span><span class="nx">c4b</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">));</span>
</span><span class='line'> <span class="nx">container</span><span class="p">.</span><span class="nx">setContentSize</span><span class="p">(</span><span class="nx">cc</span><span class="p">.</span><span class="nx">size</span><span class="p">(</span><span class="nx">winSize</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span> <span class="mi">1000</span><span class="p">));</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">scrollView</span> <span class="o">=</span> <span class="nx">cc</span><span class="p">.</span><span class="nx">ScrollView</span><span class="p">.</span><span class="nx">create</span><span class="p">(</span><span class="nx">winSize</span><span class="p">,</span> <span class="nx">container</span><span class="p">);</span>
</span><span class='line'> <span class="nx">scrollView</span><span class="p">.</span><span class="nx">setDirection</span><span class="p">(</span><span class="nx">cc</span><span class="p">.</span><span class="nx">SCROLLVIEW_DIRECTION_VERTICAL</span><span class="p">);</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">addChild</span><span class="p">(</span><span class="nx">scrollView</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">});</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="kd">function</span> <span class="nx">run</span><span class="p">()</span>
</span><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">scene</span> <span class="o">=</span> <span class="nx">cc</span><span class="p">.</span><span class="nx">Scene</span><span class="p">.</span><span class="nx">create</span><span class="p">();</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">layer</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">MainLayer</span><span class="p">();</span>
</span><span class='line'> <span class="nx">scene</span><span class="p">.</span><span class="nx">addChild</span><span class="p">(</span> <span class="nx">layer</span> <span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="nx">cc</span><span class="p">.</span><span class="nx">Director</span><span class="p">.</span><span class="nx">getInstance</span><span class="p">().</span><span class="nx">runWithScene</span><span class="p">(</span> <span class="nx">scene</span> <span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nx">run</span><span class="p">();</span>
</span></code></pre></td></tr></table></div></figure>
<p>If everything was done right, you should see nice scrolling and bouncing gradient.</p>
<h2>The end</h2>
<p>First part of tutorial is over, code can be found on <a href="https://github.com/SevInf/CCScrollView">GitHub</a>, but it
uses different folder structure. Next time we&#8217;ll dive into manual binding process and allow CCScrollView to have
JavaScript delegate.</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Esprima tutorial]]></title>
<link href="http://SevInf.github.com/blog/2012/09/29/esprima-tutorial/"/>
<updated>2012-09-29T12:27:00+03:00</updated>
<id>http://SevInf.github.com/blog/2012/09/29/esprima-tutorial</id>
<content type="html"><![CDATA[<p>Recently I had a task that sounded something like this:</p>
<blockquote><p>Get a JS code and generate some other JS code from it.</p></blockquote>
<p>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 <a href="http://esprima.org/">Esprima</a> parser.
Now I want to share my experience with it in a form of tutorial. We won&#8217;t be building code generator - I think
simpler task will be enough to dive in.</p>
<!-- more -->
<h2>What we&#8217;ll build</h2>
<p>We will build a very simple static analyzer which will run from command line.
It will warn about:</p>
<ul>
<li>Declared, but not called functions;</li>
<li>Calls to undeclared functions;</li>
<li>Functions declared multiple times.</li>
</ul>
<p>Of course, it&#8217;s not intended for compelling with excellent <a href="http://www.jshint.com/">JSHint</a> 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:</p>
<ul>
<li>Recognize any form of function declaration except:</li>
</ul>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">function</span> <span class="nx">name</span><span class="p">(...)</span> <span class="p">{</span>
</span><span class='line'> <span class="p">...</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<ul>
<li>Recognize any form of function call except:</li>
</ul>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">name</span><span class="p">(...)</span>
</span></code></pre></td></tr></table></div></figure>
<ul>
<li>Know something about imports, exports or predefined globals;</li>
<li>Work with multiple files.</li>
</ul>
<p>This is not the flaws of a parser. All this features can be easily implemented, they are just out of scope of
tutorial.</p>
<p>Example will be built using NodeJS, but Esprima works also in a browser.</p>
<h2>Preparations</h2>
<p>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&#8217;ll give examples only for Unix-like
OS terminal.
Create a directory for your project and install the library:</p>
<pre><code>mkdir esprima-tutorial
cd esprima-tutorial
npm install esprima@0.9.9
</code></pre>
<p>Create a script named analyze.js with the following content:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">var</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;fs&#39;</span><span class="p">),</span>
</span><span class='line'> <span class="nx">esprima</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;esprima&#39;</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="kd">function</span> <span class="nx">analyzeCode</span><span class="p">(</span><span class="nx">code</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="c1">// 1</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// 2</span>
</span><span class='line'><span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">argv</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Usage: analyze.js file.js&#39;</span><span class="p">);</span>
</span><span class='line'> <span class="nx">process</span><span class="p">.</span><span class="nx">exit</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">// 3</span>
</span><span class='line'><span class="kd">var</span> <span class="nx">filename</span> <span class="o">=</span> <span class="nx">process</span><span class="p">.</span><span class="nx">argv</span><span class="p">[</span><span class="mi">2</span><span class="p">];</span>
</span><span class='line'><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Reading &#39;</span> <span class="o">+</span> <span class="nx">filename</span><span class="p">);</span>
</span><span class='line'><span class="kd">var</span> <span class="nx">code</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="nx">filename</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'><span class="nx">analyzeCode</span><span class="p">(</span><span class="nx">code</span><span class="p">);</span>
</span><span class='line'><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Done&#39;</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>
<p>What happens here:</p>
<ol>
<li>We declare a function where we will be doing all code parsing and analysis stuff. For now its empty;</li>
<li>We are checking that user passes command-line argument with a file name to analyze. Why we checking for the third argument? The answer is in <a href="http://nodejs.org/api/process.html#process_process_argv">node documentation</a>:
<blockquote><p>The first element will be &#8216;node&#8217;, the second element will be the name of the JavaScript file.
The next elements will be any additional command line arguments.</p></blockquote></li>
<li>We are reading the file content and passing it to <code>analyzeCode</code>. For simplicity, I use sync version of
<code>fs.readFile</code>.</li>
</ol>
<h2>Parsing code and walking through AST</h2>
<p>Parsing can be done with a single line of code:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">function</span> <span class="nx">analyzeCode</span><span class="p">(</span><span class="nx">code</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">ast</span> <span class="o">=</span> <span class="nx">esprima</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">code</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p><code>esprima.parse</code> accepts string or node&#8217;s <code>Buffer</code> 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.</p>
<p>The result of the <code>esprima.parse</code> code will be an abstract syntax tree (AST) of your program in
<a href="https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API">Spider Monkey Parser API format</a>.</p>
<p>AST is a representation of a program code in a tree structure. For example, if we have the expression:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="mi">2</span> <span class="o">*</span> <span class="mi">3</span>
</span></code></pre></td></tr></table></div></figure>
<p>AST can be graphically represented as:</p>
<p><img src="http://SevInf.github.com/assets/images/ast_example.png" alt="AST Example" /></p>
<p>Same expression in Parser API format will look like:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="s2">&quot;type&quot;</span><span class="o">:</span> <span class="s2">&quot;Program&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="s2">&quot;body&quot;</span><span class="o">:</span> <span class="p">[</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="s2">&quot;type&quot;</span><span class="o">:</span> <span class="s2">&quot;ExpressionStatement&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="s2">&quot;expression&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="s2">&quot;type&quot;</span><span class="o">:</span> <span class="s2">&quot;BinaryExpression&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="s2">&quot;operator&quot;</span><span class="o">:</span> <span class="s2">&quot;*&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="s2">&quot;left&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="s2">&quot;type&quot;</span><span class="o">:</span> <span class="s2">&quot;Literal&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="s2">&quot;value&quot;</span><span class="o">:</span> <span class="mi">2</span>
</span><span class='line'> <span class="p">},</span>
</span><span class='line'> <span class="s2">&quot;right&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="s2">&quot;type&quot;</span><span class="o">:</span> <span class="s2">&quot;Literal&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="s2">&quot;value&quot;</span><span class="o">:</span> <span class="mi">3</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">]</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>The main entity of <code>esprima.parse</code> output is node. Each node has a type (root node is always has <code>Program</code> type),
0 or more properties and 0 or more subnodes. <code>type</code> is the only common property for nodes - names of the
other properties and subnodes depends on it.</p>
<p>In above example, Program has the only one subnode - <code>body</code> of <code>ExpressionStatement</code> type which too contains only
<code>expression</code> subnode of type <code>BinaryExpression</code>. <code>BinaryExpression</code> has property <code>operator</code> with value of &#8220;*&#8221; and
<code>left</code> and <code>right</code> <code>Literal</code> subnodes.</p>
<p>To be able to analyze the code we need a way to loop throught AST. Add following code before <code>analyzeCode</code> function:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">function</span> <span class="nx">traverse</span><span class="p">(</span><span class="nx">node</span><span class="p">,</span> <span class="nx">func</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">func</span><span class="p">(</span><span class="nx">node</span><span class="p">);</span><span class="c1">//1</span>
</span><span class='line'> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">key</span> <span class="k">in</span> <span class="nx">node</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//2</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">key</span><span class="p">))</span> <span class="p">{</span> <span class="c1">//3</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">child</span> <span class="o">=</span> <span class="nx">node</span><span class="p">[</span><span class="nx">key</span><span class="p">];</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">child</span> <span class="o">===</span> <span class="s1">&#39;object&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">child</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//4</span>
</span><span class='line'>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="nb">Array</span><span class="p">.</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">child</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">child</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">node</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//5</span>
</span><span class='line'> <span class="nx">traverse</span><span class="p">(</span><span class="nx">node</span><span class="p">,</span> <span class="nx">func</span><span class="p">);</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">traverse</span><span class="p">(</span><span class="nx">child</span><span class="p">,</span> <span class="nx">func</span><span class="p">);</span> <span class="c1">//6</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>This function accepts root node and a function and calls it recursively for each node in a tree.</p>
<ol>
<li>Calling the function for a root node;</li>
<li>Loop through all properties of a root a node;</li>
<li>Ignore inherited properties of an object;</li>
<li>Ignore simple and null properties. All nodes are actually complex objects and all properties has simple type.
Null check is necessary, because <code>typeof null === 'object'</code>;</li>
<li>Each child can be either single subnode or array of subnodes. If its an array, we call <code>traverse</code> recursively
for each subnode in it.</li>
<li>If child is not an array, just call <code>traverse</code> recurively on it.</li>
</ol>
<p>To test the function replace <code>analyzeCode</code> function with the following code:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">function</span> <span class="nx">analyzeCode</span><span class="p">(</span><span class="nx">code</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">ast</span> <span class="o">=</span> <span class="nx">esprima</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">code</span><span class="p">);</span>
</span><span class='line'> <span class="nx">traverse</span><span class="p">(</span><span class="nx">ast</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">node</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">type</span><span class="p">);</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>When you execute you script on some JS code you should see types of all nodes in a tree.</p>
<h2>Getting data for analysis</h2>
<p>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:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="s2">&quot;type&quot;</span><span class="o">:</span> <span class="s2">&quot;FunctionDeclaration&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="s2">&quot;id&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="s2">&quot;type&quot;</span><span class="o">:</span> <span class="s2">&quot;Identifier&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;myAwesomeFunction&quot;</span>
</span><span class='line'> <span class="p">},</span>
</span><span class='line'> <span class="s2">&quot;params&quot;</span><span class="o">:</span> <span class="p">[</span>
</span><span class='line'> <span class="p">...</span>
</span><span class='line'> <span class="p">],</span>
</span><span class='line'> <span class="s2">&quot;body&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="s2">&quot;type&quot;</span><span class="o">:</span> <span class="s2">&quot;BlockStatement&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="s2">&quot;body&quot;</span><span class="o">:</span> <span class="p">[</span>
</span><span class='line'> <span class="p">...</span>
</span><span class='line'> <span class="p">]</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>Type of a node is <code>FunctionDeclaration</code>. Identifier is stored in <code>id</code> subnode and name of the function is in a
<code>name</code> property of this node. <code>params</code> and <code>body</code> contain parameters and body of the function. Rest of the node
properties is omitted.</p>
<p>Second node is CallExpression:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="s2">&quot;expression&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="s2">&quot;type&quot;</span><span class="o">:</span> <span class="s2">&quot;CallExpression&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="s2">&quot;callee&quot;</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="s2">&quot;type&quot;</span><span class="o">:</span> <span class="s2">&quot;Identifier&quot;</span><span class="p">,</span>
</span><span class='line'> <span class="s2">&quot;name&quot;</span><span class="o">:</span> <span class="s2">&quot;myAwesomeFunction&quot;</span>
</span><span class='line'> <span class="p">},</span>
</span><span class='line'> <span class="s2">&quot;arguments&quot;</span><span class="o">:</span> <span class="p">[]</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p><code>callee</code> is an object being called. We are interested only in callee with <code>Identifier</code> type. Few other possible
types are <code>MemberExpression</code> (call of the object method) and <code>FunctionExpression</code> (self invoking function).</p>
<p>Now we have all information to perform the analysis. Replace <code>analyzeCode</code> function with:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">function</span> <span class="nx">analyzeCode</span><span class="p">(</span><span class="nx">code</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">ast</span> <span class="o">=</span> <span class="nx">esprima</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">code</span><span class="p">);</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">functionsStats</span> <span class="o">=</span> <span class="p">{};</span> <span class="c1">//1</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">addStatsEntry</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">funcName</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//2</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">functionsStats</span><span class="p">[</span><span class="nx">funcName</span><span class="p">])</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">functionsStats</span><span class="p">[</span><span class="nx">funcName</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="nx">calls</span><span class="o">:</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">declarations</span><span class="o">:</span><span class="mi">0</span><span class="p">};</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">};</span>
</span><span class='line'>
</span><span class='line'> <span class="nx">traverse</span><span class="p">(</span><span class="nx">ast</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">node</span><span class="p">)</span> <span class="p">{</span> <span class="c1">//3</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="s1">&#39;FunctionDeclaration&#39;</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">addStatsEntry</span><span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">id</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span> <span class="c1">//4</span>
</span><span class='line'> <span class="nx">functionsStats</span><span class="p">[</span><span class="nx">node</span><span class="p">.</span><span class="nx">id</span><span class="p">.</span><span class="nx">name</span><span class="p">].</span><span class="nx">declarations</span><span class="o">++</span><span class="p">;</span>
</span><span class='line'> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="s1">&#39;CallExpression&#39;</span> <span class="o">&amp;&amp;</span> <span class="nx">node</span><span class="p">.</span><span class="nx">callee</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="s1">&#39;Identifier&#39;</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">addStatsEntry</span><span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">callee</span><span class="p">.</span><span class="nx">name</span><span class="p">);</span>
</span><span class='line'> <span class="nx">functionsStats</span><span class="p">[</span><span class="nx">node</span><span class="p">.</span><span class="nx">callee</span><span class="p">.</span><span class="nx">name</span><span class="p">].</span><span class="nx">calls</span><span class="o">++</span><span class="p">;</span> <span class="c1">//5</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<ol>
<li>Creating a new empty object that will store calls and declarations statistics for each function. Function name
will be the key of statistics entry;</li>
<li>Declaring a function that will add an empty entry for the function name to the <code>functionStats</code> object,
if we haven&#8217;t done this previously;</li>
<li>Begin loop through the AST;</li>
<li>If we found function declaration, increase declaration count;</li>
<li>If we found function call by name, increase call.</li>
</ol>
<h2>Processing results</h2>
<p>Now the final part, processing the results we gathered and reporting all found issues.</p>
<p>Add following function:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">function</span> <span class="nx">processResults</span><span class="p">(</span><span class="nx">results</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">name</span> <span class="k">in</span> <span class="nx">results</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">results</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">name</span><span class="p">))</span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">stats</span> <span class="o">=</span> <span class="nx">results</span><span class="p">[</span><span class="nx">name</span><span class="p">];</span>
</span><span class='line'> <span class="k">if</span> <span class="p">(</span><span class="nx">stats</span><span class="p">.</span><span class="nx">declarations</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Function&#39;</span><span class="p">,</span> <span class="nx">name</span><span class="p">,</span> <span class="s1">&#39;undeclared&#39;</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">stats</span><span class="p">.</span><span class="nx">declarations</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Function&#39;</span><span class="p">,</span> <span class="nx">name</span><span class="p">,</span> <span class="s1">&#39;decalred multiple times&#39;</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">stats</span><span class="p">.</span><span class="nx">calls</span> <span class="o">===</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">&#39;Function&#39;</span><span class="p">,</span> <span class="nx">name</span><span class="p">,</span> <span class="s1">&#39;declared but not called&#39;</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>I think the code is pretty self-explanatory and doesn&#8217;t need my comments.
Finally, add call to <code>processResults</code> to the bottom of <code>analyzeCode</code> function:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="nx">processResults</span><span class="p">(</span><span class="nx">functionsStats</span><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>
<h2>Testing</h2>
<p>Run the script on this meaningless code:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='javascript'><span class='line'><span class="kd">function</span> <span class="nx">declaredTwice</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kd">function</span> <span class="nx">main</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">undeclared</span><span class="p">();</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kd">function</span> <span class="nx">unused</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kd">function</span> <span class="nx">declaredTwice</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="nx">main</span><span class="p">();</span>
</span></code></pre></td></tr></table></div></figure>
<p>You should see something like this:</p>
<pre><code>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
</code></pre>
<p>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.</p>
<h2>Conclusion</h2>
<p>Its time to end the tutorial. We&#8217;ve learnt how to parse JS code using Esprima, the format of SpiderMonkey Parse API
syntax tree. Also, we&#8217;ve learn how to traverse through AST and how to search for syntax constructions we
interested in.</p>
<h2>Useful links</h2>
<ul>
<li><a href="https://github.com/SevInf/esprima-tutorial">Source code for the tutorial</a>;</li>
<li><a href="http://esprima.org/">Esprima library</a>;</li>
<li><a href="https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API">SpiderMonkey Parser API</a>;</li>
<li><a href="http://esprima.org/demo/parse.html">Online parser demo</a>.</li>
</ul>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[Intro]]></title>
<link href="http://SevInf.github.com/blog/2012/09/29/intro/"/>
<updated>2012-09-29T11:50:00+03:00</updated>
<id>http://SevInf.github.com/blog/2012/09/29/intro</id>
<content type="html"><![CDATA[<p>Hi everyone! This is my first blog post.
Who am I? I&#8217;m a software engineer at <a href="http://bekitzur.com/">BeKitzur</a> (site under construction at the moment).
I work on various projects, mostly mobile apps. I code in Objective-C, Java, JS (browsers and node.js). I also
make short video games occasionally, but this is more hobby than a professional activity. Here is two games which
I made in collaboration with a few great people for ludum dare
competition:</p>
<ul>
<li><a href="http://www.ludumdare.com/compo/minild-35/?action=preview&amp;uid=13588">Mazes In The Sky</a>;</li>
<li><a href="http://www.ludumdare.com/compo/ludum-dare-24/?action=preview&amp;uid=13588">Super Darwin</a>.</li>
</ul>
<p>So the blog will mostly about it - mobile development, javascript, node and simple game development.
I don&#8217;t expect to post frequently - I rarely have an idea for a good technical article. But hope you find
something useful here anyway.</p>
]]></content>
</entry>
</feed>