JavaScriptCore console.log - javascript

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?

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");

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");

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)

You can debug JS file attached to context in Safari.
Steps:
1) Start Safari
2) In Safari, enable the Develop menu by going to "Preferences" -> "Advanced" -> "Show Develop menu in menu bar"
3) Go to Develop menu -> "Simulator" or name of your computer -> select "Automatically show web inspector for JSContexts" and "Automatically pause connecting to JSContexts"
4) Re-run your project and Safari should auto-show the web inspector

Swift 5.0
The other suggestions didn't work for me, so I found a web post that explains how to do it now.Essentially
let logFunction: #convention(block) (String) -> Void = { string in
print("JS_Console:", string)
}
if let console = context.objectForKeyedSubscript("console") {
console.setObject(logFunction, forKeyedSubscript: "log") // works for me
// is this needed? "console.setObject(unsafeBitCast(logFunction, to: AnyObject.self), forKeyedSubscript: "log")
}
log.console is variadic, but I could find no way to utilize it even though the link above suggests it's possible. What I did discover though is that you can use JavaScript interpolation to get values, for example:
console.log(`getCombinedFrameYaw: ${frameYaw} rot=${pathRotation}`)

Related

Calling callback inside callback in native Node

I'm a node & C++ newbie so be clement.
I'm writing a native node addon.
My addon start a webcam streaming (using UVC lib) and I want every frame to be available to node.
My CC addon do something like
uvc_start_streaming(devh, &ctrl, frameProcess, (void *) &args, 0)
Where:
devh: is the UVC device
ctrl: are device config
frameProcess: is my function callback to be called at every new frame
args: are arguments from javascript function.
The c++ callback is called every new frame and I want to simple print something like "new frame received" so my C++ is like:
void frameProcess(uvc_frame_t *frame, void *ptr) {
const FunctionCallbackInfo<Value> args = *((const FunctionCallbackInfo<Value>*)(ptr));
Isolate* isolate = args.GetIsolate();
Local<Function> cb = Local<Function>::Cast(args[0]);
const unsigned argc = 1;
Local<Value> argv[argc] = { String::NewFromUtf8(isolate, "new frame received") };
cb->Call(Null(isolate), argc, argv);
}
void testStreaming (const FunctionCallbackInfo<Value>& args) {
...
res = uvc_start_streaming(devh, &ctrl, frameProcess, (void *) &args, 0);
puts("Streaming...");
Sleep(10000); /* stream for 10 seconds */
uvc_stop_streaming(devh);
puts("Done streaming.");
...
}
...
NODE_SET_METHOD(exports, "testStreaming", testDevice);
My js is something like:
'use strict';
var uvc = require('../build/Release/binding')
uvc.testStreaming(
function (x) {
console.log(x)
}
)
The problem is that node exit without any message or error when program reach cb->Call.
If I comment cb->Call row the program run for 10 seconds (continuosly calling the ) as programmed and then exit.
But if I uncomment cb->Call the program exit immediatly.
Your frameProcess() function should call v8::Function callback in the Node.js thread, see https://stackoverflow.com/a/28116160/1355844

Need help to get this code to console application

