Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 654
SheetJS Avatar asked Oct 29 '13 04:10

SheetJS


3 Answers

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"); 
like image 173
Mike Avatar answered Sep 28 '22 04:09

Mike


Swift 3.0

let javascriptContext = JSContext() javascriptContext?.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }") let consoleLog: @convention(block) (String) -> Void = { message in     print("console.log: " + message) } javascriptContext?.setObject(unsafeBitCast(consoleLog, to: AnyObject.self), forKeyedSubscript: "_consoleLog" as (NSCopying & NSObjectProtocol)!) 

Swift 2.1

let javascriptContext = JSContext() javascriptContext.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }") let consoleLog: @convention(block) String -> Void = { message in     print("console.log: " + message) } javascriptContext.setObject(unsafeBitCast(consoleLog, AnyObject.self), forKeyedSubscript: "_consoleLog") 

Then you use it from Javascript by:

console.log("My debug message"); 
like image 40
AlBeebe Avatar answered Sep 28 '22 05:09

AlBeebe


self.jsContext = JSContext()
self.jsContext.evaluateScript(...)       
let logFunction: @convention(block) (String) -> Void  = { (string: String) in
    print(string)
}

self.jsContext.setObject(logFunction, forKeyedSubscript: "consoleLog" as NSCopying & NSObjectProtocol)
like image 21
Anton Belousov Avatar answered Sep 28 '22 04:09

Anton Belousov