some nashorn testing/ jrunscript patch

Andreas Rieber rieberandreas at gmail.com
Thu Jan 10 11:45:21 PST 2013


Hi Jim,

please find attached the jrunscript patch(es). The small Nashorn patch 
took me a while to find out... The jdk8 patch is hg export -g like 
expected on openjdk.java.net contributing page. I also updated the jtreg 
test.

The patch for jdk8 has a new file for the formatting. Tricky part for 
printf/sprintf was to do the type conversion of the javascript types. 
The init.js file will now work with Nashorn and Rhino.

nashorn> printf("%5d %f %s %B %c %<d %n", 1, 2, "3", true, "a");
     1 2.000000 3 TRUE a 97
nashorn> printf("java.util.Date(): %1$tm %1$te, %1$tY%n", new 
java.util.Date());
java.util.Date(): 01 10, 2013
nashorn> printf("javascript Date(): %1$tm %1$te, %1$tY%n", new Date());
javascript Date(): 01 10, 2013
nashorn> printf("%c %c %c %d %d%n", 'a', "b", 65, -1, "Hello");
a b A -1 72
nashorn>

The jdk8 patch works also for jdk7u as that's where i started.
I will see if i get scriptpad sample also working.

- Andreas


On 08.01.13 19:12, Jim Laskey (Oracle) wrote:
> On 2013-01-08, at 1:22 PM, Andreas Rieber <rieberandreas at gmail.com> wrote:
>
>> Hi,
>>
>> i tested Nashorn with a project i am currently working on (scripting
>> java). The first thing i spotted is that Rhino comes with print() and
>> println() as default functions in:
>>
>> jdk8/jdk/src/share/classes/com/sun/script/javascript/RhinoScriptEngine.java
>>
>> while Nashorn has only a print():
>>
>> jdk8/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
>> jdk8/nashorn/src/jdk/nashorn/api/scripting/resources/engine.js
>>
>> Will this be changed or is that a migration issue?
> We opted for print because we were primarily using V8 performance tests and v8 only supports print.  Standalone rhino does not support println either.  We did have a switch for turning on println, but it fell out of favour to be consistent with other VMs.
>
> You can duplicate the behaviour with;
>
> function print(s) { java.lang.System.out.print("" + s); }
> function println(s) { java.lang.System.out.println("" + s); }
>
>
>> importClass works only with: load("nashorn:mozilla_compat.js");
>> What is with importPackage (also used in jrunscript)?
>>
> importClass is being phased out since it duplicates the builtin behaviour of Packages and JavaClass.
>
> importPackage(java.awt);
> importClass(java.awt.Frame);
>
> is just
>
> var awt = java.awt;
> var Frame = java.awt.Frame;
>
>
>> Next problem i had was related to java array creation. Here the sample
>> from JSE 7 Documentation "Java Scripting Programmer's Guide":
>>
>> // create Java String array of 5 elements
>> var a = java.lang.reflect.Array.newInstance(java.lang.String, 5);
>>
>> // Accessing elements and length access is by usual Java syntax
>> a[0] = "scripting is great!";
>> print(a.length);
>>
>> It works with Rhino but fails with Nashorn:
>>
>> Exception in thread "main" java.lang.RuntimeException: java.lang.NoSuchMethodException: None of the fixed arity signatures [(java.lang.Class, int[]), (java.lang.Class, int)] or the variable arity signatures [(java.lang.Class, int...)] of the method java.lang.reflect.Array.newInstance match the argument types [org.dynalang.dynalink.beans.StaticClass, java.lang.Integer]
>>
>> The default number type for Rhino in vararg functions is double while
>> Nashorn has also integer (nice), might be a migration issue.
>
> We are pushing the Java syntax here to eliminate confusion between the java class and the JavaClass javascript object.  This eliminates the confusion when accessing members of the (String) class when a javascript object and members of the class itself when a java object.
>
> jjs> java.lang.String
> [JavaClass java.lang.String]
> jjs> java.lang.String.class
> class java.lang.String
>
> so
>
> var a = java.lang.reflect.Array.newInstance(java.lang.String.class, 5);
>
>> Is jrunscript and the scriptpad sample application
>> (jdk8/jdk/src/share/sample/scripting/scriptpad) now also developed
>> under Nashorn project? I would like to contribute a patch for
>> jrunscript (printf never worked and i added also a sprintf), the
>> scriptpad needed only a few fixes to work with Rhino. I signed the OCA
>> recently.
> Contributions welcome.
>
> I just see Attila answered differently, but both forms work.
>
>
>> best regards
>> Andreas
>>
-------------- next part --------------
diff --git a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
--- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
+++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
@@ -485,12 +485,6 @@
         try {
             final Object val = ctxt.getAttribute(ScriptEngine.FILENAME);
             final String fileName = (val != null) ? val.toString() : "<eval>";
-
-            // !!HACK!! do not evaluate "init.js" from jrunscript tool!!
-            if ("<system-init>".equals(fileName)) {
-                return null;
-            }
-
             final Source source = new Source(fileName, buf);
             if (globalChanged) {
                 setNashornGlobal(global);
-------------- next part --------------
# HG changeset patch
# User arieber
# Date 1357845438 -3600
# Node ID 99350545e3f5ecb0fe11fb9cce86f36664a375e9
# Parent  32a57e645e012a1f0665c075969ca598e0dbb948
fixed jrunscript for Nashorn+Rhino, added sprintf and got printf working.

diff --git a/src/share/classes/com/sun/tools/script/shell/Formatter.java b/src/share/classes/com/sun/tools/script/shell/Formatter.java
new file mode 100644
--- /dev/null
+++ b/src/share/classes/com/sun/tools/script/shell/Formatter.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.script.shell;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Formatter is a class to get the type conversion between javascript types and
+ * java types for the format (sprintf) method working.
+ *
+ * <p>In javascript the type for numbers can be different from the format type
+ * specifier. For format type '%d', '%o', '%x', '%X' double need to be
+ * converted to integer. For format type 'e', 'E', 'f', 'g', 'G', 'a', 'A'
+ * integer needs to be converted to double.
+ *
+ * <p>Format type "%c" and javascript string needs special handling.
+ *
+ * <p>The javascript date objects can be handled if they are type double (the
+ * related javascript code will convert with Date.getTime() to double). So
+ * double date types are converted to long.
+ *
+ * <p>Pattern and the logic for parameter position: java.util.Formatter
+ *
+ * @author Andreas Rieber
+ * @since 1.8
+ */
+public class Formatter {
+
+    /**
+     * Method which converts javascript types to java types for the
+     * String.format method (jrunscript function sprintf).
+     *
+     * @param format a format string
+     * @param args arguments referenced by the format specifiers in format
+     * @return a formatted string
+     */
+    public static String format(String format, Object[] args) {
+        Matcher m = fsPattern.matcher(format);
+        int positionalParameter = 1;
+
+        while (m.find()) {
+            int index = index(m.group(1));
+            boolean previous = isPreviousArgument(m.group(2));
+            char conversion = m.group(6).charAt(0);
+
+            // skip over some formats
+            if (index < 0 || previous
+                    || conversion == 'n' || conversion == '%') {
+                continue;
+            }
+
+            // index 0 here means take a positional parameter
+            if (index == 0) {
+                index = positionalParameter++;
+            }
+
+            // out of index, String.format will handle
+            if (index > args.length) {
+                continue;
+            }
+
+            // current argument
+            Object arg = args[index - 1];
+
+            // for date we convert double to long
+            if (m.group(5) != null) {
+                // convert double to long
+                if (arg instanceof Double) {
+                    args[index - 1] = ((Double) arg).longValue();
+                }
+            } else {
+                // we have to convert some types
+                switch (conversion) {
+                    case 'd':
+                    case 'o':
+                    case 'x':
+                    case 'X':
+                        if (arg instanceof Double) {
+                            // convert double to long
+                            args[index - 1] = ((Double) arg).longValue();
+                        } else if (arg instanceof String
+                                && ((String) arg).length() > 0) {
+                            // convert string (first character) to int
+                            args[index - 1] = (int) ((String) arg).charAt(0);
+                        }
+                        break;
+                    case 'e':
+                    case 'E':
+                    case 'f':
+                    case 'g':
+                    case 'G':
+                    case 'a':
+                    case 'A':
+                        if (arg instanceof Integer) {
+                            // convert integer to double
+                            args[index - 1] = ((Integer) arg).doubleValue();
+                        }
+                        break;
+                    case 'c':
+                        if (arg instanceof Double) {
+                            // convert double to integer
+                            args[index - 1] = ((Double) arg).intValue();
+                        } else if (arg instanceof String
+                                && ((String) arg).length() > 0) {
+                            // get the first character from string
+                            args[index - 1] = (int) ((String) arg).charAt(0);
+                        }
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+
+        return String.format(format, args);
+    }
+
+    /**
+     * Method to parse the integer of the argument index.
+     *
+     * @param s
+     * @return -1 if parsing failed, 0 if string is null, > 0 integer
+     */
+    private static int index(String s) {
+        int index = -1;
+
+        if (s != null) {
+            try {
+                index = Integer.parseInt(s.substring(0, s.length() - 1));
+            } catch (NumberFormatException e) { }
+        } else {
+            index = 0;
+        }
+
+        return index;
+    }
+
+    /**
+     * Method to check if a string contains '&lt;'. This is used to find out if
+     * previous parameter is used.
+     *
+     * @param s
+     * @return true if '&lt;' is in the string, else false
+     */
+    private static boolean isPreviousArgument(String s) {
+        return (s != null && s.indexOf('<') >= 0) ? true : false;
+    }
+
+    // %[argument_index$][flags][width][.precision][t]conversion
+    private static final String formatSpecifier =
+            "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
+    // compiled format string
+    private static Pattern fsPattern;
+
+    static {
+        fsPattern = Pattern.compile(formatSpecifier);
+    }
+}
diff --git a/src/share/classes/com/sun/tools/script/shell/init.js b/src/share/classes/com/sun/tools/script/shell/init.js
--- a/src/share/classes/com/sun/tools/script/shell/init.js
+++ b/src/share/classes/com/sun/tools/script/shell/init.js
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -47,7 +47,7 @@
 			__get__ : function(name) {
 				return function() {
 					return obj.invoke(name, arguments);
-				}
+				};
 			}
 		});
 }
@@ -200,8 +200,18 @@
 var inp = java.lang.System["in"];
 
 // useful imports for often used io, net classes
-importPackage(java.io);
-importPackage(java.net);
+var BufferedInputStream = java.io.BufferedInputStream;
+var BufferedOutputStream = java.io.BufferedOutputStream;
+var BufferedReader = java.io.BufferedReader;
+var DataInputStream = java.io.DataInputStream;
+var File = java.io.File;
+var FileInputStream = java.io.FileInputStream;
+var FileOutputStream = java.io.FileOutputStream;
+var InputStream = java.io.InputStream;
+var InputStreamReader = java.io.InputStreamReader;
+var OutputStream = java.io.OutputStream;
+var Reader = java.io.Reader;
+var URL = java.net.URL;
 
 /**
  * Generic any object to input stream mapper
@@ -302,18 +312,20 @@
  *
  * @param str input from which script is loaded and evaluated
  */
-function load(str) {
-	var stream = inStream(str);
-	var bstream = new BufferedInputStream(stream);
-	var reader = new BufferedReader(new InputStreamReader(bstream));
-	var oldFilename = engine.get(engine.FILENAME);
-	engine.put(engine.FILENAME, str);	
-	try {
-		engine.eval(reader);
-	} finally {
-		engine.put(engine.FILENAME, oldFilename);
+if (load === undefined) {
+    var load = function load(str) {
+	    var stream = inStream(str);
+	    var bstream = new BufferedInputStream(stream);
+	    var reader = new BufferedReader(new InputStreamReader(bstream));
+	    var oldFilename = engine.get(engine.FILENAME);
+	    engine.put(engine.FILENAME, str);	
+	    try {
+		    engine.eval(reader);
+	    } finally {
+		    engine.put(engine.FILENAME, oldFilename);
 	        streamClose(stream);
-	}
+	    }
+    };
 }
 
 // file system utilities
@@ -458,7 +470,7 @@
  * @param dir name of the new directory
  */
 function mkdir(dir) {
-	var dir = pathToFile(dir);
+	dir = pathToFile(dir);
 	println(dir.mkdir()? "created" : "can not create dir");
 }
 
@@ -469,7 +481,7 @@
  * @param dir input path name
  */
 function mkdirs(dir) {
-	var dir = pathToFile(dir);
+	dir = pathToFile(dir);
 	println(dir.mkdirs()? "created" : "can not create dirs");
 }
 	
@@ -479,7 +491,7 @@
  * @param pathname name of the file 
  */
 function rm(pathname) {
-    	file = pathToFile(pathname);
+    var file = pathToFile(pathname);
 	if (!file.exists()) {
 		println("file not found: " + pathname);
 		return false;
@@ -586,7 +598,7 @@
 		for (var i in files) {
 			var f = files[i];
 			if (filter) {			
-				if(!f.getName().match(filter)) {
+				if (!f.getName().match(filter)) {
 					continue;
 				}
 			}
@@ -776,7 +788,7 @@
 	}
 
 	var factory = javax.xml.transform.TransformerFactory.newInstance();
-	var tranformer;
+	var transformer;
 	if (style) {		
 		transformer = factory.newTransformer(XMLSource(style));	
 	} else {
@@ -840,18 +852,42 @@
 }
 
 /**
- * This is C-like printf 
+ * This is C-like printf
  *
  * @param format string to format the rest of the print items
  * @param args variadic argument list
  */
-function printf(format, args/*, more args*/) {	
-	var array = java.lang.reflect.Array.newInstance(java.lang.Object, 
-			arguments.length - 1);
-	for (var i = 0; i < array.length; i++) {
-		array[i] = arguments[i+1];
+function printf(format, args/*, more args*/) {
+	print(sprintf.apply(this, arguments));
+}
+
+/**
+ * This is C-like sprintf
+ *
+ * @param format string to format the rest of the print items
+ * @param args variadic argument list
+ */
+function sprintf(format, args/*, more args*/) {
+	var len = arguments.length - 1;
+    var array = [];
+
+	if (len < 0) {
+		return "";
 	}
-	return java.lang.System.out.printf(format, array);
+
+	for (var i = 0; i < len; i++) {
+		if (arguments[i+1] instanceof Date) {
+			array[i] = arguments[i+1].getTime();
+		} else {
+			array[i] = arguments[i+1];
+		}
+	}
+
+    if( typeof Java !== "undefined") {
+        array = Java.toJavaArray(array);
+    }
+
+	return Packages.com.sun.tools.script.shell.Formatter.format(format, array);
 }
 
 /**
@@ -882,3 +918,23 @@
 		return reader.readLine();
 	}
 }
+
+// Nashorn has only print, so we define it here
+if (println === undefined) {
+    var print = function print(str, newline) {
+        if (typeof(str) == 'undefined') {
+            str = 'undefined';
+        } else if (str == null) {
+            str = 'null';
+        }
+        if (!(out instanceof java.io.PrintWriter))
+            out = new java.io.PrintWriter(out);
+        out.print(String(str));
+        if (newline) out.print('\n');
+        out.flush();
+    };
+
+    var println = function println(str) {
+        print(str, true);
+    };
+}
diff --git a/test/sun/tools/jrunscript/jrunscriptTest.sh b/test/sun/tools/jrunscript/jrunscriptTest.sh
--- a/test/sun/tools/jrunscript/jrunscriptTest.sh
+++ b/test/sun/tools/jrunscript/jrunscriptTest.sh
@@ -46,6 +46,7 @@
 v = v + " is the value";
 if (v != 0) { println('yes v != 0'); }
 java.lang.System.out.println('hello world from script');
+printf("%3d\n",1);
 new java.lang.Runnable() { run: function() { println('I am runnable'); }}.run();
 EOF
 
@@ -64,6 +65,7 @@
 v = v + " is the value";
 if (v != 0) { println('yes v != 0'); }
 java.lang.System.out.println('hello world from script');
+printf("%3d\n",1);
 new java.lang.Runnable() { run: function() { println('I am runnable'); }}.run();
 EOF
 
diff --git a/test/sun/tools/jrunscript/repl.out b/test/sun/tools/jrunscript/repl.out
--- a/test/sun/tools/jrunscript/repl.out
+++ b/test/sun/tools/jrunscript/repl.out
@@ -3,5 +3,6 @@
 js> 35 is the value
 js> yes v != 0
 js> hello world from script
+js>   1
 js> I am runnable
 js> 
\ No newline at end of file


More information about the nashorn-dev mailing list