Javascript extension to use C based APIs(clutter) in a webapp - javascript

My goal is to use the C libraries to form web apps.
I have chosen the way to do that via using "SWIG" tool.
The Swig tool requires three things:
.c file which defines all the functions.
.i file also called interface file which is creating the
interface to load the APIs wherein I used the extern keyword.
APP written in Javascript extension (.js file).
I used SWIG tool to compile and run this app to verify the .js file has made correctly.
The application is running fine on XMING X11 window.
On compilation it creates _wrap.o, .o file and libFILENAME.so.
Now I want to run this app on browser page.
For this I have used the webkit clutter port which gives us the MxLauncher code.
I'm using webkit_iweb_view_load_uri(WEBKIT_IWEB_VIEW(view), "filename.html"); API to load my html file to run that Javascript on my webpage view.
I'm linking the .so created at the compilation time.
Error Message: JS CONSOLE: file:///filename.js:
ReferenceError: Can't find variable: example
filename.c
int gcd(int x, int y) `enter code here`{
int g;
g = y;
while (x > 0) {
g = x;
x = y % x;
y = g;
}
return g;
}
filename.i
%module example
extern int gcd(int x, int y);
filename.js
x = 42;
y = 105;
g = example.gcd(x,y);
How to get my goal to be achieved?

You also need to tell WebKit/JavaScriptCore at runtime about your bindings (this is in addition to linking with filename_wrap.o).
Specifically you need to bind them to the global JavaScript object (in order to invoke per your .js examples). A callback on the WebKit window can be used to get a timely reference to the global JavaScript context, and then you can register your functions onto it.
Adapting this example of hooking into the window-object-cleared signal the code could look similar to this:
/* the window callback -
fired when the JavaScript window object has been cleared */
static void window_object_cleared_cb(WebKitWebView *web_view,
WebKitWebFrame *frame,
gpointer context,
gpointer window_object,
gpointer user_data)
{
/* Add your classes to JavaScriptCore */
example_init(context); // example_init generated by SWIG
}
/* ... and in your main application set up */
void yourmainfunc()
{
....
g_signal_connect (G_OBJECT (web_view), "window-object-cleared",
G_CALLBACK(window_object_cleared_cb), web_view);
webkit_web_view_load_uri (WEBKIT_WEB_VIEW (web_view), "file://filename.html");
...
}
Depending on which branch of SWIG you are using you may need to generate the example_init function yourself (check filename.cxx); for reference here is what an initializer function to register wrapped C functions would look like in SWIG:
int example_init(JSContextRef context) {
JSObjectRef global = JSContextGetGlobalObject(context);
...
jsc_registerFunction(context, global, "gcd", _wrap_gcd);
...
}
NOTE -- SWIG does not yet officially support JavaScript; the above refers to using the work-in-progress (non-production) SWIG branches.
References:
SWIG-V8 source and its Javascript documentation
swig-jsc source and its example of registering bindings
SWIG JavaScriptCore GSoC project source (Google Summer of Code 2012)
Webkit: Extending JavaScript article-- tutorial / example code

Related

Bidirectional Javascript - Swift bridge for common business logic

I'm looking for a solution to setup a swift SDK (e.g. framework) that access shared business logic from a javascript bundle (shared with for example an android app or SDK). I don't want to use webviews etc...
shared business logic in javascript bundle
I have a Javascript bundle containing the shared logic of a project, available as an api, e.g. myBundle.js which exports several modules/functions/objects.
I want to build a native Swift app, that uses the above mentioned Swift SDK and as such can access the business logic from myBundle.js:
// module "myModule.js"
function cube(x) {
return x * x * x;
}
const foo = Math.PI + 1.0;
export { cube, foo };
Step 1: In my Swift project I would like to
import Foundation
public class MySwiftClass {
func doSomeStuffBasedOnJavascriptCode() {
// access MyBundle.js, and run 'cube' method on 'foo' from MyModule
let x = cube(foo);
NSLog("result: %l", x);
}
}
Step 2: In addition, accessing the Swift code from JS would also be very useful!
I tried: ...using Nativescript with iOS, and was able to setup a Swift app that runs the V8 runtime. This setup allows me to access the native Swift code from the javascript bundle (Nativescript with swift). So step 2 is covered by that. However, Nativescript setups the runtime internally and 'runs the main context of the bundle' (expecting it to contain an application 'run'?). It wraps the logic of the underlying V8 runtime with a very simple Nativescript interface:
Config* config = [[Config alloc] init];
config.IsDebug = isDebug;
config.LogToSystemConsole = isDebug;
config.MetadataPtr = metadataPtr;
config.BaseDir = baseDir;
config.ArgumentsCount = argc;
config.Arguments = argv;
[NativeScript start:config];
Is there an easy way to access the underlying runtime's context and call functions on modules from the bundle explicitly throughout the Swift code? Just like you can add Swift code throughout the JS code in the bundle...
So, I'm really looking for a bidirectional solution where javascript uses Swift code and Swift code uses JS.
Other solutions are also welcome!

