Notifying javascript from a node.js native addon (c++) - javascript

I do not have much knowledge about javascript. I have written in C++ a shared library that does certain things in a daemon thread. I needed this to be invoked from javascript. By using SWIG I've successfully able to generate a wrapper and compile my code along with it into .node module using node-gyp (wrote binding.gyp for it too). Now i can drop to node prompt and do something like:
> var a = require("./module_name")
> a.SomeCppFunction("SomeString")
and wonderfully invoke the cpp functions, start a detached thread there and return the control back to javascript. However I want to notify the javascript from the detached cpp thread about stuffs. I tried registering javascript functions by collecting function() {} signature types in void(*fp)() etc., to call them back later from c++, but that didn't work. Is there anyway to be able to achieve this ie., register javascript functions (or something else) as callback in the cpp code ?

You can use a combination of SWIG and Napi. An existing repo which does this is available here, with a blog here. But I'll sum up the process here.
Create your class to use in SWIG, which has a thread running in the threadMain method :
#include <Thread.H>
class Test : public ThreadedMethod {
void *threadMain(void);
public:
Test();
void setFnPointer(const char* s);
};
Now in the Napi code, you will generate your thread safe function like so :
Napi::ThreadSafeFunction tsfn; ///< The node api's threadsafe function
Napi::Value Start( const Napi::CallbackInfo& info ){
Napi::Env env = info.Env();
// Create a ThreadSafeFunction
tsfn = Napi::ThreadSafeFunction::New(env,
info[0].As<Napi::Function>(), // JavaScript function to call
"Resource Name", 0,1);
// return the tsfn as a pointer in a string
char addr[24];
sprintf(addr,"%p",&tsfn);
return Napi::String::New(env, addr);
}
// some small code to call NODE_API_MODULE here, check the file NapiCode.C in the repo
You compile the SWIG code to one module and the Napi code down to a different module and you pass the thread safe funciton pointer from one to the other like so :
var libNapiNodejs = require('../swig/.libs/libNapiNodejs');
let fp = libNapiNodejs.start(function () {
console.log("JavaScript callback called with arguments", Array.from(arguments));
}, 5);
// SWIG get our C++ and thread running
var libSwigCNodejs = require('../swig/.libs/libSwigCNodejs');
let test = new libSwigCNodejs.Test;
test.setFnPointer(fp); // tell swig the callback function pointer to execute
test.run(); // run the C++ thread in the SWIG module
You will see that the C++ thread calls the javascript function. This is what the C++ thread looks like in SWIG :
Napi::ThreadSafeFunction *tsfn; ///< The node api's threadsafe function
void *Test::threadMain(void){
printf("C++ Thread enter %s\n",__func__);
auto callback = []( Napi::Env env, Napi::Function jsCallback, int* value ) {
jsCallback.Call( {Napi::Number::New( env, *value )} );
};
for (int i=0; i<10; i++){
sleep(1);
if (*tsfn) {
printf("calling tsfn->BlockingCall\n");
napi_status status = tsfn->BlockingCall( &i, callback );
if ( status != napi_ok ) // Handle error
break;
}
}
tsfn->Release();
printf("C++ Thread exit %s\n",__func__);
return NULL;
}

Related

Node.js C++ add-on trying to execute javascript code with `require` function

