Search code examples
javascriptobjective-cxcodemacoscore-foundation

JavaScriptCore console.log


I've put together a very simple program that uses JavaScriptCore to evaluate JS:

#import <CoreFoundation/CoreFoundation.h>
#import <JavaScriptCore/JavaScriptCore.h>

int main(int argc, const char * argv[])
{
    JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);

    FILE *f = fopen(argv[1],"r");
    char * buffer = malloc(10000000);
    fread(buffer,1,10000000,f);

    CFStringRef strs = CFStringCreateWithCString(NULL, buffer, kCFStringEncodingASCII);

    JSStringRef jsstr = JSStringCreateWithCFString(strs);
    JSValueRef result = JSEvaluateScript(ctx, jsstr, NULL, NULL, 0, NULL);

    double res  = JSValueToNumber(ctx, result, NULL);
    JSGlobalContextRelease(ctx);

    printf("%lf\n", res);
    return 0;
}

The idea here is that the last value is expected to be a Number, and that value is printed. This works for valid javascript code, such as

var square = function(x) { return x*x; }; square(4)

However, if the code tries to perform a console.log, the program segfaults. Is there a log function available in JSC or do I have to roll my own?


Solution

  • You do have to provide your own console log if using the JavaScriptCore framework from Mac or IOS.

    Here is some code that worked for me (sorry it is Objective-C rather than standard C as per your code above):

    JSContext *javascriptContext  = [[JSContext alloc] init];
    javascriptContext[@"consoleLog"] = ^(NSString *message) {
        NSLog(@"Javascript log: %@",message);
    };
    

    Then you use it from Javascript by:

    consoleLog("My debug message");
    

    Note that I have tried to define a vararg version (log taking multiple parameters) but I couldn't get this to work correctly across the framework api.

    Note that this solution uses features introduced with the new Objective-C API for the JavaScriptCore.framework introduced at the same time as IOS 7. If you are looking for an intro to this well-integrated bridge between Objective-C and Javascript, check out the 2013 WWDC introduction "Integrating JavaScript into Native Apps" session on Apple's developer network: https://developer.apple.com/videos/wwdc/2013/?id=615

    Update to answer:

    For those of you wanting to maximise your javascript code reuse without refactoring, I've managed to get a version working that declares a log of the form console.log() :

    JSContext *javascriptContext  = [[JSContext alloc] init];
    [javascriptContext evaluateScript:@"var console = {}"];
    javascriptContext[@"console"][@"log"] = ^(NSString *message) {
        NSLog(@"Javascript log: %@",message);
    };
    

    Then you use it from Javascript by:

    console.log("My debug message");