V8 console.log does not print

I am attempting to embed v8 into my application, I am messing about seeing what is included in the V8 environment (duktape does not include a console implementation) and it seems like v8 does include an implementation however when I call console.log it does not print anything, instead it just prints undefined (which i assume to be the return value of console.log) So how would one go about linking the default std::cout output with console.log.
This is my code currently, I am working with the default hello world code very slightly modified.
int main(int argc, char* argv[]) {
// Initialize V8.
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
// Create a new Isolate and make it the current one.
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
// Create a stack-allocated handle scope.
v8::HandleScope handle_scope(isolate);
// Create a new context.
v8::Local<v8::Context> context = v8::Context::New(isolate);
// Enter the context for compiling and running the hello world script.
v8::Context::Scope context_scope(context);
{
// Create a string containing the JavaScript source code.
v8::Local<v8::String> source =
v8::String::NewFromUtf8(isolate, R"(
console.log("does not print?")
)",
v8::NewStringType::kNormal)
.ToLocalChecked();
// Compile the source code.
v8::Local<v8::Script> script =
v8::Script::Compile(context, source).ToLocalChecked();
// Run the script to get the result.
v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();
// Convert the result to an UTF8 string and print it.
v8::String::Utf8Value utf8(isolate, result);
printf("%s\n", *utf8);
}
}
// Dispose the isolate and tear down V8.
isolate->Dispose();
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
delete create_params.array_buffer_allocator;
std::cin.get();
return 0;
}
and I am using the prebuilt v8 binaries here
Try the following:
#include "src/debug/interface-types.h"
define your own "console delegate" class, deriving from debug::ConsoleDelegate
override any methods you're interested in, e.g. void Log(const debug::ConsoleCallArguments& args, const v8::debug::ConsoleContext&) override;
instantiate it and call debug::SetConsoleDelegate(isolate, &your_console_delegate); after creating your Isolate
To see an example, start at https://cs.chromium.org/chromium/src/v8/src/d8/d8-console.h?l=14&gsn=D8Console and trace where it's used.
So for anyone in the future that is dealing with this, this is the process I used to fix it.
download the source from here, only the src folder is needed.
extract it and link it in to your project where ever you put vendor code in addition to the bundle.
put it in the a src folder because otherwise its includes don't work
you will need to make a bunch of include directories for it to compile, mine include v8/src, and v8
make sure to link it with the nuget package, you may not have to do this, one machine needed it the other didn't.
you do not need to generate builtins-generated/bytecodes-builtins-list.h

WebAssembly calling JavaScript methods from wasm i.e. within C++ code

