Access fields of a JavaScript object in Java - javascript

I am writing a Scala application (that is supposed to run on Hadoop using Spark) and my users are to execute JavaScript snippets that they upload and I want to provide access to certain helper functions written in Scala (like "make an HTTP call" etc.) to these JavaScript users. So what I do is writing a big JavaScriptHelpers object and then give access to that object using
engine = scriptEngineManager.getEngineByName("JavaScript")
engine.put("jql", JavaScriptHelpers)
so users can say jql.httpPost(...) from within JavaScript. The Scala code that makes this possible looks as follows:
def httpPost(where: String, params: Object): Try[String] = {
params match {
// JavaScript string becomes Java's String:
case body: String =>
// ...
// JavaScript object becomes Java's ScriptableObject
case obj: ScriptableObject =>
val params = convertToMap(obj)
// ...
}
}
protected def convertToMap(obj: ScriptableObject): Map[String, String] = {
(for (key <- obj.getIds().toList) yield {
(key.toString, obj.get(key) match {
case s: String =>
s
case d: java.lang.Double if d.toString.endsWith(".0") =>
d.toInt.toString
case other => other.toString
})
}).toMap
}
The only way I found to access information stored in JavaScript objects is to look at them as an instance of sun.org.mozilla.javascript.ScriptableObject. Now this works like a charm on my local OpenJDK installation
java version "1.7.0_75"
OpenJDK Runtime Environment (fedora-2.5.4.2.fc20-x86_64 u75-b13)
OpenJDK 64-Bit Server VM (build 24.75-b04, mixed mode)
but when I run the same code on my Hadoop cluster, which is running
java version "1.7.0_67"
Java(TM) SE Runtime Environment (build 1.7.0_67-b01)
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)
then I get:
java.lang.NoClassDefFoundError: sun/org/mozilla/javascript/ScriptableObject
sun.org.mozilla.javascript.internal.JavaMembers.discoverAccessibleMethods(JavaMembers.java:383)
sun.org.mozilla.javascript.internal.JavaMembers.discoverAccessibleMethods(JavaMembers.java:335)
sun.org.mozilla.javascript.internal.JavaMembers.reflect(JavaMembers.java:455)
sun.org.mozilla.javascript.internal.JavaMembers.<init>(JavaMembers.java:76)
sun.org.mozilla.javascript.internal.JavaMembers.lookupClass(JavaMembers.java:847)
sun.org.mozilla.javascript.internal.NativeJavaObject.initMembers(NativeJavaObject.java:88)
sun.org.mozilla.javascript.internal.NativeJavaObject.<init>(NativeJavaObject.java:78)
sun.org.mozilla.javascript.internal.NativeJavaObject.<init>(NativeJavaObject.java:68)
...
and looking at the version of Rhino that Oracle bundles with the JDK 7 as downloadable from http://www.oracle.com/technetwork/opensource/jdk7-source-1634015.html it seems like all sun.org.mozilla.javascript.* classes have been moved to sun.org.mozilla.javascript.internal.*.
Now how do I deal with that situation? Is there any Rhino-independent way of accessing the fields of a JavaScript object in Java? Or, how can I force the Oracle JVM to use the ...javascript.internal.ScriptableObject while using ...javascript.ScriptableObject in my local environment?
Any help much appreciated.

You can use function overloading instead.
// `params` matches a JavaScript string
def httpPost(where: String, params: String): Try[String] = {
// ...
}
// `params` matches a JavaScript object
def httpPost(where: String, params: java.util.Map[_, _]): Try[String] = {
// ...
}
This solution worked on my environment (Oracle JDK 8 and OpenJDK 1.7).

Related

How to call pre-existing WebAssembly code from Emscripten

