Just added JSR-223 script engine for AWK. This is based on Jawk implementation. If you are interested, you can checkout the sources from https://scripting.dev.java.net! Jawk has nice Java support - like calling Java constructors, methods etc.
It is easy to have more than on JDK or JRE version(in particular JRE -- because of browser plugin downloads!) in the same machine. If you compile your Java source(s) with a JDK version and run the same with with an earlier JDK/JRE version, you could get an error that looks like this:
Exception in thread "main" java.lang.UnsupportedClassVersionError: Bad version number in .class file
Now, how do I know what is the version of a .class file and what version of JDK I need to run the same? The section 4.1 of VM spec. edition 2 explains the "header" structure of every .class file:
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
The magic of a .class file is 0xCAFEBABE. The following class takes a class file as input and prints the major, minor version of it and also prints minium JDK/JRE required to run the given class.
File: Version.java
import java.io.*;
public class Version {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Usage: java version <.class file>");
System.exit(1);
}
if (! new File(args[0]).exists()) {
System.err.println(args[0] + " does not exist!");
System.exit(2);
}
DataInputStream dis = new DataInputStream(
new FileInputStream(args[0]));
int magic = dis.readInt();
if (magic != 0xcafebabe) {
System.err.println(args[0] + " is not a .class file");
System.exit(3);
}
int minor = dis.readShort();
int major = dis.readShort();
System.out.println("class file version is " + major + "." + minor);
String version = null;
if (major < 48) {
version = "1.3.1";
} else if (major == 48) {
version = "1.4.2";
} else if (major == 49) {
version = "1.5";
} else if (major == 50) {
version = "6";
} else {
version = "7";
}
System.out.println("You need to use JDK " + version + " or above");
}
}
For example, after compiling the above class with JDK 1.5 and running it against itself prints the following:
java -cp . Version Version.class
class file version is 49.0
You need to use JDK 1.5 or above
John O'Conner has written a nice article on scripting for the Java platform. This is a well written article with sample code. One minor thing: there was a change in javax.script.Invocable interface after the article was written/reviewed. You may have to change the downloaded code -- if you are working with Mustang build 91 or above.
I was in India Engineering Center, Bangalore for the last 3 days to attend the Global Technology Leadership Conference. Sun's Distinguished Engineers visited from the US. There were few Sun customers. And there were local tech. leads from Bangalore. It is good to know about various product offerings from Sun and to hear from the customers. I visited Banglore after quite some time (yes, I work from home @ Chennai). It is always a great pleasure to meet friends!
Solaris sustaining friends helped by installing Solaris Nevada on my Dell laptop - the trick was to install without disturbing the other OS in the laptop. Thanks to Venky T.V and friends! (sorry for not being exhaustive)
Met AppServer friends - Sahoo, Binod and Java sustaining friends (Kannan, Thilak...). We discussed many things - this is one thing that you miss with work-from-home - these informal face-to-face meetings/chat are great.
Thomas Künneth has started a java.net project for AppleScript JSR-223 script engine - this project is hosted at https://jasconn.dev.java.net/
Recent updates to scripting.dev.java.net:
ScriptEngine.eval() call and call the same in subsequent ScriptEngine.eval() calls. This is because internally, each eval'ed code is compiled as a separate ScriptXXX class. i.e., all global functions in the eval'ed code go into a class derived from Script class. To give an illusion of single engine level scope for "global" functions, we create a map of method closures from all ScriptXXX instances created and search there for global functions (using Groovy's MetaClass mechanism). With this change, the engine behaves as if there is a single global functions scope per ScriptEngine instance. Groovy's Meta Object Protocol (MOP) is really powerful indeed.
Chuk-Munn Lee has written NetBeans scripting modules. These modules let you customize NetBeans using scripting. We've just checked this module into the scripting.dev.java.net project.
As you'd know, in the scripting.dev.java.net project JSR-223 script engines for various languages are developed and maintained.
We have recently added binary downloads (jsr223-engines.tar.gz and jsr223-engines.zip) at scripting.dev.java.net. You can download, gunzip/untar or unzip the binary. Note that this just contains JSR-223 engine implementations for various languages. You still need to download underlying opensource implementation (For example of Groovy, JRuby etc.) from the respective language sit. The URL for each language site is mentioned in the README.TXT file for each language.
Please note that the binaries at scripting.dev.java.net are built with the latest build of Mustang (build 91) and so won't work with earlier versions of JDK - (This is because class file version changes with Mustang)
As you may know already, Mustang (Java SE 6) includes JSR-223: Scripting for the Java Platform API. There was a recent change (since Mustang build 91) in javax.script.Invocable interface. Previously, there were two invoke methods in this interface - one for calling script top-level or global functions from Java and another for calling methods on script object. These have been renamed to invokeFunction and invokeMethod respectively.
javax.script.Invocable interface used to have the following methods:
// call script global function
invoke(String name, Object...args)
// call method on a script object
invoke(Object this, String name, Object...args)
This results in a problem when invoking a function with String arguments. For example, the following call
Object ret = engine.invoke("functionName", "arg1", "arg2", "arg3");
is ambiguous. javac cannot tell which invoke overload is being called. User gets an error that looks like:
reference to invoke is ambiguous, both method invoke(java.lang.Object,
java.lang.String,java.lang.Object...) in javax.script.Invocable and method
invoke(java.lang.String,java.lang.Object...) in javax.script.Invocable match
Now, these methods have been renamed to
// call a script global function
invokeFunction(String name, Object...args)
// call a method on a script object
invokeMethod(Object this, String name, Object...args)
The JavaScript engine (co-bundled in Sun's Java SE 6 implementation) and the script engines maintained at http://scripting.dev.java.net have been fixed to track this interface change. If you are maintaining/implementing your own JSR-223 engine, please fix it for this change.
In addition to this, scripting.dev.java.net project has been updated to useIf you are using http://phobos.dev.java.net, you may want to refer to Roberto Chinnici's blog entry titled Tracking JSR-223 Changes
I was making a jar file for testing something. I had given a manifest file and a set of classes by the following command line:
jar cvfm myjar.jar mymanifest.mf *.class
When I used myjar.jar, the manifest entries I specified in my mymanifest.mf were not working as expected. I opened the jar and checked the added manifest.mf in the jar file -- it did not have attributes defined in mymanifest.mf in it! In fact, my manifest.mf had just one attribute!! It turned out that I was bitten by whitespace -- newline was missing at the end of the attribute. Apparently, manifest file spec. mandates newline at the end of attributes. jar tool did not complain about my manifest file
Moral of the story: beware of whitespaces!
This is continuation of my earlier post on the same topic.
This is just to remind that objects have a prototype object. JavaScript is prototype-delegation based language. If a property or method is not found in an object, it's prototype is searched. So, when you access obj.x, the property "x" may actually be in the prototype of object "obj" (or prototype of prototype and so on). An object's prototype is accessible by a special property of the name __proto__. Note that this is non-standard Rhino specific extension. Rhino allows you to change prototype by assigning to __proto__ property!
One more similarity to Lisp! In Lisp, scopes are known as "environments". Scopes are nothing but a map of variable names to variable values. In JavaScript, scopes are objects whose property names are variable names. For example, this keyword in global/toplevel scope refers to the global object. You can walk all global variables by this function:
function printGlobals() {
for (var i in this) {
println(i + ' = ' + this[i]);
}
}
Scopes are organized in a parent-child chain. The local scope of a function has the global scope as it's parent. Local variable scope of a nested function has enclosing function's local scope as it's parent and so on. Scope objects are also referred as "variable objects" (i.e., object that keeps variables). The "next" link in this chain is accessible by the special property called __parent__. Note that this is non-standard Rhino specific extension. Rhino allows you to change parent by assigning to __parent__ property! In addition to functions and local functions, with statements also add an innermost scope to scope-chains.
How can we get hold of the "local scope" object of a function? Note that a nested function's parent scope is the local scope of the enclosing function. So, we can introduce an anonymous local function (with no code) to get it's __parent__.
function f() {
var x = 10;
// introduce an anonymous nested function just to get
// it's parent scope -- which is nothing but scope of
// this function.
var localScope = (function() {}).__parent__;
println(x); // prints 10
println(localScope.x); // prints 10 again!
}
With this knowledge we can write generic code to print all local variables of
a function:
var printLocals = "{ var _locals = (function(){}).__parent__; " +
" for (var _i in _locals) { " +
" if (/_locals/(_i) || /_i/(_i)) continue; " +
" println(_i + '=' + _locals[_i].toSource()); " +
" } " +
"}";
function h() {
var x = 32;
var y = { x : 3 };
// prints all locals (x, y in this function)
eval(printLocals);
}
Note that I've used eval - I cannot define printLocals as a function -- because to access local scope object of given function (like h() above), I've to define an anonymous function inside the same function! So, I'm eval'ing a string to get hold of the same. But, printLocals is generic -- in the sense that you can eval it any of your function to print all locals of it. Note that although JavaScript is similar to Lisp, macros are not supported. I had to use eval to simulate macro expansion.
If you combine the fact that scopes are objects and objects have prototypes, we have the following lookup rule for variables:
JavaScript exception handling involves try..catch statement. But, exceptions typically are JavaScript Error object or subtypes of it (but other types like string can also be thrown!). If you are getting a Java exception, that is also wrapped as a JavaScript Error object. Sometimes, you may want to get the exact Java exception thrown -- may be the Java method you called throws multiple exceptions and you may want to handle those differently. Rhino adds javaException property to Error objects -- this property is initialized if an Error object wraps around a Java exception.
try {
var obj = new java.lang.Object();
obj.wait();
} catch (e) {
var exp = e.javaException;
if (exp instanceof java.lang.IllegalMonitorStateException) {
exp.printStackTrace();
} else if (exp instanceof java.lang.InterruptedException) {
// do something differently..
} else {
// something else!
}
}
In Java debugging, we often use Throwable.printStackTrace()
or Thread.dumpStack() to print stack trace. How about similar stack trace for JavaScript? It turns out that Rhino does support stack trace - with script file name, function name and line number!. Rhino adds rhinoException property to Error object. The printStackTrace method on this rhinoException include script functions, line numbers etc.
File: test.js
function f() {
g();
}
function g() {
try {
var c = undefined;
c.toString();
} catch (e) {
e.rhinoException.printStackTrace();
}
}
f();
The above code prints something like:
D:\>jrunscript test.ps
sun.org.mozilla.javascript.internal.EcmaError: TypeError: Cannot call method "toString" of undefined (p.ps#19)
at sun.org.mozilla.javascript.internal.ScriptRuntime.constructError(ScriptRuntime.java:3234)
at sun.org.mozilla.javascript.internal.ScriptRuntime.constructError(ScriptRuntime.java:3224)
at sun.org.mozilla.javascript.internal.ScriptRuntime.typeError(ScriptRuntime.java:3240)
at sun.org.mozilla.javascript.internal.ScriptRuntime.typeError2(ScriptRuntime.java:3259)
at sun.org.mozilla.javascript.internal.ScriptRuntime.undefCallError(ScriptRuntime.java:3278)
at sun.org.mozilla.javascript.internal.ScriptRuntime.getPropFunctionAndThis(ScriptRuntime.java:1968)
at sun.org.mozilla.javascript.internal.Interpreter.interpretLoop(Interpreter.java:2931)
at script.g(test.ps:19)
at script.f(test.ps:2)
at script(test.ps:27)
at sun.org.mozilla.javascript.internal.Interpreter.interpret(Interpreter.java:2250)
at sun.org.mozilla.javascript.internal.InterpretedFunction.call(InterpretedFunction.java:149)
at sun.org.mozilla.javascript.internal.ContextFactory.doTopCall(ContextFactory.java:337)
at sun.org.mozilla.javascript.internal.ScriptRuntime.doTopCall(ScriptRuntime.java:2757)
at sun.org.mozilla.javascript.internal.InterpretedFunction.exec(InterpretedFunction.java:160)
at sun.org.mozilla.javascript.internal.Context.evaluateReader(Context.java:1163)
at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:106)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:230)
at com.sun.tools.script.shell.Main.evaluateReader(Main.java:314)
at com.sun.tools.script.shell.Main.evaluateStream(Main.java:350)
at com.sun.tools.script.shell.Main.processSource(Main.java:267)
at com.sun.tools.script.shell.Main.access$100(Main.java:19)
at com.sun.tools.script.shell.Main$2.run(Main.java:182)
at com.sun.tools.script.shell.Main.main(Main.java:30)
Note that the red lines above include script function, file name and line number. With this you can write a generic printStackTrace function as
function printStackTrace(exp) {
if (exp == undefined) {
try {
exp.toString();
} catch (e) {
exp = e;
}
}
// note that user could have caught some other
// "exception"- may be even a string or number -
// and passed the same as argument. Also, check for
// rhinoException property before using it
if (exp instanceof Error &&
exp.rhinoException != undefined) {
exp.rhinoException.printStackTrace();
}
}
You can call above function whereever you want script stack trace - much like java.lang.Thread.dumpStack() call. You can get fancier output by getting the stack trace into a string and then filtering it to print only the lines that begin with at script -- so that you can view only the script frames in the trace. That is left an exercise to the reader
[hint: use printStackTrace that accepts java.io.PrintWriter]
Mustang (Java SE 6) is co-bundled with Mozilla Rhino based JavaScript engine as an example implementation for javax.script API.
There are 3 use cases within JDK
There is already an experimental web application environment that uses JavaScript - http://phobos.dev.java.net.
So, we may expect that JavaScript will be employed within the JDK context more often. How about debugging support for JavaScript engine in JDK context? For Firefox, there are atleast two debuggers (that I know of!):
Rhino comes with it's own debugger -- but in Mustang, Rhino's tools are not included. Also, javax.script API does not include debugger API (yet?). For Rhino in Mustang, we have to resort to other ways to debug scripts including good old println/printf style. Few debugging tips and tricks are here...
You can use load function to load your script files (or URLs). In the interactive mode, you can call specific script functions or evaluate various expressions.
Use good old printf/System.out.println/alert/echo style debugging! In Mustang Java, there are built-in print and println functions always defined. If you have scripts that use alert, you can consider defining something like this:
// define alert to be same as println function
var alert = println;
Note that 'print' and 'println' functions print the message to output writer configured in the script context (recall that "context" is a pre-defined variable initialized with the current ScriptContext object). If you configure different output writer (may be, the one that writes messages to a file or socket etc.), you can make all print/println messages to go there.
// viewing function sources in jrunscript prompt.
// built-in functions are shown as "native code"
// But, you can get arity (number of arguments)
// of the function
js> eval.toSource()js> eval.toSource()
function eval() { [native code for eval, arity=1] }
// for script functions toSource() shows full source
js> function add(x, y) { return x + y }
js> add.toSource()
function add(x, y) {return x + y;}
// for Java methods we get signature as part of "source"
js> var v = new java.util.Date()
js> v.getDay.toSource()
function getDay() {/*
int getDay()
*/}
// for overloaded Java methods, toSource() shows signatures
// for all overloads
js> var out = java.lang.System.out
js> out.println.toSource()
function println() {/*
void println(boolean)
void println(char)
void println(int)
void println()
void println(long)
void println(java.lang.Object)
void println(java.lang.String)
void println(char[])
void println(double)
void println(float)
*/}
for(var i in obj) { print(obj[i]); }
like loop.
I frequently print all fields of an object (or elements of array) this way. Note that you will get object's methods (which are function valued properties) as well. To filter these, you can use typeof operator. You can also filter properties using property name pattern/index.
JavaScript functions can be passed as arguments and returned as values. Functions can be called by apply or call functions. Functions are first-class values that can be stored in variables. You can use these to implement JavaScript AOP. You can use "interceptors" to print debug trace output. For eg. function entry/exit can be printed (along with arguments, if desired). For example, the following is a variation of JavaScript AOP referred in above link..
var Aspects = new Object();
// calls "before" interceptor function before calling function
// name specified by fname. The function is expected to be a property
// of object 'obj'
Aspects.addBefore = function(obj, fname, before) {
var oldFunc = obj[fname];
var newFunc = function() {
return oldFunc.apply(this, before(arguments, oldFunc, this));
};
// store oldFunc for restore purpose...
newFunc.oldFunc = oldFunc;
obj[fname] = newFunc;
};
Aspects.restore = function(obj, fname) {
obj[fname] = obj[fname].oldFunc;
}
// tracing function entry
function traceEntry(args, oldFunc, thiz) {
// print the name of the function
print("entering " + oldFunc.name);
// print arguments
var str = "";
for (var i = 0; i < args.length; i++) {
str += args[i] + ", ";
}
print("arguments " + str);
// return argument array - used by oldFunc.
return args;
}
With the above code in a debug library (say "debug.js"), the client code can write something like:
function add(x, y) { return x + y; }
// just add two arguments
add(3, 4);
Aspect.addBefore(this, "add", traceEntry);
// call add -- prints debug output on entry
add(3, 4);
// restore old "add" function without debug output
Aspect.restore(this, "add");
Use objects instead. It is easy to accidentally create globals in JavaScript. For example,
function f() {
x = m(); // forgot 'var' before x, x is global!
// rest of the code...
}
While it may be possible to spot "unwanted" globals by code inspection, we can
do better than that. It is possible to check global variable assignments and accesses
using javax.script.Bindings used for global variable storage.
import javax.script.*;
import java.io.*;
public class Test {
public static void main(String[] args) throws Exception {
// create a new ScriptEngineManager
ScriptEngineManager m = new ScriptEngineManager();
// get JavaScript engine instance
ScriptEngine jsEngine = m.getEngineByName("javascript");
// set a "debug" bindings for global variables
jsEngine.setBindings(new DebugBindings(), ScriptContext.ENGINE_SCOPE);
// eval code from a java.io.Reader object.
jsEngine.eval(new FileReader(args[0]));
}
}
The DebugBindings class could look like
// a simple Bindings implementation that prints debug output
// whenever a variable is accessed or assigned.
import javax.script.*;
public class DebugBindings extends SimpleBindings {
@Override public Object put(String name, Object value) {
Object res = super.put(name, value);
System.out.println("Global assign: " + name);
return res;
}
@Override public Object get(Object key) {
Object res = super.get(key);
System.out.println("Global access: " + key);
return res;
}
}
When running the Test class with the following t.js
script file,
function h() {
// global variable assign
x = 32;
}
h();
we get the following output..
Global assign: context Global assign: print Global assign: println Global access: javax.script.filename Global assign: h Global access: h Global assign: xNote that debug output is printed for function "assignments" as well. Note that global functions are global variables with "function" value.
Because JavaScript is dynamically typed language, we can replace an object with any other object that "looks" like the "original". i.e., the replacement objects should just support same methods, properties -- but can do anything. (If it walks like a duck and quacks like a duck, it must be a duck). You can wrap actual object with a "debuggable" object (whose methods print debug/trace output) -- so long as debug wrapper objects supports same methods, properties. Usually, it is very easy to wrap an object with another object in JavaScript. But, if your object supports properties (a.k.a fields) it becomes tricky to wrap the same - for example, you may want to wrap XMLHttpRequest that has properties. But, you can use JSAdapter and "hook" all property or method access or assignments.
Seeing both Brazil and Argentina exit (That too Argentina in a penalty shoot-out!), FIFA is over for me. I turned to the good old (the?) other sport -- 1-0 is way better than 0-0 (square turner in West Indies!!) - but could have been 2-0 or even 3-0!