I was playing with WebAssembly and so far and I was able to manage emscripten compile my test C++ project to wasm file
em++ provides me 2 files i.e.
mainTest.js mainTest.wasm
When I load mainTest.js in my html page then I get a JavaScript object called
"Module".
I did found how to call C++/wasm methods from javascript i.e. something like:
var myTestInteger = Module._callMyTestMethod();
and read strings from the
Module.wasmMemory.buffer
, but I do NOT understand how to call JavaScript from C++ code.
i.e. I would like to be able to do something like that:
#ifdef __cplusplus
extern "C" {
#endif
extern void testExternJSMethod();
int main()
{
cout << " Hello From my Test1 !" << endl;
testExternJSMethod();
return 0;
}
int EMSCRIPTEN_KEEPALIVE callMyTestMethod(){
return 26;
}
#ifdef __cplusplus
}
#endif
and the my js method testExternMethod that I am loading in another js file called utils.js
function testExternMethod() {
console.log("Hello from testExternMethod!" + )
}
Here I would like to call the JavaScript testExternJSMethod from C++.
When I run the page in Firefox get "-1" in the debugger console.
So what I am missing in this case? Unfortunately The Mozilla documentation is giving only examples in those S-expressions instead of C++.
What am I missing in example? In C++ I have defined the method with the extern keyword i.e.
extern void testExternJSMethod();
but I get the feeling that that is not all I have to do.
I believe that I should somehow link that JavaScript method to the Module somehow but I do not know how.
Module.asm gives me the exports. Which method call should give me the imports? since I believe that this _testExternJSMethod() should be in some imports method I can not figure out how to get to it.
I'm not exactly sure of your use case, but you are missing important steps to be able to use your function testExternalJSMethod. You have two options here:
Option 1 - Library
1 - Define your function in c/c++ .
extern void testExternalJSMethod();
2 - Create a file called myLibrary.js
3 - The JS function needs to be added to the LibraryManager in your library file with the following code:
function makeAlert(text) {
alert(text);
}
if (typeof mergeInto !== 'undefined') mergeInto(LibraryManager.library, {
testExternalJSMethod: function() {
makeAlert("Hello world");
}
});
4 - If testExternalJSMethod depends on anything outside of its own scope (for example, makeAlert above), make sure to include the script in your html page
<script async type="text/javascript" src="myLibrary.js"></script>
5 - Add option --js-library to your emcc command, and immediately after the relative path to myLibrary.js
emcc ... --js-library myLibrary.js
Option 2 - Passing Pointers
1 - Define your javascript function type in c/c++
typedef void testExternalJSMethod()
2 - Wherever you want this function to be used, accept an int param which will be the function pointer, and cast the pointer to your function
void passFnPointer(int ptr) {
((testExternalJSMethod*)ptr)();
}
3 - Use emscripten's addFunction() and store its returned value (the pointer in c/c++)
var fnPtr = Module.addFunction(function () {
alert("You called testExternalJSMethod");
});
4 - Use the stored pointer value from step 3 to pass to our function passFnPointer
var passFnPointer = Module.cwrap('passFnPointer', 'undefined', ['number']);
passFnPointer(fnPtr);
5 - Add option -s RESERVED_FUNCTION_POINTERS to your emcc command
emcc ... -s RESERVED_FUNCTION_POINTERS=10
Have you tried looking at the Emscripten documentation? It has a whole section on interacting with code that details how to expose C / C++ functions to JavaScript and call JavaScript functions from C / C++.
Emscripten provides two main approaches for calling JavaScript from C/C++: running the script using emscripten_run_script() or writing “inline JavaScript”.
It is worth noting that the Mozilla documentation details plain WebAssembly, whereas Emscripten adds a lot more framework and tooling around WebAssembly in order to make it easier to port large C / C++ codebases.

