I need to parse JavaScript from java.
I have already tried the NashornScriptEngine (builtin in the jdk), and RhinoScriptEngine, but they both (apparently) do not support the "class" keyword.
This is what i tried:
public static void main(String[] args) throws Throwable {
RhinoScriptEngineFactory factory = new RhinoScriptEngineFactory();
RhinoScriptEngine engine = (RhinoScriptEngine) factory.getScriptEngine();
engine.eval(new FileReader("js/SomeClass.js"));
}
And this is the content of SomeClass.js:
class SomeTestClass {
}
function start() {
print("hello")
}
And this is the stacktrace:
Exception in thread "main" javax.script.ScriptException: identifier is a reserved word: class in eval at line number 0 at column number 6
at org.mozilla.javascript.engine.RhinoScriptEngine.eval(RhinoScriptEngine.java:124)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:249)
at com.clut.MyMain.main(MyMain.java:21)
So what can i do to parse javascript containing classes?
I can even use another library if its needed
I found out that GraalVm has builtin javascript parsing
Related
Setting variables directly in Java classes doesn't seem to be working. Why not? What is the proper syntax? Where does the variable go??
The following prints out 2 and 1. Thus the f.x=2; never happened according to the object f of Foo.
#Test
public void testJS2Java() throws IOException, ScriptException, Exception {
ScriptEngineManager factory = new ScriptEngineManager();// create JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
class Foo {
int x = 1;
}
Foo f = new Foo();
engine.put("f", f);
System.out.println(engine.eval("f.x=2;"));
System.out.println(f.x);
}
The f.x=2; executes without error but which x was set?
Three issues with your test:
Nashorn allows access only to public members of public classes (from
exported modules for jdk9+) only. The local class Foo is
not public. So its members are not accessible from JavaScript.
Nashorn allows access to static members only from "Java type
objects" and not from instances of Java types. (different from Java).
Nashorn would ignore property sets on Java object if no public
field or public bean property with appropriate setter is found.
A working sample demonstrating access to a static Java field from Nashorn:
import javax.script.*;
public class Main {
public static int x = 10;
public static void main(String[] args) throws Exception {
ScriptEngine e = new ScriptEngineManager().
getEngineByName("JavaScript");
// access type object for Java class "Main" using Java.type
e.eval("var M = Java.type('Main');");
// access public static field 'x' of Main class
e.eval("print(M.x)");
// assign to public static field 'x' of Main class
e.eval("M.x += 10;");
// change is seen from Java
System.out.println(Main.x);
}
}
I would like to use Incremental DOM library in my GWT app.
https://google.github.io/incremental-dom/#about
As I am coming from the Java world, I struggle with concepts of JavaScript namespaces and modules. I was able to use Closure Compiler with closure version of Incremental DOM (has to be build from sources).
It starts with the following line:
goog.module('incrementaldom');
So if I was to use it in regular JS I would type:
var patch = goog.require('incrementaldom').patch;
And then the patch function would be available in the scope of my code. But how to make it accessible from #JsInterop annotated classes?
I tried something like:
public class IncrementalDom {
#JsMethod(namespace = "incrementaldom", name = "patch")
public static native void patch(Element element, Patcher patcher);
#JsFunction
#FunctionalInterface
public interface Patcher {
void apply();
}
}
But it doesn't work. I get this error in the runtime:
(TypeError) : Cannot read property 'patch' of undefined
So I guess I have to somehow expose the incrementaldom module or at least only the patch method. But I don't know how.
After fighting for the whole day I found the solution. In the goog.module: an ES6 module like alternative to goog.provide document I found the missing information about the role of goog.scope function - required modules are visible only within the scoped call.
I created another Closure JS file named incrementaldom.js:
goog.provide('app.incrementaldom'); // assures creation of namespace
goog.require("incrementaldom");
goog.scope(function() {
var module = goog.module.get("incrementaldom");
var ns = app.incrementaldom;
app.incrementaldom.patch = module.patch;
});
goog.exportSymbol("app.incrementaldom", app.incrementaldom);
And now I can call it from Java code like this:
public class IncrementalDom {
#JsMethod(namespace = "app.incrementaldom", name = "patch")
public static native void patch(Element element, Patcher patcher);
#JsFunction
#FunctionalInterface
public interface Patcher {
void apply();
}
}
Still I have to define every object exported in original module separately in the Closure JS file. Fortunately I only need patch method. I hope one day I will find less cumbersome way for #JsInterop with goog.module :(
I am working on a project using GWT and d3. i have used d3 in javascript files. Let me explain a bit more . i have a class name AstForm in GWT. in this class i have a function which i have called in my javascript file using following code.it the code works fine for me.
public native void PrepareFunctionsForJS() /*-{
$wnd.ExtractOFFNetWork = this.#org.valcri.asstsrchui.client.AstForm::ExtractOFFNetWork(*);
}-*/;
public void ExtractOFFNetWork(JsArrayMixed args)
{
Window.alert("thisCurrent row and Column is " +
args.getString(0) + " " + args.getString(1)+"OffenderNetwork?");
}
void testfunction ()
{
Window.alert("testfunction)
}
in java script i have used the following code
window.ExtractOFFNetWork(["GWT","JS"]);
my code works fine. i can call the ExtractOFFNetWork in javascript file. however the problem is in the ExtractOFFNetWork function when i call testfunction which is also the member function of the ASTFORM class the programe error saying testfunction is not a function. however when i changed testfunction as static than i can access this function within ExtractOFFNetWork. alternatievly i can also use the testfunction inside ExtractOFFNetWork by creating a separate object of ASTForm as
AstForm my =new AstForm();
my.testfunction();
however i do not want to use either static or separate ASTform object to access member function of ASTForm. i also used this.testfunction() within ExtractOFFNetWork but it also does not work. i would appreciate if any body can help to solve my problem i have spend full day without any success :)
already tried this when calling your PrepareFunctionsForJS(btw by java naming conventions method names start with lowercase letter..)
//assuming that you are calling the prepare function from inside the AstForm
public class AstForm() {
//...
PrepareFunctionsForJS(this);
}
public native void PrepareFunctionsForJS(AstForm instance) /*-{
$wnd.ExtractOFFNetWork = instance.#org.valcri.asstsrchui.client.AstForm::ExtractOFFNetWork(*);
}-*/;
I want to make a class of mine accessible in JavaScript via a C# WebView-Control.
Therefore I am using the WebView.AddWebAllowedObject method. However if I assign an attribute, it works fine, but if I assign the whole class to get all attributes in js, all of the attributes(and methods btw) are "undefined". I tried everything I found in the www. See the attached code:
//The class I want to make accessible
[AllowForWeb, ComVisible(true)]
[MarshalingBehavior(MarshalingType.Agile)]
public class DeviceInformation
{
public string IPAdress { get; private set; }
public DeviceInformation()
{
IPAdress = GetIPAdress();
}
public string GetDeviceUUID()
{
EasClientDeviceInformation deviceinfo = new EasClientDeviceInformation();
return deviceinfo.Id.ToString();
}
public string GetIPAdress()
{
List<string> ipAddresses = new List<string>();
var hostnames = NetworkInformation.GetHostNames();
foreach (var hn in hostnames)
{
if (hn?.IPInformation != null && (hn.IPInformation.NetworkAdapter.IanaInterfaceType == 71 ||
hn.IPInformation.NetworkAdapter.IanaInterfaceType == 6))
{
string ipadress = hn.DisplayName;
return ipadress;
}
}
return string.Empty;
}
}
Here the objects are initialized.
DeviceInformation devinf = new DeviceInformation();
private void View_NavigationStarting(WebView sender, WebViewNavigationStartingEventArgs args)
{
if (args.Uri.Host == "")
{
//win_ipadress has an ipadress as value
view.AddWebAllowedObject("win_ipadress", devinf.IPAdress);
//deviceInformation is initialized as well but I have no access to its attributes
view.AddWebAllowedObject("deviceInformation", devinf);
}
}
That's the way i call it in js:
else if ($.os.ie) {
myIpAdr = window.win_ipadress;
//Throws an exception because GetIPAdress() is "undefined"
myIpAdr = window.deviceInformation.GetIPAdress();
}
I am using this in a Windows Universal App. The Javascript and in the WebView displayed HTML-Code is already in use for Android an iOS.
I believe you need to define the method name starting with a lower case character.
For example: change GetIPAddress to getIPAddress.
I tested it on my side and found if I use the upper case name 'GetIPAddress', it won't work. But if I use getIPAddress, it works.
And after I read kangax's explanation in this thread, I think it makes sense.
[Update]
Since it still doesn't work after you make the change on method name, I think the issue should be related to how you expose the windows runtime object. I guess you simply defined the DeviceInformation class and tried to use it in the same project.
First, we need to create a separate windows universal windows runtime component project.
The c# class DeviceInformation should be put into this project. Keep the same code.
Then, in your universal app project, add reference to the windows runtime component and keep rest code to consume the windows runtime object.
[Update 2]
Just noticed an interesting behavior in VS. No matter if the Method name we defined in C# is starting with uppercase or lowercase, the visual studio intellisense shows the lowercase, so the method name will be automatically converted when we try to use it in js.
Given the following, running with Oracle JRE 6 gives the output boo, but OpenJDK 6 gives an exception
javax.script.ScriptException: sun.org.mozilla.javascript.EvaluatorException: The choice of Java
constructor replace matching JavaScript argument types (function,string) is ambiguous; candidate
constructors are:
class java.lang.String replace(char,char)
class java.lang.String replace(java.lang.CharSequence,java.lang.CharSequence) (<Unknown source>#1)
in <Unknown source> at line number 1
That's presumably because with OpenJDK (presumably the rt.jar supplied with it) the function's getting a java.lang.String, but with Oracle's it's getting a JavaScript String (or something that can be implicitly coerced to one).
So which is more correct? The Javascript (in this case) is the API, so can we write the Java such that the API's the same for either implementation? (If the OpenJDK implementation is "more correct" (and so likely to be what everyone does in the future), then I guess changing the API (documentation, examples, tests) throwing in new String(...) as appropriate wouldn't be impossible, but I'd rather not uglify the API unless I'm more confident.)
import javax.script.*;
class st {
public static void main(String[] args) {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
Bindings bindings = jsEngine.getBindings(ScriptContext.ENGINE_SCOPE);
Foo foo = new Foo();
bindings.put("v", foo);
try {
jsEngine.eval("v.run(function(a) {println(a.replace(/f/,\"b\"));})");
} catch (ScriptException ex) {
ex.printStackTrace();
}
}
}
and
public class Foo {
public void run(FooCB cb) {
cb.run("foo");
}
public static interface FooCB {
public void run(Object val);
}
}
The Java SE 6 spec (JSR 270) merely says:
There will be no requirement that any
particular scripting language be
supported by the platform;
implementors may choose to include
support for the scripting language(s)
of their choice as they see fit.
To the best of my knowledge, there is no formal spec for how to integrate Java types into JavaScript. It's unfortunate, but there's no reason to expect 100% compatibility across implementations.
I believe both the Oracle JRE and OpenJDK ship with Rhino, but there's no guarantee about version level, patches, etc.