<?xml version="1.0" encoding="us-ascii"?>
<?xml-stylesheet version="1.0" type="text/xsl" href="../../../article.xsl"?>
<page title="Coding with spidermonkey - Part II" url="http://aoeex.com/personal/2006/06/29/coding_with_spidermonkey_part_ii.php">
	<header>
		<stylesheet href="/css/personal/articles.css" />
		<stylesheet href="/css/comments/comments.css" />
	</header>
	<content xmlns:xhtml="http://www.w3.org/1999/xhtml">
		<article>
			<next href="../../07/04/coding_with_spidermonkey_part_iii.php" title="Part III --&gt;"/>
			<previous href="../07/coding_with_spidermonkey.php" title="&lt;-- Part I" />
			<body>

<xhtml:h1>Coding with Spidermonkey - Part II</xhtml:h1>

<xhtml:p>In my <xhtml:a href="../07/coding_with_spidermonkey.php">previous article</xhtml:a>, I showed you how to get a basic application working 
which successfully loads up and initializes the Spidermonkey engine.  I never did show you how to actually use this engine
though, so that is what I will do in todays article.  Below you will learn how to go about loading up and executing a javascript 
file.</xhtml:p>

<xhtml:p>So, lets start with the code we had from the previous article.  Here's a copy/paste version for those who don't have it any 
more, or are starting mid-game.  You can also download the code by clicking the file name.</xhtml:p>

<sampleCode title="engine.c" href="../07/files/engine.c">
#include &lt;stdio.h&gt;
#include &lt;jsapi.h&gt;

JSClass js_global_object_class = {
  "System",
  0,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_PropertyStub,
  JS_EnumerateStub,
  JS_ResolveStub,
  JS_ConvertStub,
  JS_FinalizeStub,
  JSCLASS_NO_OPTIONAL_MEMBERS
};

int main(int argc, char **argv){
    char *fileToRun=NULL;
    JSRuntime *rt=NULL;
    JSContext *cx=NULL;
    JSObject *obj=NULL;

    if (argc > 1){
        fileToRun = argv[1];
    }
    
    rt = JS_NewRuntime(8L*1024L);
    if (!rt){
        printf("Failed to initialize JS Runtime.\n");
        return 1;
    }

    cx = JS_NewContext(rt, 8L*1024L*1024L);
    if (!cx){
        printf("Failed to initialize JS Context.\n");
        JS_DestroyRuntime(rt);
        return 1;
    }

    obj = JS_NewObject(cx, &amp;js_global_object_class, NULL, NULL);
    if (!obj){
        printf("Failed to create global object.\n");
        JS_DestroyContext(cx);
        JS_DestroyRuntime(rt);
        return 1;
    }

    JS_InitStandardClasses(cx, obj);
    
    if (fileToRun){
        printf("At this point, we would run the file %s if we were developed more.\n", fileToRun);
    }
    else {
        printf("Usage: %s file\n", argv[0]);
    }


    JS_DestroyContext(cx);
    JS_DestroyRuntime(rt);
    return 0;
}
</sampleCode>

<xhtml:h3>Put those scripts to work</xhtml:h3>
<xhtml:p>When you compile and run that file, you'll notice it really serves no purpose at all.  We start the JS engine, then we check 
if a file name was specified.  If so, we say that we would run it if we knew how.  Otherwise we print the usage information.
Now it's time to code in the file execution so it does know how to run the file.</xhtml:p>

<xhtml:p>Executing a file in spidermonkey is actually a pretty easy process.  There are multiple methods to achieve the goal.  You can 
either read the file in yourself and pass it to spidermonkey as strings, or you can tell spider monkey to compile the file, and 
then execute the compiled file.  I chose to do the later.  I also add some error checking such as making sure the file exists and
is readable.  I wrote a simple function to encapsulate the process of executing a file into a nice single function call that 
returns either true or false, depending on if the file was successfully executed.</xhtml:p>