I've made a compiler from my programming language (called AEC) targeting WebAssembly. However, that compiler only produces WebAssembly Binary Toolkit (WABT) compatible code, as I couldn't find sufficient documentation for the assembly compatible with Emscripten. So, how can I call the functions in that language from Emscripten-compatible C or C++?
Previously, I made a compiler for that language targeting x86, and it was rather easy to use C++ to interact with code written in that language. The same, however, doesn't seem to be true when using WebAssembly.
Let's say I have this code in AEC:
Function plusOne(Integer32 integer) Which Returns Integer32 Does
Return integer + 1;
EndFunction
I compile it as follows:
[teo.samarzija#teos-acer-laptop debug]$ ../AECforWebAssembly/aec *.aec
Running the tests...
All the tests passed.
Reading the file...
All characters read!
Tokenizing the program...
Finished tokenizing the program!
Parsing the program...
Finished parsing the program!
Compiling the program...
Compilation finished!
Saving the assembly in file "plusOne.wat"...
Assembly successfully saved, quitting now.
[teo.samarzija#teos-acer-laptop debug]$ wat2wasm plusOne.wat
Now, let's say I want to call it from a C code such as this one:
#include <stdio.h>
extern int plusOne(int);
int main() {
printf("plusOne(4)=%d\n",plusOne(4));
return 0;
}
How do I compile the C program to be able to do that? If I try it in an analogous way I could do it with my AEC compiler targeting x86 assembly, I get this error:
[teo.samarzija#teos-acer-laptop debug]$ emcc -o test.html test.c plusOne.wasm
emcc: error: plusOne.wasm: Input file has an unknown suffix, don't know what to do with it!
So, what should I do?
The following NodeJS code accomplishes what I want:
const FileSystem = require('fs');
const wasmFileContent = FileSystem.readFileSync("plusOne.wasm");
let stack_pointer = new WebAssembly.Global({value : 'i32', mutable : true}, 0);
let memory = new WebAssembly.Memory({initial : 1, maximum : 1});
let importObject = {
JavaScript : {stack_pointer : stack_pointer, memory : memory}
};
WebAssembly.instantiate(wasmFileContent, importObject).then((results) => {
const exports = results.instance.exports;
let plusOne = exports.plusOne;
console.log("plusOne(4)=", plusOne(4));
});
So, how can I do that in C or C++ using Emscripten?

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

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();
}

Supply JavaScript date to Nashorn script

I am working on an API in Java that allows users to write scripts and access a specific set of methods that are passed in (in the form of an API object) by the Nashorn script engine.
I want to, in the JavaScript, call a function getDate(), which will return some arbitrary date (as a native JavaScript date) that's provided from the Java side.
I have tried simply putting an org.java.util.Date on the API object, but that won't behave like a JS date. The goal is to make this as simple as possible for end-users who are experienced with JS.
Java Example:
public class MyAPI {
public void log(String text){
System.out.println(text);
}
public Date getDate(){
// Return something that converts to a native-JS date
}
public static void main(){
// MyClassFilter implements Nashorn's ClassFilter
ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(new MyClassFilter());
((Invokable) engine).invokeFunction("entryPoint", new MyAPI());
}
JavaScript example
function entryPoint(myApi){
var date = myApi.getDate();
myApi.log(date.getMinutes());
}
jdk.nashorn.internal.* packages are nashorn internal implementation classes. There is no guarantee that these won't be changed or be removed between JDK versions. Besides with a security manager around, accessing these packages from java code directly would result in SecurityException being thrown! With jdk9 modular jdk, these packages are not exported packages from nashorn module and so javac won't even compile your code in jdk9!
I'd recommend using JS wrapper (solution 1) in the answer by user "ug_". If you do have to call from Java, you can use API exported from jdk.nashorn.api.scripting package.
If "engine" is your javax.script.ScriptEngine of nashorn, then you can do something like the following:
import jdk.nashorn.api.scripting.*;
..
public Object getDate() {
// get JS Date constructor object - you can get once and store
// as well/
JSObject dateConstructor = (JSObject) engine.eval("Date");
// now do "new" on it
return dateConstructor.newObject();
}
With that, your JS script can call "getDate()" on your API object and get a JS
Date object. Note that you can also pass constructor arguments to newObject
method call (it is a Java variadic method).
The Nashorn engine has objects it uses internally which represent the Javascript objects. As you have guessed the java.util.Date != new Date() (in javascript). The engine uses a class called jdk.nashorn.internal.objects.NativeDate to represent its JS date.
If I were building this out I would not have the NativeDate constructed in the Java but instead have a wrapper in Javascript for the MyApi object which would contain a few other native JS methods, such as getDate().
var MYAPI_JAVASCRIPT = {
log: function() {
print(arguments);
},
getDate: function() {
return new Date();
}
}
You could then pass that object as the method parameter.
However if your really set on using the NativeDate in your Java code then you can construct one like so:
public NativeDate getDate() {
return (NativeDate) NativeDate.construct(true, null);
}

Categories

Resources