I have a JavaScript code that I need to get into console application. The script works fine trough cmd but I want to make a console application out of it so its more user friendly. Can someone explain to me how I can write this code in console application or attach it inside the console application with links. I am new to console application so I apologize if I ask anything stupid :-)
When I use it trough cmd then I do the following;
- Run cmd.
- Type "cd downloads" and press enter.
- Type "cscript /nologo process.js log.txt 100 200" and press enter.
- Then I will get a list in the cmd window and I need to have process.js and log.txt in the download folder to make this work.
if(WScript.Arguments.Count() < 3)
{
WScript.Echo("Usage: cscript process.js <filename> <lower_value> <upper_value>");
WScript.Quit();
}
var filename = WScript.Arguments.Item(0);
var lowerBound = parseInt(WScript.Arguments.Item(1));
var upperBound = parseInt(WScript.Arguments.Item(2));
WScript.Echo("Here is the data from the file associated with the text 'verdi', where the");
WScript.Echo("number following 'verdi' is above " + lowerBound + " and below " + upperBound);
var fso = new ActiveXObject("Scripting.FileSystemObject")
var file = fso.OpenTextFile("log.txt", 1, false);
var lines = file.ReadAll().split('\r');
var failed = 0;
for(var idx in lines)
{
try
{
if(lines[idx].indexOf('verdi') > 0)
{
var tmp = lines[idx];
var regex = /verdi\s*\=\s*(\d+)/;
var result = regex.exec(tmp);
var num = parseInt(result[1]);
if(num >= lowerBound && num <= upperBound)
{
WScript.Echo(num);
}
}
}
catch(ex)
{
failed++;
}
}
if(failed > 0)
{
WScript.Echo("WARNING: one or more lines could not be processed!");
}
I have made this code in console application but it doesent work properly. I can choose the values and get the cmd to run. But I don't get the results in the window and print the result to a document.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
class Program
{
private static object cmd;
private static int verdi;
private static int s;
private static int d;
public static object WScript { get; private set; }
static void Main(string[] args)
{
//Choose lower and upper value
Console.WriteLine("Choose a lower and upper value:");
string value = Console.ReadLine();
//Choose file
Console.WriteLine("Choose a file to scan:");
string file = Console.ReadLine();
//Run the javascript code
Console.WriteLine("cd downloads");
Console.WriteLine("cscript /nologo process.js {0} {1} > mydata.txt", file, value);
string command = Console.ReadLine();
Console.WriteLine("Press any key to start scan");
System.Diagnostics.Process.Start("cmd.exe", "/C" + command);
//Quit Console Application
Console.WriteLine("Press any key to quit.");
Console.ReadKey();
}
}
}
Console.WriteLine only prints strings. It doesn't allow you to execute commands.
You could try this instead:
string command = $"cscript /nologo c:/downloads/process.js c:/downloads/{file} {lowerValue} {upperValue} > mydata.txt");
System.Diagnostics.Process.Start($"cmd.exe /C {command}");
There is also an error in process.js. That script always reads from log.txt and ignores the filename.
But why are you using two programs here? You could just have all the code in one file. And why use JavaScript for one and C# for the other?

How to display javascript console.log in eclipse console?

Title says it all. I am wondering if i can display javascript console.log in eclipse console rather than web browser's dev console?
Just found an article regarding this.
This is How it works(For Window 7).
Install Node.js javascript engine at Node.js
Open your Eclipse, in the menu
Run->External Tools->External Tools Configuration
Create new launch configuration under program category.
Set
Location : C:\WINDOWS\system32\cmd.exe
Working Directory : C:\WINDOWS\system32
Argument : /c "node ${resource_loc}"
Now create new environment variable 'node' refers to node.exe file(wherever you installed)
All done.
Redirect javascript console.logs, in Java console
Here is my solution to get javascript console messages in Java (with SWT browser)
create shell SWT and SWT browser see: Shell + Browser
create custom function SWT see: call Java from JavaScript
Add listener on error events in javascript see: mdn event error
Override console object in javascript and call custom java function (2.)
Here is my example snippet:
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.BrowserFunction;
import org.eclipse.swt.browser.LocationAdapter;
import org.eclipse.swt.browser.LocationEvent;
import org.eclipse.swt.browser.ProgressListener;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
public class Snippet307d3 {
public static final Shell createShell() {
final var display = new Display();
final var shell = new Shell(display);
shell.setText("Snippet DEBUG");
shell.setLayout(new FillLayout());
shell.setBounds(10, 10, 300, 200);
return shell;
}
public static final Browser createBrowser(Shell shell) {
try {
return new Browser(shell, SWT.NONE);
} catch (final SWTError e) {
System.out.println("Could not instantiate Browser: " + e.getMessage());
shell.getDisplay().dispose();
System.exit(-1);
return null;
}
}
public static final void runShell(Shell shell) {
shell.open();
final var display = shell.getDisplay();
while (!shell.isDisposed())
if (!display.readAndDispatch())
display.sleep();
display.dispose();
}
public static void main(String[] args) {
// -> Create shell
final var shell = createShell();
// -> Create browser
final var browser = createBrowser(shell);
browser.setJavascriptEnabled(true);
// -> set HTML or use setUrl
browser.setText(createHTML());
// browser.setUrl(URL_DOCUMENT_HTML_TEST);
// -> Create custom function
final BrowserFunction function = new CustomFunction(browser, "theJavaFunctionDebugInEclipse");
// -> Register function for cleanup
browser.addProgressListener(ProgressListener.completedAdapter(event -> {
browser.addLocationListener(new LocationAdapter() {
#Override
public void changed(LocationEvent event) {
browser.removeLocationListener(this);
System.out.println("left java function-aware page, so disposed CustomFunction");
function.dispose();
}
});
}));
// -> 6) Start shell
runShell(shell);
}
private static class CustomFunction extends BrowserFunction {
public CustomFunction(Browser browser, String name) {
super(browser, name);
}
#Override
public Object function(Object[] arguments) {
for (final Object v : arguments)
if (v != null)
System.out.println(v.toString());
return new Object();
}
}
private static String createHTML() {
return """
<!DOCTYPE>
<html lang='en'>
<head>
<title>DEBUG SWT</title>
<script>
const console = {
log : function(args) {
try {
theJavaFunctionDebugInEclipse('redirect > ' + args);
} catch (_e) {
return;
}
},
error : function(args) {
this.log(args);
},
exception : function(args) {
this.log(args);
},
debug : function(args) {
this.log(args);
},
trace : function(args) {
this.log(args);
},
info : function(args) {
this.log(args);
}
};
window.addEventListener('error', function(e) {
console.log(e.type + ' : ' + e.message);
console.log(e);
});
</script>
</head>
<body>
<input id=button type='button' value='Push to Invoke Java'
onclick='function1();'>
<p>
<a href='http://www.eclipse.org'>go to eclipse.org</a>
</p>
<script>
// bad char sequence .. send error
eeeee
function function1() {
let result;
try {
// Call bad function java .. send log
result = badFunctionJava(12, false, null, [ 3.6,
[ 'swt', true ] ], 'eclipse');
} catch (e) {
console.log('a error occurred: ' + e.message);
return;
}
}
</script>
</body>
</html>
""";
}
}
Further to #ringord's answer here, these would be the commands for your External Tools Configuration on Linux:
Location : /home/<user>/.nvm/versions/node/<version>/bin/node (or wherever you installed node)
Working Directory : /home/<user>
Arguments : ${container_loc}/${resource_name}