I'm maintaining a Node.js add-on in C++.
I need to call a SQL parser (a very non-standard variant of SQL, actually) inside an asynchronously called C++ function, but unfortunately (for me) that parser has been implemented in Javascript using some Node.js libraries (the function require is being used); this is an example of the kind of javascript code I need to execute:
require("./util/SqlParser")("SELECT 1 FROM DUAL").getSyntaxTree()
I've tried writing something like this:
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::TryCatch trycatch;
v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate
,
"require(\"./util/SqlParser\")(\"SELECT 1 FROM DUAL\").getSyntaxTree()"
,
v8::NewStringType::kNormal).ToLocalChecked()
;
v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked();
v8::MaybeLocal<v8::Value> result = script->Run(context);
if( result.IsEmpty() )
{
v8::Local<v8::Value> exc = trycatch.Exception();
if( !exc.IsEmpty() )
{
auto msg = exc->ToString();
if( !msg.IsEmpty() )
throw std::string( *v8::String::Utf8Value(msg) );
}
throw "unknown error in called js function";
}
But unfortunately this doesn't work. The net outcome is the error message "ReferenceError: require is not defined": apparently, my context doesn't know anything about Node.js.
I tried to wrap the above expression in a js function parseSqlText, but this function is equally unknown to the script executor (I get the error message "ReferenceError: parseSqlText is not defined").
My question is: is there any way to get around this?
Any help would be very appreciated. I would be very glad to avoid reimplementing that parser in C++... which at present seems the only viable way to do what I need.
First, v8 is not Node.js, Node.js is built on top of v8.
v8 is a javascript engine
Thus Node.js libraries are not carried with v8 by it-self
The N-API is the answer to question. Although, normally it is used to write native C++ plugins for Node.js. The github thread belwo has some examples. Additionally, the node.js docs for N-API have also been linked.
Github Thread
Node N-API Docs
EDIT: It would seem the work has been done already, here in this repository. The writer made it so it appears to abstract all the prep work of v8 and be one line for you.
#include "node_embed.h"
int main(int argc, char** argv) {
node_context *context = nodeSetup(argc, argv);
if (context) {
nodeExecuteString(context, "let foo = 1", "__init__");
nodeExecuteString(context, "foo = 2", "__init__");
nodeExecuteString(context, "console.log(foo)", "__init__");
return nodeTeardown(context);
} else {
return 12;
}
}
The above is from the repository and will allow you to run that snippet of code.
I suggest that you go find an SQL parser that is already written in C++. I found one immediately in a ten-second search on github.com.
In my opinion, there is zero technical justification for bringing the JavaScript language into this scenario, just to be able to "parse SQL." You will not have to "write from scratch" anything at all. It will just be an object – pluck it "off the shelf," instantiate an instance of it, and use it.

Kotlin Call Javascript function in Android without webView

Is there any way we can call JS function from Kotlin without having WebView in Android?
Let's say as mentioned below I have one JS function helloJS() in test.js file,
test.js:-
function helloJS(){
return "Hello from JS"
}
And now I want to call this function from Kotlin file like
TestClass.kt:-
class TestHello{
fun getHelloFromJS(){
val name = test.helloJS()
}
}
Till now I am using Webview and loading JS file into that and getting result as call back
But, I read that Kotlin is interoperable with JS like Java
So I am curious to know if there is any way we can use that on Android without having webView
This is not possible straight forward but I found one library Execute JavaScript in Android without WebView for achieve this.
Read that blog carefully and follow below step.
Keep your JavaScript file (test.js) in assets folder in android project.
I have converted that code into Kotlin.
CallJavaScript.jks
import org.mozilla.javascript.Context
import org.mozilla.javascript.Function
import java.io.InputStreamReader
object CallJavaScript {
fun callFunction(mContext: android.content.Context): Any? {
var jsResult: Any? = null
val params = arrayOf<Any>("")
// Every Rhino VM begins with the enter()
// This Context is not Android's Context
val rhino = Context.enter()
// Turn off optimization to make Rhino Android compatible
rhino.optimizationLevel = -1
try {
val scope = rhino.initStandardObjects()
// Note the forth argument is 1, which means the JavaScript source has
// been compressed to only one line using something like YUI
val assetManager = mContext.assets
try {
val input = assetManager.open("test.js")
val targetReader = InputStreamReader(input)
rhino.evaluateReader(scope, targetReader, "JavaScript", 1, null)
} catch (e: Exception) {
e.printStackTrace()
}
// Get the functionName defined in JavaScriptCode
val obj = scope.get("helloJS", scope)
if (obj is Function) {
// Call the function with params
jsResult = obj.call(rhino, scope, scope, params)
// Parse the jsResult object to a String
val result = Context.toString(jsResult)
}
} finally {
Context.exit()
}
return jsResult
}
}
Add this line to build.gradle:
implementation 'org.mozilla:rhino:1.7R4'
In your assets folder, create a file called test.js:
function helloJS()
{
return "Hello from JS";
}
Now simply call above function from Activity.
Log.e("JS : ", CallJavaScript.callFunction(this).toString());
Output :
E/JS :: Hello from JS
It's not possible. You must not confuse the language with the platform.
Kotlin is interoperable with JS like Java
means Kotlin/JS can use and be used in a Javascript platform (Node.js or browsers). The Kotlin code compiled (transpiled) into js is able to call other js files. And external Js code can call the js code build from Kotlin. This is the interoperability with JS.
There is no interoperability between Kotlin/JS and Kotlin/JVM.
Kt looks like JS, but it is not. It will be compiled for Android runtime, not for the java script engine.
JS code require a JS runtime, but it is not in Android Runtime.
i.e. you cannot run JS directly in Java / Kt code for Android.
Am not a pro in Kotlin but Java is a pie for me. Anything you can implement in Java can be implemented in Kotlin and To execute Javascript code, I use rhino which does the job pretty easier than using the webview
Implement it:
try {
Scriptable scope = rhino.initStandardObjects();
rhino.evaluateString(scope, javaScriptCode, "JavaScript", 1, null);
Object obj = scope.get(functionNameInJavaScriptCode, scope);
if (obj instanceof Function) {
Function jsFunction = (Function) obj;
// Call the function with params
Object jsResult = jsFunction.call(rhino, scope, scope, params);
// Parse the jsResult object to a String
String result = Context.toString(jsResult);
}
}finally {
Context.exit();
}

