Saturday, February 23, 2008

JavaScript Core in Leopard (The Basics)

I just discovered the JavaScriptCore.framework which is new to Leopard. Creating a hello world or a similar very basic app is always the first step I take when try to learn some new programming API and the javascript framework was no exception for me. Here is an example of something entirely useless with no practical use: Defining a function using javascript which returns the sum of two numbers, then calling that javascript function from C through the JavaScriptCore.framework. Maybe I should've had it read the two numbers from stdin?

The getObjectProperty function was borrowed from another Apple sample. Remember you'll need to add the JavaScriptCore.framework to your application.

What happens in this example:

  • A javascript context is created and then the global object is obtained

  • The target javascript is converted from a NSString/CFStringRef to a JSStringRef

  • The javascript is evaluated which in this case defines the sum function.

  • The sum function is located in the global object and called with two arguments

  • The result of calling the function is converted to a number and then printed to stdout

  • javascript context is released



#import <JavaScriptCore/JavaScriptCore.h>

/* Convenience function for getting a property that is an object. */
static JSObjectRef getObjectProperty(JSContextRef ctx, JSObjectRef object, CFStringRef name)
{
JSStringRef nameJS = JSStringCreateWithCFString(name);
JSValueRef function = JSObjectGetProperty(ctx, object, nameJS, NULL);
JSStringRelease(nameJS);

if (!function || !JSValueIsObject(ctx, function))
return NULL;
return (JSObjectRef)function;
}
int main (int argc, const char * argv[])
{
JSGlobalContextRef g_context = JSGlobalContextCreate(NULL);
JSObjectRef jsGlobalObject = JSContextGetGlobalObject(g_context);
NSString *script = @"\
function sum(a,b) { return a+b; } \n\
";


JSStringRef scriptJS = JSStringCreateWithCFString((CFStringRef)script);
JSEvaluateScript(g_context, scriptJS, NULL, NULL, 0, NULL);
JSStringRelease(scriptJS);

JSObjectRef function = getObjectProperty(g_context, jsGlobalObject, CFSTR("sum"));
if(!function)
{
printf("no function called sum!\n");
}
else
{
JSValueRef arguments[] = { JSValueMakeNumber(g_context, 123.456 ), JSValueMakeNumber(g_context, 543.21 ) };
JSValueRef result = JSObjectCallAsFunction(g_context, function, NULL, 2, arguments, NULL);

printf("resulting number: %f", JSValueToNumber(g_context, result, NULL) );

}

JSGlobalContextRelease( g_context );
return 0;
}



Other resources:
2 JavaScriptCore samples from Apple (that I've found):
JSPong
JSInterpreter