iOS native app, integrating with Cordova 2.9.0

I have created a native iOS app (Xcode 5.1), and I want to open via a btn, a cordova(Cordova 2.9.0) web view(Otherwise CDVViewController). I've succeed this and the web view works and it shows me the webpage, but when I embed the cordova.js (inside the webpage), the
CDVCommandQueue.m
- (void)fetchCommandsFromJs
{
// Grab all the queued commands from the JS side.
NSString* queuedCommandsJSON = [_viewController.webView stringByEvaluatingJavaScriptFromString:
#"cordova.require('cordova/exec').nativeFetchMessages()"];
NSLog(#"---- %#",queuedCommandsJSON);
[self enqueCommandBatch:queuedCommandsJSON];
if ([queuedCommandsJSON length] > 0) {
CDV_EXEC_LOG(#"Exec: Retrieved new exec messages by request.");
}
}
calls the above function and it executes the 'cordova.require('cordova/exec').nativeFetchMessages()',
this function returns
[["Device748313476","Device","getDeviceInfo",[]],["NetworkStatus748313477","NetworkStatus","getConnectionInfo",[]]]
and then it passes this value to
- (void)executePending
{
// Make us re-entrant-safe.
if (_currentlyExecuting) {
return;
}
#try {
_currentlyExecuting = YES;
for (NSUInteger i = 0; i < [_queue count]; ++i) {
// Parse the returned JSON array.
NSLog(#"%#",[_queue objectAtIndex:i]);
**NSArray* commandBatch = [[_queue objectAtIndex:i] JSONObject];**
// Iterate over and execute all of the commands.
for (NSArray* jsonEntry in commandBatch) {
CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
CDV_EXEC_LOG(#"Exec(%#): Calling %#.%#", command.callbackId, command.className, command.methodName);
if (![self execute:command]) {
#ifdef DEBUG
NSString* commandJson = [jsonEntry JSONString];
static NSUInteger maxLogLength = 1024;
NSString* commandString = ([commandJson length] > maxLogLength) ?
[NSString stringWithFormat:#"%#[...]", [commandJson substringToIndex:maxLogLength]] :
commandJson;
DLog(#"FAILED pluginJSON = %#", commandString);
#endif
}
}
}
[_queue removeAllObjects];
} #finally
{
_currentlyExecuting = NO;
}
}
My app is crashing because on this line
NSArray* commandBatch = [[_queue objectAtIndex:i] JSONObject];
doesn't recognize the value as json object and it gives me this error message
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString JSONObject]: unrecognized selector sent to instance
Thanks a lot.
Had the same recently, while was building old cordova app in new Xcode.
You should check Other linker flags in Your target settings:
For the debug build configuration You could use -ObjC flag. (What does the -ObjC linker flag do?, Why do I get a runtime exception of "selector not recognized" when linking against an Objective-C static library that contains categories?)
If after reading previous links, You still want to use this flag in release — just do it.
Otherwise, You should add -force_load ${BUILT_PRODUCTS_DIR}/libCordova.a to the release linker flag.
In order to check/edit active build configuration go to Product > Scheme > Edit scheme (Cmd <).

Javascript console.log() in an iOS UIWebView

When writing a iPhone / iPad app with a UIWebView, the console isn't visible.
this excellent answer shows how to trap errors, but I would like to use the console.log() as well.
After consulting with an esteemed colleague today he alerted me to the Safari Developer Toolkit, and how this can be connected to UIWebViews in the iOS Simulator for console output (and debugging!).
Steps:
Open Safari Preferences -> "Advanced" tab -> enable checkbox "Show Develop menu in menu bar"
Start app with UIWebView in iOS Simulator
Safari -> Develop -> i(Pad/Pod) Simulator -> [the name of your UIWebView file]
You can now drop complex (in my case, flot) Javascript and other stuff into UIWebViews and debug at will.
EDIT: As pointed out by #Joshua J McKinnon this strategy also works when debugging UIWebViews on a device. Simply enable Web Inspector on your device settings: Settings->Safari->Advanced->Web Inspector (cheers #Jeremy Wiebe)
UPDATE: WKWebView is supported too
I have a solution to log, using javascript, to the apps debug console.
It's a bit crude, but it works.
First, we define the console.log() function in javascript, which opens and immediately removes an iframe with a ios-log: url.
// Debug
console = new Object();
console.log = function(log) {
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "ios-log:#iOS#" + log);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
};
console.debug = console.log;
console.info = console.log;
console.warn = console.log;
console.error = console.log;
Now we have to catch this URL in the UIWebViewDelegate in the iOS app using the shouldStartLoadWithRequest function.
- (BOOL)webView:(UIWebView *)webView2
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
NSString *requestString = [[[request URL] absoluteString] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
//NSLog(requestString);
if ([requestString hasPrefix:#"ios-log:"]) {
NSString* logString = [[requestString componentsSeparatedByString:#":#iOS#"] objectAtIndex:1];
NSLog(#"UIWebView console: %#", logString);
return NO;
}
return YES;
}
Here's the Swift solution:
(It's a bit of a hack to get the context)
You create the UIWebView.
Get the internal context and override the console.log() javascript function.
self.webView = UIWebView()
self.webView.delegate = self
let context = self.webView.valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") as! JSContext
let logFunction : #convention(block) (String) -> Void =
{
(msg: String) in
NSLog("Console: %#", msg)
}
context.objectForKeyedSubscript("console").setObject(unsafeBitCast(logFunction, AnyObject.self),
forKeyedSubscript: "log")
Starting from iOS7, you can use native Javascript bridge. Something as simple as following
#import <JavaScriptCore/JavaScriptCore.h>
JSContext *ctx = [webview valueForKeyPath:#"documentView.webView.mainFrame.javaScriptContext"];
ctx[#"console"][#"log"] = ^(JSValue * msg) {
NSLog(#"JavaScript %# log message: %#", [JSContext currentContext], msg);
};
NativeBridge is very helpful for communicating from a UIWebView to Objective-C. You can use it to pass console logs and call Objective-C functions.
https://github.com/ochameau/NativeBridge
console = new Object();
console.log = function(log) {
NativeBridge.call("logToConsole", [log]);
};
console.debug = console.log;
console.info = console.log;
console.warn = console.log;
console.error = console.log;
window.onerror = function(error, url, line) {
console.log('ERROR: '+error+' URL:'+url+' L:'+line);
};
The advantage of this technique is that things like newlines in log messages are preserved.
Swift 5
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("your javascript string") { (value, error) in
if let errorMessage = (error! as NSError).userInfo["WKJavaScriptExceptionMessage"] as? String {
print(errorMessage)
}
}
}
Tried Leslie Godwin's fix but was getting this error:
'objectForKeyedSubscript' is unavailable: use subscripting
For Swift 2.2, here's what worked for me:
You will need to import JavaScriptCore for this code to compile:
import JavaScriptCore
if let context = webView.valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") {
context.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")
let consoleLog: #convention(block) String -> Void = { message in
print("javascript_log: " + message)
}
context.setObject(unsafeBitCast(consoleLog, AnyObject.self), forKeyedSubscript: "_consoleLog")
}
Then in your javascript code, calling console.log("_your_log_") will print in Xcode console.
Better yet, add this code as an extension to UIWebView:
import JavaScriptCore
extension UIWebView {
public func hijackConsoleLog() {
if let context = valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") {
context.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")
let consoleLog: #convention(block) String -> Void = { message in
print("javascript_log: " + message)
}
context.setObject(unsafeBitCast(consoleLog, AnyObject.self), forKeyedSubscript: "_consoleLog")
}
}
}
And then call this method during your UIWebView initialization step:
let webView = UIWebView(frame: CGRectZero)
webView.hijackConsoleLog()

Categories