Running IAsyncOperation from a Windows Runtime Component using JavaScript

I have a solution that has both a Windows Runtime Component (C#) and a Universal App (JS).
One of my classes in the WRC has the following static function:
public static IAsyncOperation<Project> Import()
{
return System.Threading.Tasks.Task.Run<Project>(async () =>
{
try
{
FileOpenPicker picker = new FileOpenPicker();
picker.ViewMode = PickerViewMode.List;
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.FileTypeFilter.Add(".xml");
StorageFile source = await picker.PickSingleFileAsync();
if (source != null)
{
StorageFile destination = await ApplicationData.Current.RoamingFolder.CreateFileAsync(source.Name, CreationCollisionOption.ReplaceExisting);
await source.MoveAndReplaceAsync(destination);
return await Project.Open(source.DisplayName);
}
else
{
return null;
}
}
catch (Exception)
{
return null;
}
}).AsAsyncOperation<Project>();
}
I am trying to call this function from JS using:
SignalOne.Data.Project.import().done(function () {
new Windows.UI.Popups.MessageBox("Done").showAsync();
}
However, while the "Done" message appears, the file open dialog does not. If I put a message box as the first line inside the try of the C#, it doesn't display, either.
I know I have an upper-case Import in C# and a lower-case import in JS, but that is how it comes up with Intellisense, and if I change it to upper-case in JS it crashes.
I'm sure I'm missing something small/stupid, but I can't put my finger on it.
Thanks.
As you known, if we want to use the async method in Windows Runtime Components, we should be able to use the WindowsRuntimeSystemExtensions.AsAsyncAction or AsAsyncOperation extension method to wrap the task in the appropriate interface.
You can use .NET Framework tasks (the Task class and generic Task class) to implement your asynchronous method. You must return a task that represents an ongoing operation, such as a task that is returned from an asynchronous method written in C# or Visual Basic, or a task that is returned from the Task.Run method.
For more info, see Asynchronous operations.
Also the FileOpenPicker.PickSingleFileAsync method should be run in UI thread.
In this example, the event is being fired on the UI thread. If you fire the event from a background thread, for example in an async call, you will need to do some extra work in order for JavaScript to handle the event. For more information, see Raising Events in Windows Runtime Components.
So we should be able to use CoreWindow.GetForCurrentThread method get the UI thread before the async Task is run that the async Task is not run on the UI thread.
For example:
var window = Windows.UI.Core.CoreWindow.GetForCurrentThread();
var m_dispatcher = window.Dispatcher;
Then we should be able to use the FileOpenPicker.PickSingleFileAsync method in the CoreDispatcher.RunAsync method.
For example:
await m_dispatcher.RunAsync(CoreDispatcherPriority.Normal, new DispatchedHandler(async () =>
{
var source = await picker.PickSingleFileAsync();
}));

Can I run JavaScript inside Swift code?

I need to include JavaScript code in Swift code to be able to call a signalR chat, is that possible? If not, can I convert it?
sendmessage is a button.
$(function () {
// Declare a proxy to reference the hub.
var chat = $.connection.chatHub;
// Create a function that the hub can call to broadcast messages.
chat.client.broadcastMessage = function (name, message) {
// some code
};
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendmessage').click(function () {
// Call the Send method on the hub.
chat.server.send('name', 'message');
});
});
});
and the signalr code is:
public void Send(string name, string message)
{
// Call the broadcastMessage method to update clients.
Clients.All.broadcastMessage(name, message);
}
Update #1:
changed question a little bit so it is not confusing per #MartinR
Last tested with Swift 5.1
Here is an example you can run in Playground to get you started:
import JavaScriptCore
let jsSource = "var testFunct = function(message) { return \"Test Message: \" + message;}"
var context = JSContext()
context?.evaluateScript(jsSource)
let testFunction = context?.objectForKeyedSubscript("testFunct")
let result = testFunction?.call(withArguments: ["the message"])
result would be Test Message: the message.
You also can run JavaScript code within a WKWebView calling evaluate​Java​Script(_:​completion​Handler:​).
You can also run JavaScript within a UIWebView by calling string​By​Evaluating​Java​Script(from:​), but note that that method has been deprecated and is marked as iOS 2.0–12.0.
Using JavaScriptCore framework include JavaScript code in Swift code.
The class that you’ll be dealing the most with, is JSContext. This class is the actual environment (context) that executes your JavaScript code.
All values in JSContext, are JSValue objects, as the JSValue class represents the datatype of any JavaScript value. That means that if you access a JavaScript variable and a JavaScript function from Swift, both are considered to be JSValue objects.
I strongly advise you to read the official documentation regarding the JavaScriptCore framework. 
import JavaScriptCore
var jsContext = JSContext()
// Specify the path to the jssource.js file.
if let jsSourcePath = Bundle.main.path(forResource: "jssource", ofType: "js") {
do {
// Load its contents to a String variable.
let jsSourceContents = try String(contentsOfFile: jsSourcePath)
// Add the Javascript code that currently exists in the jsSourceContents to the Javascript Runtime through the jsContext object.
self.jsContext.evaluateScript(jsSourceContents)
}
catch {
print(error.localizedDescription)
}
}
more details refer this tutorial