<xhtml:p>Here is a copy of my function.  I will explain the parts below.  Copy and paste this function into the engine.c file above
at the end of the file.  Remember to add a function prototype to the top of the file as well.  In order to use the stat()
system call, we also need to include a couple new headers.  The headers we need to add are 
<xhtml:code>#include &lt;sys/stat.h&gt;</xhtml:code> and <xhtml:code>#include &lt;sys/types.h&gt;</xhtml:code>.  Click the file name below to 
get a new copy of engine.c with the function added.</xhtml:p>

<sampleCode title="engine_r1.c" href="./files/engine_r1.c">
#include &lt;sys/types.h&gt;
#include &lt;sys/stat.h&gt;

/* Function Prototypes */
JSBool js_engine_execute_file(JSContext *ctx, const char *file);

/* The orginal code ... */

JSBool js_engine_execute_file(JSContext *ctx, const char *file){
    JSScript *script;
    jsval returnValue;
    JSObject *global = JS_GetGlobalObject(ctx);
    struct stat statinfo;

    if (file == NULL){
        return JS_FALSE;
    }

    if (stat(file, &amp;statinfo) == -1){
        return JS_FALSE;
    }

    if (!S_ISREG(statinfo.st_mode)){
        return JS_FALSE;
    }

    script = JS_CompileFile(ctx, global, file);

    if (script == NULL){
        return JS_FALSE;
    }

    return JS_ExecuteScript(ctx, global, script, &amp;returnValue);
}
</sampleCode>

<xhtml:p>So, now you have a function, <apiref name="js_engine_execute_file">js_engine_execute_file()</apiref> which can be used
to execute a javascript file.  Lets go over how this function works, a few blocks of code at a time.</xhtml:p>

<sampleCode>
    JSScript *script;
    jsval returnValue;
    JSBool returnVal;
    JSObject *global = JS_GetGlobalObject(ctx);
    struct stat statinfo;
</sampleCode>

<xhtml:p>First up, we declare the variables we need to use in order to execute the file. <parameter>script</parameter> holds the 
compiled code.  This is what spidermonkey will use to actually execute our script.  <parameter>returnValue</parameter> 
is the return value of the script.  This is basically whatever the value of the last statement in the file is.  
<parameter>returnVal</parameter> is holds the return value from JS_ExecuteScript and will be what we return from this 
function. 
<parameter>global</parameter> is the global object for the given context.  We obtain this using 
<apiref name="JS_GetGlobalObject">JS_GetGlobalObject</apiref> rather than pass it
in separately or use a global variable for convinence and to ensure we get the proper global object.  Finally we have the 
variable <parameter>statinfo</parameter> which is used to in the call to stat() to make sure the file exists and 
find out some extra info about the file before trying to execute it.</xhtml:p>

<sampleCode>
    if (file == NULL){
        return JS_FALSE;
    }

    if (stat(file, &amp;statinfo) == -1){
        return JS_FALSE;
    }

    if (!S_ISREG(statinfo.st_mode)){
        return JS_FALSE;
    }
</sampleCode>

<xhtml:p>Once the variables are declared, we do a few simple validation tests to make sure we can execute this file, hopefully 
successfully.  If the tests fail, we return JS_FALSE to tell the caller the file couldn't be executed.  Right now there isn't 
any way for the caller to know the specific reason why the file couldn't be executed.  I may add something later, but right now 
I simply don't care.</xhtml:p>

<xhtml:p>The first test is a basic check to make sure we are passed a valid pointer to a file name, and not a NULL pointer.  Second
we call stat() on the file to get information on the file, and at the same time check to see if the file exists.  If the file 
does exist, or the stat fails for some other reason, return false.  Next we check the information retreived by stat() to see 
if the filename passed is a regular file, rather than a directory or pipe or some odd thing like that.  If it is a regular file, 
we continue on, if it isn't, we return false to the caller.</xhtml:p>

<sampleCode>
    script = JS_CompileFile(ctx, global, file);
  
    if (script == NULL){
        return JS_FALSE;
    }
</sampleCode>

<xhtml:p>Next we use Spidermonkey's <apiref name="JS_CompileFile">JS_CompileFile</apiref>
to compile the file into the proper format for Spidermonkey to execute it.  This function might return NULL if it's unable 
to parse and compile the code, therefore we check for a null return value and if one is found, we return false to the caller 
to inform them that the file couldn't be executed.</xhtml:p>