How to access a basic JSIL library (from C#) in a web page?

I'm trying to get started with JSIL. I've followed the directions as far as I understand. I have a very basic C# dummy project with the code:
namespace TestLib
{
public class MagicType
{
public int Multiply(int x, int y)
{ // test instance method
return x * y;
}
public static int Add(int x, int y)
{// test static int method
return x + y;
}
}
}
I've compiled this with jsilc, and created a website that hosts this along with the jsil scripts. My html initializes this:
<script type="text/javascript">
var jsilConfig = {
libraryRoot: '/js/jsil/',
scriptRoot: '/js/testlib/',
manifestRoot: '/js/testlib/',
manifests: ['TestLib.dll'] // gets as far as Loading '/js/testlib/TestLib.dll.manifest.js'...
// which is 200 / OK
};
var asm = null;
var runMain = function () { // doesn't get invoked
console.log('> main');
asm = JSIL.GetAssembly("TestLib", true); // (executed outside method) returns a stub with no content
console.log('< main');
};
</script>
<script src="/js/jsil/jsil.js"></script>
but... I can't access the library. The console output indicates that it is loading:
Loading '/js/testlib/TestLib.dll.manifest.js'...
which is a 200 OK. However, I can't access it. If I run:
var asm = JSIL.GetAssembly("TestLib", true);
then I get back a stub to the library, but it doesn't have anything in it. There's a asm.$typesByName, but that is an empty object. What I want to do (to see it work) is to call the Multiply and Add methods.
So: what am I missing? my intent is to host a transpiled library that I can access through js, which is as I understand it: possible. I just can't make it work. I have uploaded my entire test project here: https://github.com/mgravell/jsilfun
The missing piece is that JSIL's browser layer performs loading in two stages: There's the bootstrapper, which you've loaded successfully and has loaded your manifests, and then the browser scaffold, which sets up the DOM interfaces and actually executes the assemblies you've loaded. This occurs in two stages since so many things are asynchronous and you might have a reason to load additional assemblies on your own or do other work before actually running all the code.
The browser scaffold exposes a function called onLoad (yeah, sorry) that you can call to perform the second stage. The examples all do this with <body onload="onLoad()"> or something similar, but you can call it any way you want.

Integration clojurescript into a javascript framework

I would like to use Clojurescript to write a component within a Javascript framework but I can't work out how to create the constructor and call global variables within the object.
The framework creates views (within their own .js file) by reading their state from a saved jason file and reifying them in javascript (the views are code like so):
(function() {
var Title = function(json) {
view.View.call(this, json); // view is defined in another js file - global namespace
this.title = json.title;
this.el.addClass("title");
}
view.inherit(view.View, Title);
view.Title = Title;
view.types.Title = Title;
Title.prototype.json = function() {
return $.extend(view.View.prototype.json.call(this), {
type: 'Title',
title: this.title
});
}
Title.prototype.reflow = function() {
this.h2.quickfit(opts);
}
})();
I have seen how you create a Javascript object using deftype macro and Object:
(deftype Foo [a b c]
Object
(bar x (+ a b c x)))
I'm new to both javascript and clojurescript. I see that the anonymous function wrapping everything provides a scope for the view but not sure how to (or if I need to) so something equivalent in clojurescript.
So my questions are:
how do I create the constructor function for Title in this model??
And do how should I handle the calls to the view variable, such as view.inherit etc?
Thanks
This is a bit general of an answer, but you seem to be wanting to share code between both ClojureScript and JavaScript, so here is a primer and a few select tidbits on Using JavaScript Libraries in Clojure:
Exporting
Protecting symbols you declare from renaming is easy; just define :export metadata on any ClojureScript var, and the ClojureScript compiler will ensure that it is not munged.
For example, a function can be declared like this:
(ns example)
(defn ^:export hello [name]
(js/alert (str "Hello," name "!")))
It is then available, via the same name, in an external JavaScript context:
<script>
example.hello("Eustace")
</script>
Externs
In order to go the other way, and reference a variable declared externally from within your code, you must provide the Google Closure compiler with an "extern file", a .js file defining javascript variables which will not be munged. This file is passed to the Google Closure compiler, and when compiling, it will not munge any names defined in it.
For example, I can try compiling the following ClojureScript (utilizing the Raphael.js library) in advanced mode without an extern file.
(defn test []
(let [raphael (js/Raphael. 10 50 320 200)]
(. raphael (circle 50 50 50))))
When I call the test function, it will throw a javascript error that new Raphael(10, 50, 320, 200)).K is not a function. K is the munged name of the circle function from Raphael, and obviously can't be found, since it isn't defined anywhere. We need to tell the compiler to preserve the name circle, not munge it.
I can do this by creating the following externs.js file:
var Raphael = {};
Raphael.circle = function() {};
And use it when compiling my ClojureScript:
(closure/build "src/cljs" {:optimizations :advanced
:externs ["externs.js"]
:output-to "helloworld.js"})

Categories

Resources