XPCOM C++ multithread and javascript callback

Background
In XULRunner version belowe 12.0 it's works, but when i'm trying port it to version 12.0 or higher it crash application.
Main reason is that in sdk v12 or newer developers remove proxy objects to xpcom components and recommend replace it
by wrapping objects with nsRunnable/nsIRunnable and route invocation to main thread by function NS_DispatchToMainThread (click here)
What i'm developing?
I created db connector which is async and comunicate with main thread by callbacks.
Using: XULRunner v6, porting to XULRunner v17 or above
//nsIDBCallback.idl
[scriptable, function, uuid(XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)]
interface nsIDBCallback : nsISupports {
void onInfo(in long phase, in long status, in string info);
}
//nsDBService.h, it is XPCOM component
class nsDBService : public nsIDBService, nsIRunnable
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
NS_DECL_NSIDBSERVICE
private:
std::vector<nsIThread*> threads;
std::vector<nsIDBCallback*> callbacks;
std::vector<const char*> sqls;
nsIThread* makeNewThread();
void runOperationIfNotBussy();
public:
NS_IMETHODIMP Query(const char *sql, nsIDBCallback *callback);
}
//nsDBService.cpp
// adding query and other data to buffers,
// it's thread safe, there are used mutex's
NS_IMETHODIMP nsDBService::Query(const char *sql, nsIDBCallback *callback)
{
callbacks.push_back(callback);
sqls .push_back(sql);
threads .push_back( makeNewThread() );
//run added operation if db driver is free,
//if driver is bussy then invocation is in buffer and need to wait
runOperationIfNotBussy();
return NS_OK;
}
void nsDBService::runOperationIfNotBussy()
{
//some conditions, test's etc.
//run first operation on list
// RUNNING A THREAD, still ok
if(...) threads.front()->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
}
//if this method is used by another thread+db query,
//then other operations can't run and need to wait
//operations are stored and supported like fifo
NS_IMETHODIMP nsDBService::Run(void)
{
//some other operations
//real db operations in background
int32_t phase = 3; //endphase
int32_t code = 0; //ok
const char *msg = "OK";
nsIDBCallback *callback = callbacks.pop();
//wrapping callback function with runnable interface
nsIRunnable *runCallback = new nsResultCallback(callback,
phase,
code,
msg);
//routing event to main thread
NS_DispatchToMainThread(runCallback, NS_DISPATCH_NORMAL);
runOperationIfNotBussy();
}
//nsResultCallback.h
class nsResultCallback: public nsRunnable
{
public:
NS_DECL_ISUPPORTS
public:
NS_DECL_NSIRUNNABLE
private:
nsIDBCallback* callback;
int32_t resPhase;
int32_t resStatus;
const char* resMessage;
public:
nsResultCallback(nsIDBCallback* callback,
int32_t phase,
int32_t status,
const std::string &message)
: callback(callback),
resPhase(phase),
resStatus(status),
resMessage(c_str_clone(message.c_str())) {};
~nsResultCallback();
};
//nsResultCallback.cpp
NS_IMETHODIMP nsResultCallback::Run(void)
{
nsresult rv = NS_ERROR_FAILURE;
try
{
// APP HANDS AND CRUSH !
if(this->callback) this->callback->OnInfo(resPhase, resStatus, resMessage);
}
catch(...)
{
rv = NS_ERROR_UNEXPECTED;
ERRF("nsBackpack::Run call method OnInfo from callback failed");
}
return rv;
}
INVOCATION
// *.js
nsDBService.query("SELECT * FROM t", function(phase, code, mes) {
//some UI actions or others db queries
});
Problem:
Application freeze and crash when code execution look like this:
nsDBService::Query //main thread ok
nsDBService::runOperationIfNotBussy //main thread
nsDBService::threads.front()->Dispatch //run bg thread
nsDBService:Run //bg thread
NS_DispatchToMainThread //main thread
nsResultCallback::Run //main thread
nsIDBCallback::OnInfo //main thread, crash
If code execution look like this, everything is ok:
nsDBService::Query //main thread ok
NS_DispatchToMainThread //main thread
nsResultCallback::Run //main thread
nsIDBCallback::OnInfo //main thread ok
Question:
When nsIDBCallback is invoked from NS_DispatchToMainThread and NS_DispatchToMainThread is invoked from other thread then main app thread, then execution fails, what i'm missing, don't understand? Or what is another approach for background tasks?
Cannot reproduce, as you didn't provide a self-contained, complete example, so some remarks instead:
The first thing I noticed is the cross-thread access of std::vector. You wrote something about mutexes in the comments, so this might be OK.
What is certainly wrong, is storing raw pointers to nsIDBCallback. XPCOM objects are ref-counted. So as soon your Query method returns, the underlying object might be deleted if there are no other references to it, leaving behind a dangling pointer in your vector. I think this is what is happening here!
You need to keep the object alive until the thread is done with it, preferably by putting it into a nsCOMPtr<nsIDBCallback> somewhere, e.g. in an nsCOMPArray<nsIDBCallback>.
PS: Turns out this is a somewhat old question, which I missed... So sorry for the delay answering it :p

Categories

Resources