<sampleCode>
    returnVal = JS_ExecuteScript(ctx, global, script, &amp;returnValue);
    JS_DestroyScript(ctx, script);
    return returnVal;
</sampleCode>

<xhtml:p>Last but not least, we use <apiref name="JS_ExecuteScript">JS_ExecuteScript</apiref> to execute the script we just compiled.  This function will return true or false depending on whether or not the 
script can be successfully executed or not (not being when there's a runtime error or such).  We need to store this in our variable 
<xhtml:span class="name">returnVal</xhtml:span> so that we may pass this information back to the caller as the return value from our 
function.  If desired, it could be captured and acted upon, but that's something best left to the caller.  Regardless of whether 
our script executed successfully or not though, we then need to destroy the compiled script using 
<apiref name="JS_DestroyScript">JS_DestroyScript</apiref>.</xhtml:p>

<xhtml:p>Now that we have our nice <xhtml:code>js_engine_execute_file()</xhtml:code> function and know how to use it, let us go fourth and implement 
it into your main code so that we can execute files!  It's really simple with this function.  You simply call it and pass in 
the name of the file to execute, which we have stored in the variable <xhtml:span class="name">fileToRun</xhtml:span>, and pass in the 
context object you want it to be executed in, which we only have one of and that is <xhtml:span class="name">cx</xhtml:span>.  
As usual, click the file name for the complete code.</xhtml:p>

<sampleCode href="./files/engine_r2.c" title="engine_r2.c">
    if (fileToRun){
        if (js_engine_execute_file(cx, fileToRun)){
          printf("File %s has been successfully executed.\n", fileToRun);
        }
        else {
          printf("Failed to executed %s for an unknown reason.\n", fileToRun);
        }
    }
</sampleCode>

<xhtml:h3>Testing your new beast</xhtml:h3>
<xhtml:p>Now, we need a simple javascript file or two to execute and make sure this works.  Remember that JS doesn't have any native 
functions or anything to output data, so really all we can do is see if the built-in objects work and make sure the file is 
successfully executed.  We need to stick to only the built-in functions since no custom objects have been created.</xhtml:p>

<xhtml:p>Here is a somewhat complex test script I wrote that tests a the Date and Math built in objects, as well as creates a function, 
calls that function, and uses variables and an object literal syntax.  It tests quite a few parts of the standard JS, but is no 
where near exhaustive.  Feel free to take this test script, or write up your own.</xhtml:p>

<xhtml:p>In case your wondering what it does, this script defines a function which will calculate the difference between two dates, 
or the elapsed time if you will.  It takes the number of seconds between two dates (or just times) and returns how many years, 
days, hours, minutes, and seconds have elapsed.  As I mentioned above, there's no native JS object to display the results though.
I test this function by creating two date objects, and setting one to be the same date as now, but last year.  I then get the 
difference between the two in seconds, and add a random amount of seconds between 0 and the current year (so as of now, 
that is adding somewhere between 0 and 2006 seconds to the difference) and then call the function.</xhtml:p>

<sampleCode title="test.js" href="./files/test.js">
var today = new Date();
var lastYear = new Date();
lastYear.setFullYear(today.getFullYear()-1);

/* Takes the difference in seconds between two times and returns an 
   object that describes how many years, days, hours, minutes, and seconds have 
   elapsed.
   */
function CalculateTimeDifference(difference){
  
  var years = Math.floor(difference/(60*60*24*365));
  difference = difference % (60*60*24*365);
  
  var days = Math.floor(difference/(60*60*24));
  difference = difference % (60*60*24);
  
  var hours = Math.floor(difference/(60*60));
  difference = difference % (60*60);
  
  var minutes = Math.floor(difference/60);
  difference = difference % 60;
  
  var seconds = difference;
  
  return {years: years, days: days, hours: hours, minutes: minutes, seconds: seconds};
}

var difference = today.getTime() - lastYear.getTime();
//Convert to seconds difference.
difference /= 1000;

//Add in some random amount of time.
difference += Math.random()*today.getFullYear();

var breakdown = CalculateTimeDifference(difference);
</sampleCode>

<xhtml:p>So, when you run this script, you should get an output similar to this:</xhtml:p>
<consoleOutput>
$./engine test.js
File test.js has been successfully executed.
</consoleOutput>

<xhtml:p>Let's introduce a JS Syntax error just to see what happens when we do.  To introduce an error, let's simply delete the word 
'<xhtml:code>function</xhtml:code>' from line 9, and then re-run the script.  You should receive a failed message like below.</xhtml:p>

<consoleOutput>
$./engine test.js
Failed to executed test.js for an unknown reason.
</consoleOutput>

				<author>
					<name>Keith Maika</name>
					<homepage>http://www.aoeex.com</homepage>
					<email username="keithm" domain="aoeex.com" />
				</author>
			</body>
			<attachments>
				<attachment href="../07/files/engine.c" title="engine.c">Here is an original copy of the code from my last article.</attachment>
				<attachment href="./files/engine_r1.c" title="engine_r1.c">Here is the first revision of the engine.c file in this article.  
    This revision adds the <xhtml:code>js_engine_execute_file()</xhtml:code> function.</attachment>
				<attachment href="./files/engine_r2.c" title="engine_r2.c">Here is the second revision of the engine.c file in this article.  
    This revision activates the new <apiref name="js_engine_execute_file">js_engine_execute_file()</apiref> function by using it in 
	<apiref name="main">main()</apiref> to executed the specified file, and return a result message.</attachment>
			</attachments>
			<apiPrototypes>
				<function name="js_engine_execute_file" return="bool">
					<parameter type="JSContext *" name="ctx" />
					<parameter type="const char *" name="file" />
				</function>
				<function name="JS_DestroyScript" return="void" docs="http://developer.mozilla.org/en/docs/JS_DestroyScript">
					<parameter type="JSContext *" name="ctx" />
					<parameter type="JSScript *" name="script" />
				</function>
				<function name="JS_ExecuteScript" return="JSBool" docs="http://developer.mozilla.org/en/docs/JS_ExecuteScript">
					<parameter type="JSContext *" name="cx" />
					<parameter type="JSObject *" name="obj" />
					<parameter type="JSScript *" name="script" />
					<parameter type="jsval *" name="rval" />
				</function>
				<function name="JS_CompileFile" return="JSScript *" docs="http://developer.mozilla.org/en/docs/JS_CompileFile">
					<parameter type="JSContext *" name="cx" />
					<parameter type="JSObject *" name="obj" />
					<parameter type="const char *" name="filename" />
				</function>
				<function name="JS_GetGlobalObject" return="JSObject *" docs="http://developer.mozilla.org/en/docs/JS_GetGlobalObject">
					<parameter type="JSContext *" name="cx" />
				</function>	
			</apiPrototypes>
			<comments pageId="3" showForm="true">
				<comment visitor="pratik" email="pratikdbl -(@)- gmail -(.)- com,st104800 -(@)- ait -(.)- ac -(.)- th"><![CDATA[Thank you.
It helped me a lot.But when I tried to compile js file at the URL (eg. 203.159.10.36/test.js) it gave error "Failed to execute for an unknown reason.". Please help me how to fix this error.]]></comment>
				<comment visitor="Gagan" email="gagan -(.)- thukral -(@)- gmail -(.)- com"><![CDATA[Hi,

Thanks for the great article.i have one doubt about compiling the scripts and calling the function from them.

example.
//js code
function(n)
{
   return n;
}

i would like to compile this code and call the function by passing the argument.

Please let me know how to do it.

thanks in advance.
Gagan]]></comment>
				<comment visitor="Keith" email="keithm -(@)- aoeex -(.)- com"><![CDATA[Look at the seventh article for info on how to execute a function once the script is compiled.

http://aoeex.com/personal/2008/01/25/coding_with_spidermonkey_part_vii.php]]></comment>
			</comments>
		</article>
	</content>
</page>

