# HG changeset patch # User Lubomir Nerad # Date 1355480137 -3600 # Node ID 15cbc8cb2163b8fea5e7cb82fa6870b71f2de6e2 # Parent ec7d8bc177256ec04b4f955ba7572146d611dfdd# Parent 8da329789435be467c110affcbe01165ea1a9c33 Merge with trunk diff -r ec7d8bc17725 -r 15cbc8cb2163 benchmarks/matrix-multiplication/jsTestDriver.conf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/benchmarks/matrix-multiplication/jsTestDriver.conf Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,5 @@ +server: http://localhost:9876 + +load: + - target/classes/org/apidesign/benchmark/matrixmul/*.js + diff -r ec7d8bc17725 -r 15cbc8cb2163 benchmarks/matrix-multiplication/nbactions.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/benchmarks/matrix-multiplication/nbactions.xml Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,29 @@ + + + + + run + + process-classes + org.codehaus.mojo:exec-maven-plugin:1.2.1:exec + + + diff -r ec7d8bc17725 -r 15cbc8cb2163 benchmarks/matrix-multiplication/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/benchmarks/matrix-multiplication/pom.xml Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,111 @@ + + + 4.0.0 + + org.apidesign.bck2brwsr + matrix.multiplication + 0.3-SNAPSHOT + jar + + Matrix multiplication + + + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.7 + 1.7 + + + + + org.apidesign.bck2brwsr + mojo + 0.3-SNAPSHOT + + + + j2js + + + + + + + + + + + run-benchmarks + + bck2brwsr.runBenchmarks.browsers + + + + + + + com.googlecode.jstd-maven-plugin + jstd-maven-plugin + 1.3.2.5 + + 9876 + ${bck2brwsr.runBenchmarks.browsers} + 720 + all + jsTestDriver.conf + true + ${basedir}/target/testOutput + + + + + run-tests + + test + + + + + + + + + + + + + org.apidesign.bck2brwsr + emul + 0.3-SNAPSHOT + + + com.googlecode.jstd-maven-plugin + jstd-maven-plugin + 1.3.2.5 + test + + + + + + jstd-maven-plugin google code repo + http://jstd-maven-plugin.googlecode.com/svn/maven2 + + + + + jstd-maven-plugin google code repo + http://jstd-maven-plugin.googlecode.com/svn/maven2 + + + + diff -r ec7d8bc17725 -r 15cbc8cb2163 benchmarks/matrix-multiplication/src/main/java/org/apidesign/benchmark/matrixmul/Main.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/benchmarks/matrix-multiplication/src/main/java/org/apidesign/benchmark/matrixmul/Main.java Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,42 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.benchmark.matrixmul; + +public class Main { + + public static final int ITERATION_COUNT = 100000; + + public static void main(String[] args) { + Matrix m1 = new Matrix(5); + Matrix m2 = new Matrix(5); + + m1.generateData(); + m2.generateData(); + + //m1.printOn(System.out); + //System.out.println("x"); + //m2.printOn(System.out); + + for (int i = 0; i < ITERATION_COUNT; i++) { + m1.multiply(m2); + } + + //System.out.println("="); + //m1.printOn(System.out); + } +} diff -r ec7d8bc17725 -r 15cbc8cb2163 benchmarks/matrix-multiplication/src/main/java/org/apidesign/benchmark/matrixmul/Matrix.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/benchmarks/matrix-multiplication/src/main/java/org/apidesign/benchmark/matrixmul/Matrix.java Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,81 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.benchmark.matrixmul; + +//import java.io.PrintStream; +//import java.util.Random; + +public class Matrix { + + private final int rank; + private float data[][]; + + public Matrix(int r) { + rank = r; + data = new float[r][r]; + } + + public void setElement(int i, int j, float value) { + data[i][j] = value; + } + public float getElement(int i, int j) { + return data[i][j]; + } + + public void generateData() { + //final Random rand = new Random(); + //final int x = 10; + for (int i = 0; i < rank; i++) { + for (int j = 0; j < rank; j++) { + data[i][j] = i + j; + } + } + } + + public Matrix multiply(Matrix m) { + if (rank != m.rank) { + throw new IllegalArgumentException("Rank doesn't match"); + } + + final float res[][] = new float[rank][rank]; + for (int i = 0; i < rank; i++) { + for (int j = 0; j < rank; j++) { + float ij = 0; + for (int q = 0; q < rank; q++) { + ij += data[i][q] * m.data[q][j]; + } + res[i][j] = ij; + } + } + data = res; + + return this; + } + + /* + public void printOn(PrintStream s) { + for (int i = 0; i < rank; i++) { + for (int j = 0; j < rank; j++) { + s.printf("%f ", data[i][j]); + } + s.println(); + } + } + */ + +} diff -r ec7d8bc17725 -r 15cbc8cb2163 benchmarks/matrix-multiplication/src/main/resources/org/apidesign/benchmark/matrixmul/multiplication-test.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/benchmarks/matrix-multiplication/src/main/resources/org/apidesign/benchmark/matrixmul/multiplication-test.js Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,25 @@ +/* + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +MatrixTest = TestCase("MatrixTest"); + + +MatrixTest.prototype.testMultiplication = function() { + var vm = new bck2brwsr(); + var x = vm.loadClass("org.apidesign.benchmark.matrixmul.Main"); + x.main__V_3Ljava_lang_String_2(null); +}; diff -r ec7d8bc17725 -r 15cbc8cb2163 benchmarks/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/benchmarks/pom.xml Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,17 @@ + + + 4.0.0 + + bck2brwsr + org.apidesign + 0.3-SNAPSHOT + + org.apidesign.bck2brwsr + benchmarks + 0.3-SNAPSHOT + pom + Performance benchmarks + + matrix-multiplication + + diff -r ec7d8bc17725 -r 15cbc8cb2163 benchmarks/run-firefox.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/benchmarks/run-firefox.sh Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Back 2 Browser Bytecode Translator +# Copyright (C) 2012 Jaroslav Tulach +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 2 of the License. +# +# This program 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 for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. Look for COPYING file in the top folder. +# If not, see http://opensource.org/licenses/GPL-2.0. +# + +/usr/bin/firefox --display=`echo $DISPLAY` "$@" diff -r ec7d8bc17725 -r 15cbc8cb2163 core/src/main/java/org/apidesign/bck2brwsr/core/JavaScriptPrototype.java --- a/core/src/main/java/org/apidesign/bck2brwsr/core/JavaScriptPrototype.java Wed Dec 12 14:07:53 2012 +0100 +++ b/core/src/main/java/org/apidesign/bck2brwsr/core/JavaScriptPrototype.java Fri Dec 14 11:15:37 2012 +0100 @@ -29,10 +29,13 @@ @Target({ ElementType.TYPE }) public @interface JavaScriptPrototype { /** Expression that identifies the function where all methods - * should be added into. + * should be added into. If this attribute is unspecified + * all methods are added to the same object specified by + * {@link #prototype()}. + * * @return name of function to contain methods found in given class */ - String container(); + String container() default ""; /** Expression that defines the way to construct prototype for this * class. * @return expression to construct prototype diff -r ec7d8bc17725 -r 15cbc8cb2163 emul/src/main/java/java/lang/String.java --- a/emul/src/main/java/java/lang/String.java Wed Dec 12 14:07:53 2012 +0100 +++ b/emul/src/main/java/java/lang/String.java Fri Dec 14 11:15:37 2012 +0100 @@ -1426,7 +1426,7 @@ */ @JavaScriptBody(args = { "self", "find", "from" }, body= "find = find.toString();\n" + - "return self.toString().substring(from, find.length) === find;\n" + "return self.toString().substring(from, from + find.length) === find;\n" ) public boolean startsWith(String prefix, int toffset) { char ta[] = toCharArray(); diff -r ec7d8bc17725 -r 15cbc8cb2163 emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_String.js --- a/emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_String.js Wed Dec 12 14:07:53 2012 +0100 +++ b/emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_String.js Fri Dec 14 11:15:37 2012 +0100 @@ -1,5 +1,5 @@ // initialize methods on String constants -java_lang_String(false); +vm.java_lang_String(false); // we need initialized arrays Array.prototype.fillNulls = function() { diff -r ec7d8bc17725 -r 15cbc8cb2163 javap/src/main/java/org/apidesign/javap/ClassData.java --- a/javap/src/main/java/org/apidesign/javap/ClassData.java Wed Dec 12 14:07:53 2012 +0100 +++ b/javap/src/main/java/org/apidesign/javap/ClassData.java Fri Dec 14 11:15:37 2012 +0100 @@ -554,14 +554,14 @@ return in.toString(); } case CONSTANT_CLASS: - String jn = javaName(getClassName(cpx)); + String jn = getClassName(cpx); if (textual) { if (refs != null) { refs[0] = jn; } - return jn.replace('/', '_') + "(false).constructor.$class"; + return jn; } - return jn; + return javaName(jn); case CONSTANT_STRING: String sv = stringValue(((CPX)x).cpx, textual); if (textual) { diff -r ec7d8bc17725 -r 15cbc8cb2163 javap/src/main/java/org/apidesign/javap/Hashtable.java --- a/javap/src/main/java/org/apidesign/javap/Hashtable.java Wed Dec 12 14:07:53 2012 +0100 +++ b/javap/src/main/java/org/apidesign/javap/Hashtable.java Fri Dec 14 11:15:37 2012 +0100 @@ -4,6 +4,8 @@ */ package org.apidesign.javap; +import org.apidesign.bck2brwsr.core.JavaScriptBody; + /** A JavaScript optimized replacement for Hashtable. * * @author Jaroslav Tulach @@ -23,6 +25,9 @@ Hashtable() { } + @JavaScriptBody(args = { "self", "key", "val" }, body = + "self[key] = val;" + ) synchronized void put(Object key, Object val) { int[] where = { -1, -1 }; Object found = get(key, where); @@ -56,6 +61,9 @@ } } + @JavaScriptBody(args = {"self", "key" }, body = + "return self[key];" + ) Object get(Object key) { return get(key, null); } diff -r ec7d8bc17725 -r 15cbc8cb2163 javap/src/main/java/org/apidesign/javap/Vector.java --- a/javap/src/main/java/org/apidesign/javap/Vector.java Wed Dec 12 14:07:53 2012 +0100 +++ b/javap/src/main/java/org/apidesign/javap/Vector.java Fri Dec 14 11:15:37 2012 +0100 @@ -4,10 +4,14 @@ */ package org.apidesign.javap; +import org.apidesign.bck2brwsr.core.JavaScriptBody; +import org.apidesign.bck2brwsr.core.JavaScriptPrototype; + /** A JavaScript ready replacement for java.util.Vector * * @author Jaroslav Tulach */ +@JavaScriptPrototype(prototype = "new Array" ) final class Vector { private Object[] arr; @@ -15,22 +19,31 @@ } Vector(int i) { - this(); } void add(Object objectType) { addElement(objectType); } + @JavaScriptBody(args = { "self", "obj" }, body = + "self.push(obj);" + ) void addElement(Object obj) { final int s = size(); setSize(s + 1); setElementAt(obj, s); } + @JavaScriptBody(args = { "self" }, body = + "return self.length;" + ) int size() { return arr == null ? 0 : arr.length; } + @JavaScriptBody(args = { "self", "newArr" }, body = + "for (var i = 0; i < self.length; i++) {\n" + + " newArr[i] = self[i];\n" + + "}\n") void copyInto(Object[] newArr) { if (arr == null) { return; @@ -41,16 +54,22 @@ } } + @JavaScriptBody(args = { "self", "index" }, body = + "return self[index];" + ) Object elementAt(int index) { return arr[index]; } - void setSize(int len) { + private void setSize(int len) { Object[] newArr = new Object[len]; copyInto(newArr); arr = newArr; } + @JavaScriptBody(args = { "self", "val", "index" }, body = + "self[index] = val;" + ) void setElementAt(Object val, int index) { arr[index] = val; } diff -r ec7d8bc17725 -r 15cbc8cb2163 javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Wed Dec 12 14:07:53 2012 +0100 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Fri Dec 14 11:15:37 2012 +0100 @@ -19,12 +19,12 @@ import java.io.IOException; import java.io.InputStream; -import java.lang.reflect.Method; import java.util.Set; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; +import org.apidesign.vm4brwsr.Bck2Brwsr; import org.testng.annotations.Test; import static org.testng.Assert.*; @@ -125,15 +125,7 @@ if (sb == null) { sb = new StringBuilder(); } - try { - Method m; - Class genJS = Class.forName("org.apidesign.vm4brwsr.GenJS"); - m = genJS.getDeclaredMethod("compile", Appendable.class, String[].class); - m.setAccessible(true); - m.invoke(null, sb, names); - } catch (Exception exception) { - throw new IOException(exception); - } + Bck2Brwsr.generate(sb, ProcessPageTest.class.getClassLoader(), names); ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine js = sem.getEngineByExtension("js"); try { diff -r ec7d8bc17725 -r 15cbc8cb2163 javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java --- a/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java Wed Dec 12 14:07:53 2012 +0100 +++ b/javaquery/demo-calculator/src/main/java/org/apidesign/bck2brwsr/mavenhtml/App.java Fri Dec 14 11:15:37 2012 +0100 @@ -70,6 +70,10 @@ private static void setValue(double v) { StringBuilder sb = new StringBuilder(); sb.append(v); + if (sb.toString().endsWith(".0")) { + final int l = sb.length(); + sb.delete(l - 2, l); + } Calculator.DISPLAY.setValue(sb.toString()); } diff -r ec7d8bc17725 -r 15cbc8cb2163 mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Bck2BrswrMojo.java --- a/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Bck2BrswrMojo.java Wed Dec 12 14:07:53 2012 +0100 +++ b/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Bck2BrswrMojo.java Fri Dec 14 11:15:37 2012 +0100 @@ -21,7 +21,7 @@ import java.io.File; import java.io.FileWriter; -import java.lang.reflect.Method; +import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -34,6 +34,7 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; +import org.apidesign.vm4brwsr.Bck2Brwsr; /** Compiles classes into JavaScript. */ @Mojo(name="j2js", defaultPhase=LifecyclePhase.PROCESS_CLASSES) @@ -72,14 +73,10 @@ try { URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts()); - - Class c = Class.forName("org.apidesign.vm4brwsr.GenJS"); - Method m = c.getDeclaredMethod("compile", ClassLoader.class, Appendable.class, String[].class); - m.setAccessible(true); FileWriter w = new FileWriter(javascript); - m.invoke(null, url, w, arr.toArray(new String[0])); + Bck2Brwsr.generate(w, url, arr.toArray(new String[0])); w.close(); - } catch (Exception ex) { + } catch (IOException ex) { throw new MojoExecutionException("Can't compile", ex); } } diff -r ec7d8bc17725 -r 15cbc8cb2163 pom.xml --- a/pom.xml Wed Dec 12 14:07:53 2012 +0100 +++ b/pom.xml Fri Dec 14 11:15:37 2012 +0100 @@ -13,6 +13,7 @@ mojo javaquery javap + benchmarks diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Bck2Brwsr.java Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,106 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; + +/** Build your own virtual machine! Use methods in this class to generate + * a skeleton JVM in JavaScript that contains pre-compiled classes of your + * choice. The generated script defines one JavaScript method that can + * be used to bootstrap and load the virtual machine:
+ * var vm = bck2brwsr();
+ * var main = vm.loadClass('org.your.pkg.Main');
+ * main.main__V_3Ljava_lang_String_2(null);
+ * 
+ * In case one wants to initialize the virtual machine with ability to + * load classes lazily when needed, one can provide a loader function to + * when creating the virtual machine:
+ * var vm = bck2brwsr(function(resource) { 
+ *   return null; // byte[] for the resource
+ * });
+ * 
+ * In this scenario, when a request for a unknown class is made, the loader + * function is asked for its byte code and the system dynamically transforms + * it to JavaScript. + * + * @author Jaroslav Tulach + */ +public final class Bck2Brwsr { + private Bck2Brwsr() { + } + + /** Generates virtual machine from bytes served by a resources + * provider. + * + * @param out the output to write the generated JavaScript to + * @param resources provider of class files to use + * @param classes additional classes to include in the generated script + * @throws IOException I/O exception can be thrown when something goes wrong + */ + public static void generate(Appendable out, Resources resources, String... classes) throws IOException { + StringArray arr = StringArray.asList(classes); + arr.add(VM.class.getName().replace('.', '/')); + VM.compile(resources, out, arr); + } + + /** Generates virtual machine from bytes served by a class loader. + * + * @param out the output to write the generated JavaScript to + * @param loader class loader to load needed classes from + * @param classes additional classes to include in the generated script + * @throws IOException I/O exception can be thrown when something goes wrong + */ + public static void generate(Appendable out, final ClassLoader loader, String... classes) throws IOException { + class R implements Resources { + @Override + public InputStream get(String name) throws IOException { + Enumeration en = loader.getResources(name); + URL u = null; + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + return u.openStream(); + } + } + generate(out, new R(), classes); + } + + /** Provider of resources (classes and other files). The + * {@link #generate(java.lang.Appendable, org.apidesign.vm4brwsr.Bck2Brwsr.Resources, java.lang.String[]) + * generator method} will call back here for all classes needed during + * translation to JavaScript. + */ + public interface Resources { + /** Loads given resource (class or other file like image). The + * resource name to load bytes for the {@link String} class + * would be "java/lang/String.class". + * + * @param resource path to resource to load + * @return the input stream for the resource + * @throws IOException can be thrown if the loading fails on some error + * or the file cannot be found + */ + public InputStream get(String resource) throws IOException; + } +} diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Wed Dec 12 14:07:53 2012 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Fri Dec 14 11:15:37 2012 +0100 @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.InputStream; +import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.javap.AnnotationParser; import org.apidesign.javap.ClassData; import org.apidesign.javap.FieldData; @@ -30,7 +31,7 @@ * * @author Jaroslav Tulach */ -public abstract class ByteCodeToJavaScript { +abstract class ByteCodeToJavaScript { private ClassData jc; final Appendable out; @@ -57,9 +58,12 @@ * * @param className suggested name of the class */ - protected String assignClass(String className) { + /* protected */ String assignClass(String className) { return className + " = "; } + /* protected */ String accessClass(String classOperation) { + return classOperation; + } /** * Converts a given class file to a JavaScript version. @@ -100,12 +104,15 @@ if (proto == null) { String sc = jc.getSuperClassName(); // with _ out.append("\n var pp = "). - append(sc.replace('/', '_')).append("(true);"); + append(accessClass(sc.replace('/', '_'))).append("(true);"); out.append("\n var p = CLS.prototype = pp;"); out.append("\n var c = p;"); out.append("\n var sprcls = pp.constructor.$class;"); } else { out.append("\n var p = CLS.prototype = ").append(proto[1]).append(";"); + if (proto[0] == null) { + proto[0] = "p"; + } out.append("\n var c = ").append(proto[0]).append(";"); out.append("\n var sprcls = null;"); } @@ -140,7 +147,8 @@ for (String superInterface : jc.getSuperInterfaces()) { out.append("\n c.$instOf_").append(superInterface.replace('/', '_')).append(" = true;"); } - out.append("\n CLS.$class = java_lang_Class(true);"); + out.append("\n CLS.$class = "); + out.append(accessClass("java_lang_Class(true);")); out.append("\n CLS.$class.jvmName = '").append(jc.getClassName()).append("';"); out.append("\n CLS.$class.superclass = sprcls;"); out.append("\n CLS.$class.cnstr = CLS;"); @@ -190,7 +198,7 @@ } final String mn = findMethodName(m, new StringBuilder()); if (mn.equals("class__V")) { - toInitilize.add(className(jc) + "(false)." + mn); + toInitilize.add(accessClass(className(jc)) + "(false)." + mn); } generateMethod(prefix, mn, m); return mn; @@ -849,7 +857,7 @@ int indx = readIntArg(byteCodes, i); String ci = jc.getClassName(indx); emit(out, "@1 = new @2;", - smapper.pushA(), ci.replace('/', '_')); + smapper.pushA(), accessClass(ci.replace('/', '_'))); addReference(ci); i += 2; break; @@ -1017,7 +1025,8 @@ String[] fi = jc.getFieldInfoName(indx); final int type = VarType.fromFieldType(fi[2].charAt(0)); emit(out, "@1 = @2.@3;", - smapper.pushT(type), fi[0].replace('/', '_'), fi[1]); + smapper.pushT(type), + accessClass(fi[0].replace('/', '_')), fi[1]); i += 2; addReference(fi[0]); break; @@ -1036,7 +1045,8 @@ String[] fi = jc.getFieldInfoName(indx); final int type = VarType.fromFieldType(fi[2].charAt(0)); emit(out, "@1.@2 = @3;", - fi[0].replace('/', '_'), fi[1], smapper.popT(type)); + accessClass(fi[0].replace('/', '_')), fi[1], + smapper.popT(type)); i += 2; addReference(fi[0]); break; @@ -1047,7 +1057,8 @@ if (!type.startsWith("[")) { // no way to check arrays right now // XXX proper exception - emit(out, "if (@1.$instOf_@2 != 1) throw {};", + emit(out, + "if (@1 !== null && !@1.$instOf_@2) throw {};", smapper.getA(0), type.replace('/', '_')); } i += 2; @@ -1057,7 +1068,8 @@ int indx = readIntArg(byteCodes, i); final String type = jc.getClassName(indx); emit(out, "@2 = @1.$instOf_@3 ? 1 : 0;", - smapper.popA(), smapper.pushI(), type.replace('/', '_')); + smapper.popA(), smapper.pushI(), + type.replace('/', '_')); i += 2; break; } @@ -1265,7 +1277,7 @@ } final String in = mi[0]; - out.append(in.replace('/', '_')); + out.append(accessClass(in.replace('/', '_'))); out.append("(false)."); out.append(mn); out.append('('); @@ -1339,6 +1351,7 @@ String s = jc.stringValue(entryIndex, classRef); if (classRef[0] != null) { addReference(classRef[0]); + s = accessClass(s.replace('/', '_')) + "(false).constructor.$class"; } return s; } @@ -1383,8 +1396,7 @@ String space; int index; if (!isStatic) { - out.append(p.args[0]); - space = ","; + space = outputArg(out, p.args, 0); index = 1; } else { space = ""; @@ -1392,9 +1404,8 @@ } for (int i = 0; i < cnt.length(); i++) { out.append(space); - out.append(p.args[index]); + space = outputArg(out, p.args, index); index++; - space = ","; } out.append(") {").append("\n"); out.append(p.body); @@ -1487,6 +1498,18 @@ ap.parse(data, cd); } + private static String outputArg(Appendable out, String[] args, int indx) throws IOException { + final String name = args[indx]; + if (name == null) { + return ""; + } + if (name.contains(",")) { + throw new IOException("Wrong parameter with ',': " + name); + } + out.append(name); + return ","; + } + private static void emit(final Appendable out, final String format, final CharSequence... params) throws IOException { @@ -1511,5 +1534,4 @@ out.append(format, processed, length); } - } diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/main/java/org/apidesign/vm4brwsr/GenJS.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/GenJS.java Wed Dec 12 14:07:53 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,198 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.vm4brwsr; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Enumeration; - -/** Generator of JavaScript from bytecode of classes on classpath of the VM. - * - * @author Jaroslav Tulach - */ -class GenJS extends ByteCodeToJavaScript { - public GenJS(Appendable out) { - super(out); - } - - static void compile(Appendable out, String... names) throws IOException { - compile(out, StringArray.asList(names)); - } - static void compile(ClassLoader l, Appendable out, String... names) throws IOException { - compile(l, out, StringArray.asList(names)); - } - static void compile(Appendable out, StringArray names) throws IOException { - compile(GenJS.class.getClassLoader(), out, names); - } - static void compile(ClassLoader l, Appendable out, StringArray names) throws IOException { - new GenJS(out).doCompile(l, names); - } - protected void doCompile(ClassLoader l, StringArray names) throws IOException { - StringArray processed = new StringArray(); - StringArray initCode = new StringArray(); - for (String baseClass : names.toArray()) { - references.add(baseClass); - for (;;) { - String name = null; - for (String n : references.toArray()) { - if (processed.contains(n)) { - continue; - } - name = n; - } - if (name == null) { - break; - } - InputStream is = loadClass(l, name); - if (is == null) { - throw new IOException("Can't find class " + name); - } - try { - String ic = compile(is); - processed.add(name); - initCode.add(ic == null ? "" : ic); - } catch (RuntimeException ex) { - if (out instanceof CharSequence) { - CharSequence seq = (CharSequence)out; - int lastBlock = seq.length(); - while (lastBlock-- > 0) { - if (seq.charAt(lastBlock) == '{') { - break; - } - } - throw new IOException("Error while compiling " + name + "\n" - + seq.subSequence(lastBlock + 1, seq.length()), ex - ); - } else { - throw new IOException("Error while compiling " + name + "\n" - + out, ex - ); - } - } - } - - for (String resource : scripts.toArray()) { - while (resource.startsWith("/")) { - resource = resource.substring(1); - } - InputStream emul = l.getResourceAsStream(resource); - if (emul == null) { - throw new IOException("Can't find " + resource); - } - readResource(emul, out); - } - scripts = new StringArray(); - - StringArray toInit = StringArray.asList(references.toArray()); - toInit.reverse(); - - for (String ic : toInit.toArray()) { - int indx = processed.indexOf(ic); - if (indx >= 0) { - out.append(initCode.toArray()[indx]).append("\n"); - initCode.toArray()[indx] = ""; - } - } - - } - } - private static void readResource(InputStream emul, Appendable out) throws IOException { - try { - int state = 0; - for (;;) { - int ch = emul.read(); - if (ch == -1) { - break; - } - if (ch < 0 || ch > 255) { - throw new IOException("Invalid char in emulation " + ch); - } - switch (state) { - case 0: - if (ch == '/') { - state = 1; - } else { - out.append((char)ch); - } - break; - case 1: - if (ch == '*') { - state = 2; - } else { - out.append('/').append((char)ch); - state = 0; - } - break; - case 2: - if (ch == '*') { - state = 3; - } - break; - case 3: - if (ch == '/') { - state = 0; - } else { - state = 2; - } - break; - } - } - } finally { - emul.close(); - } - } - - private static InputStream loadClass(ClassLoader l, String name) throws IOException { - Enumeration en = l.getResources(name + ".class"); - URL u = null; - while (en.hasMoreElements()) { - u = en.nextElement(); - } - if (u == null) { - throw new IOException("Can't find " + name); - } - if (u.toExternalForm().contains("rt.jar!")) { - throw new IOException("No emulation for " + u); - } - return u.openStream(); - } - - static String toString(String name) throws IOException { - StringBuilder sb = new StringBuilder(); - compile(sb, name); - return sb.toString().toString(); - } - - private StringArray scripts = new StringArray(); - private StringArray references = new StringArray(); - - @Override - protected boolean requireReference(String cn) { - if (references.contains(cn)) { - return false; - } - references.add(cn); - return true; - } - - @Override - protected void requireScript(String resourcePath) { - scripts.add(resourcePath); - } -} diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/main/java/org/apidesign/vm4brwsr/Main.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Wed Dec 12 14:07:53 2012 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Fri Dec 14 11:15:37 2012 +0100 @@ -32,6 +32,7 @@ public static void main(String... args) throws IOException { if (args.length < 2) { + System.err.println("Bck2Brwsr Translator from Java(tm) to JavaScript, (c) Jaroslav Tulach 2012"); System.err.println("Usage: java -cp ... -jar ... java/lang/Class org/your/App ..."); return; } @@ -40,7 +41,8 @@ StringArray classes = StringArray.asList(args); classes.delete(0); try { - GenJS.compile(w, classes); + Bck2Brwsr.generate(w, Main.class.getClassLoader(), + classes.toArray()); } finally { w.close(); } diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/main/java/org/apidesign/vm4brwsr/VM.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VM.java Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,208 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import java.io.InputStream; + +/** Generator of JavaScript from bytecode of classes on classpath of the VM. + * + * @author Jaroslav Tulach + */ +class VM extends ByteCodeToJavaScript { + public VM(Appendable out) { + super(out); + } + + static { + // uses VMLazy to load dynamic classes + VMLazy.init(); + } + + static void compile(Bck2Brwsr.Resources l, Appendable out, StringArray names) throws IOException { + new VM(out).doCompile(l, names); + } + protected void doCompile(Bck2Brwsr.Resources l, StringArray names) throws IOException { + out.append("(function VM(global) {"); + out.append("\n var vm = {};"); + StringArray processed = new StringArray(); + StringArray initCode = new StringArray(); + for (String baseClass : names.toArray()) { + references.add(baseClass); + for (;;) { + String name = null; + for (String n : references.toArray()) { + if (processed.contains(n)) { + continue; + } + name = n; + } + if (name == null) { + break; + } + InputStream is = loadClass(l, name); + if (is == null) { + throw new IOException("Can't find class " + name); + } + try { + String ic = compile(is); + processed.add(name); + initCode.add(ic == null ? "" : ic); + } catch (RuntimeException ex) { + if (out instanceof CharSequence) { + CharSequence seq = (CharSequence)out; + int lastBlock = seq.length(); + while (lastBlock-- > 0) { + if (seq.charAt(lastBlock) == '{') { + break; + } + } + throw new IOException("Error while compiling " + name + "\n" + + seq.subSequence(lastBlock + 1, seq.length()), ex + ); + } else { + throw new IOException("Error while compiling " + name + "\n" + + out, ex + ); + } + } + } + + for (String resource : scripts.toArray()) { + while (resource.startsWith("/")) { + resource = resource.substring(1); + } + InputStream emul = l.get(resource); + if (emul == null) { + throw new IOException("Can't find " + resource); + } + readResource(emul, out); + } + scripts = new StringArray(); + + StringArray toInit = StringArray.asList(references.toArray()); + toInit.reverse(); + + for (String ic : toInit.toArray()) { + int indx = processed.indexOf(ic); + if (indx >= 0) { + out.append(initCode.toArray()[indx]).append("\n"); + initCode.toArray()[indx] = ""; + } + } + } + out.append( + " global.bck2brwsr = function() {\n" + + " var args = arguments;\n" + + " var loader = {};\n" + + " loader.vm = vm;\n" + + " loader.loadClass = function(name) {\n" + + " var attr = name.replace__Ljava_lang_String_2CC(name, '.','_');\n" + + " var fn = vm[attr];\n" + + " if (fn) return fn(false);\n" + + " if (!args[0]) throw 'bck2brwsr initialized without loader function, cannot load ' + name;\n" + + " return vm.org_apidesign_vm4brwsr_VMLazy(false).\n" + + " load___3Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_String_2_3Ljava_lang_Object_2(loader, name, args);\n" + + " }\n" + + " return loader;\n" + + " };\n"); + out.append("}(this));"); + } + private static void readResource(InputStream emul, Appendable out) throws IOException { + try { + int state = 0; + for (;;) { + int ch = emul.read(); + if (ch == -1) { + break; + } + if (ch < 0 || ch > 255) { + throw new IOException("Invalid char in emulation " + ch); + } + switch (state) { + case 0: + if (ch == '/') { + state = 1; + } else { + out.append((char)ch); + } + break; + case 1: + if (ch == '*') { + state = 2; + } else { + out.append('/').append((char)ch); + state = 0; + } + break; + case 2: + if (ch == '*') { + state = 3; + } + break; + case 3: + if (ch == '/') { + state = 0; + } else { + state = 2; + } + break; + } + } + } finally { + emul.close(); + } + } + + private static InputStream loadClass(Bck2Brwsr.Resources l, String name) throws IOException { + return l.get(name + ".class"); // NOI18N + } + + static String toString(String name) throws IOException { + StringBuilder sb = new StringBuilder(); +// compile(sb, name); + return sb.toString().toString(); + } + + private StringArray scripts = new StringArray(); + private StringArray references = new StringArray(); + + @Override + protected boolean requireReference(String cn) { + if (references.contains(cn)) { + return false; + } + references.add(cn); + return true; + } + + @Override + protected void requireScript(String resourcePath) { + scripts.add(resourcePath); + } + + @Override + String assignClass(String className) { + return "vm." + className + " = "; + } + + @Override + String accessClass(String className) { + return "vm." + className; + } +} diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/VMLazy.java Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,129 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import org.apidesign.bck2brwsr.core.JavaScriptBody; + +/** + * + * @author Jaroslav Tulach + */ +final class VMLazy { + private final Object loader; + private final Object[] args; + + private VMLazy(Object loader, Object[] args) { + this.loader = loader; + this.args = args; + } + + static void init() { + } + + @JavaScriptBody(args={"l", "res", "args" }, body = "" + + "\ntry {" + + "\n return args[0](res.toString());" + + "\n} catch (x) {" + + "\n throw Object.getOwnPropertyNames(l.vm).toString() + x.toString();" + + "\n}") + private static native byte[] read(Object l, String res, Object[] args); + + static Object load(Object loader, String name, Object[] arguments) + throws IOException, ClassNotFoundException { + return new VMLazy(loader, arguments).load(name); + } + + private Object load(String name) + throws IOException, ClassNotFoundException { + String res = name.replace('.', '/') + ".class"; + byte[] arr = read(loader, res, args); + if (arr == null) { + throw new ClassNotFoundException(name); + } +// beingDefined(loader, name); + StringBuilder out = new StringBuilder(); + out.append("var loader = arguments[0];\n"); + out.append("var vm = loader.vm;\n"); + new Gen(this, out).compile(new ByteArrayInputStream(arr)); + String code = out.toString().toString(); + String under = name.replace('.', '_'); + return applyCode(loader, under, code); + } + +/* possibly not needed: + @JavaScriptBody(args = {"loader", "n" }, body = + "var cls = n.replace__Ljava_lang_String_2CC(n, '.','_').toString();" + + "loader.vm[cls] = true;\n" + ) + private static native void beingDefined(Object loader, String name); +*/ + + + @JavaScriptBody(args = {"loader", "name", "script" }, body = + "try {\n" + + " new Function(script)(loader, name);\n" + + "} catch (ex) {\n" + + " throw 'Cannot compile ' + ex + ' line: ' + ex.lineNumber + ' script:\\n' + script;\n" + + "}\n" + + "return vm[name](false);\n" + ) + private static native Object applyCode(Object loader, String name, String script); + + + private static final class Gen extends ByteCodeToJavaScript { + private final VMLazy lazy; + + public Gen(VMLazy vm, Appendable out) { + super(out); + this.lazy = vm; + } + + @JavaScriptBody(args = {"self", "n"}, + body = + "var cls = n.replace__Ljava_lang_String_2CC(n, '/','_').toString();" + + "\nvar dot = n.replace__Ljava_lang_String_2CC(n,'/','.').toString();" + + "\nvar lazy = self.fld_lazy;" + + "\nvar loader = lazy.fld_loader;" + + "\nvar vm = loader.vm;" + + "\nif (vm[cls]) return false;" + + "\nvm[cls] = function() {" + + "\n return lazy.load__Ljava_lang_Object_2Ljava_lang_String_2(lazy, dot);" + + "\n};" + + "\nreturn true;") + @Override + protected boolean requireReference(String internalClassName) { + throw new UnsupportedOperationException(); + } + + @Override + protected void requireScript(String resourcePath) { + } + + @Override + String assignClass(String className) { + return "vm[arguments[1]]="; + } + + @Override + String accessClass(String classOperation) { + return "vm." + classOperation; + } + } +} diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/BytesLoader.java Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,58 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Set; +import java.util.TreeSet; + +/** + * + * @author Jaroslav Tulach + */ +public final class BytesLoader { + private static Set requested = new TreeSet(); + + public byte[] get(String name) throws IOException { + if (!requested.add(name)) { + throw new IllegalStateException("Requested for second time: " + name); + } + InputStream is = BytesLoader.class.getClassLoader().getResourceAsStream(name); + if (is == null) { + throw new IOException("Can't find " + name); + } + byte[] arr = new byte[is.available()]; + int len = is.read(arr); + if (len != arr.length) { + throw new IOException("Read only " + len + " wanting " + arr.length); + } + /* + System.err.print("loader['" + name + "'] = ["); + for (int i = 0; i < arr.length; i++) { + if (i > 0) { + System.err.print(", "); + } + System.err.print(arr[i]); + } + System.err.println("]"); + */ + return arr; + } + +} diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Wed Dec 12 14:07:53 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Fri Dec 14 11:15:37 2012 +0100 @@ -110,6 +110,12 @@ "java.io.IOException", false, null ); } + @Test public void jsClassParam() throws Exception { + assertExec("Calls the nameOfIO()", Classes.class, + "nameOfIO__Ljava_lang_String_2", + "java.io.IOException" + ); + } private static CharSequence codeSeq; private static Invocable code; diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/test/java/org/apidesign/vm4brwsr/Classes.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Wed Dec 12 14:07:53 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Fri Dec 14 11:15:37 2012 +0100 @@ -30,6 +30,14 @@ @ClassesMarker(number = 10) @ClassesNamer(name = "my text") public class Classes { + public static String nameOfIO() { + return nameFor(IOException.class); + } + + private static String nameFor(Class c) { + return c.getName(); + } + public static boolean equalsClassesOfExceptions() { return MalformedURLException.class.getSuperclass() == IOException.class; } diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/test/java/org/apidesign/vm4brwsr/CompareVMs.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/CompareVMs.java Wed Dec 12 14:07:53 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/CompareVMs.java Fri Dec 14 11:15:37 2012 +0100 @@ -18,7 +18,10 @@ package org.apidesign.vm4brwsr; import java.lang.reflect.Method; +import java.util.Map; +import java.util.WeakHashMap; import javax.script.Invocable; +import javax.script.ScriptContext; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import org.testng.Assert; @@ -73,7 +76,7 @@ if (v1 instanceof Number) { v1 = ((Number)v1).doubleValue(); } - Assert.assertEquals(v1, v2, "Comparing results"); + Assert.assertEquals(v2, v1, "Comparing results"); } @Override @@ -85,52 +88,49 @@ private final Method m; private final boolean js; Object value; - private static Invocable code; - private static CharSequence codeSeq; + private Invocable code; + private CharSequence codeSeq; + private static final Map compiled = new WeakHashMap(); private Run(Method m, boolean js) { this.m = m; this.js = js; } - private static void compileTheCode(Class clazz) throws Exception { - if (code != null) { + private void compileTheCode(Class clazz) throws Exception { + final Object[] data = compiled.get(clazz); + if (data != null) { + code = (Invocable) data[0]; + codeSeq = (CharSequence) data[1]; return; } StringBuilder sb = new StringBuilder(); - class SkipMe extends GenJS { - - public SkipMe(Appendable out) { - super(out); - } - - @Override - protected boolean requireReference(String cn) { - if (cn.contains("CompareVMs")) { - return true; - } - return super.requireReference(cn); - } - } - SkipMe sm = new SkipMe(sb); - sm.doCompile(CompareVMs.class.getClassLoader(), StringArray.asList( - clazz.getName().replace('.', '/'))); + Bck2Brwsr.generate(sb, CompareVMs.class.getClassLoader()); ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine js = sem.getEngineByExtension("js"); + js.getContext().setAttribute("loader", new BytesLoader(), ScriptContext.ENGINE_SCOPE); + + sb.append("\nfunction initVM() {" + + "\n return bck2brwsr(" + + "\n function(name) { return loader.get(name);}" + + "\n );" + + "\n};"); Object res = js.eval(sb.toString()); Assert.assertTrue(js instanceof Invocable, "It is invocable object: " + res); code = (Invocable) js; codeSeq = sb; + compiled.put(clazz, new Object[] { code, codeSeq }); } @Test(groups = "run") public void executeCode() throws Throwable { if (js) { try { compileTheCode(m.getDeclaringClass()); - Object inst = code.invokeFunction(m.getDeclaringClass().getName().replace('.', '_'), false); - value = code.invokeMethod(inst, m.getName() + "__I"); + Object vm = code.invokeFunction("initVM"); + Object inst = code.invokeMethod(vm, "loadClass", m.getDeclaringClass().getName()); + value = code.invokeMethod(inst, m.getName() + "__" + computeSignature(m)); } catch (Exception ex) { throw new AssertionError(StaticMethodTest.dumpJS(codeSeq)).initCause(ex); } @@ -142,5 +142,56 @@ public String getTestName() { return m.getName() + (js ? "[JavaScript]" : "[Java]"); } + + private static String computeSignature(Method m) { + StringBuilder sb = new StringBuilder(); + appendType(sb, m.getReturnType()); + for (Class c : m.getParameterTypes()) { + appendType(sb, c); + } + return sb.toString(); + } + + private static void appendType(StringBuilder sb, Class t) { + if (t == null) { + sb.append('V'); + return; + } + if (t.isPrimitive()) { + int ch = -1; + if (t == int.class) { + ch = 'I'; + } + if (t == short.class) { + ch = 'S'; + } + if (t == byte.class) { + ch = 'B'; + } + if (t == boolean.class) { + ch = 'Z'; + } + if (t == long.class) { + ch = 'J'; + } + if (t == float.class) { + ch = 'F'; + } + if (t == double.class) { + ch = 'D'; + } + assert ch != -1 : "Unknown primitive type " + t; + sb.append((char)ch); + return; + } + if (t.isArray()) { + sb.append("_3"); + appendType(sb, t.getComponentType()); + return; + } + sb.append('L'); + sb.append(t.getName().replace('.', '_')); + sb.append("_2"); + } } } diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java Wed Dec 12 14:07:53 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java Fri Dec 14 11:15:37 2012 +0100 @@ -161,7 +161,8 @@ Object ret = null; try { - ret = code.invokeFunction(clazz.getName().replace('.', '_'), true); + ret = code.invokeFunction("bck2brwsr"); + ret = code.invokeMethod(ret, "loadClass", clazz.getName()); ret = code.invokeMethod(ret, method, args); } catch (ScriptException ex) { fail("Execution failed in\n" + StaticMethodTest.dumpJS(codeSeq), ex); diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/test/java/org/apidesign/vm4brwsr/StaticMethod.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethod.java Wed Dec 12 14:07:53 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethod.java Fri Dec 14 11:15:37 2012 +0100 @@ -106,6 +106,11 @@ throw new IllegalStateException(); } + public static String castNull(boolean n) { + Object value = n ? null : "Ahoj"; + return (String)value; + } + public static String swtch(int what) { switch (what) { case 0: return "Jarda"; diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java Wed Dec 12 14:07:53 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java Fri Dec 14 11:15:37 2012 +0100 @@ -20,6 +20,9 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Enumeration; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; @@ -238,6 +241,13 @@ ); } + @Test public void checkNullCast() throws Exception { + assertExec("Null can be cast to any type", + StaticMethod.class, "castNull__Ljava_lang_String_2Z", + null, true + ); + } + private static CharSequence codeSeq; private static Invocable code; @@ -262,7 +272,8 @@ ) throws Exception { Object ret = null; try { - ret = toRun.invokeFunction(clazz.getName().replace('.', '_'), true); + ret = toRun.invokeFunction("bck2brwsr"); + ret = toRun.invokeMethod(ret, "loadClass", clazz.getName()); ret = toRun.invokeMethod(ret, method, args); } catch (ScriptException ex) { fail("Execution failed in\n" + dumpJS(theCode), ex); @@ -288,7 +299,7 @@ if (sb == null) { sb = new StringBuilder(); } - GenJS.compile(sb, names); + Bck2Brwsr.generate(sb, new EmulationResources(), names); ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine js = sem.getEngineByExtension("js"); if (eng != null) { @@ -313,4 +324,21 @@ w.close(); return new StringBuilder(f.getPath()); } + private static class EmulationResources implements Bck2Brwsr.Resources { + @Override + public InputStream get(String name) throws IOException { + Enumeration en = StaticMethodTest.class.getClassLoader().getResources(name); + URL u = null; + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + if (u.toExternalForm().contains("rt.jar!")) { + throw new IOException("No emulation for " + u); + } + return u.openStream(); + } + } } diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/test/java/org/apidesign/vm4brwsr/VMLazy.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/VMLazy.java Wed Dec 12 14:07:53 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program 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 for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.vm4brwsr; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import org.apidesign.bck2brwsr.core.JavaScriptBody; - -/** - * - * @author Jaroslav Tulach - */ -class VMLazy extends ByteCodeToJavaScript { - private final Object vm; - private final Object global; - - private VMLazy(Object global, Object vm, Appendable out) { - super(out); - this.vm = vm; - this.global = global; - } - - static String toJavaScript(Object global, Object vm, byte[] is) throws IOException { - StringBuilder sb = new StringBuilder(); - new VMLazy(global, vm, sb).compile(new ByteArrayInputStream(is)); - return sb.toString().toString(); - } - - @JavaScriptBody(args = { "self", "n" }, - body= - "var cls = n.replace__Ljava_lang_String_2CC(n,'/','_').toString();" - + "var glb = self.fld_global;" - + "var vm = self.fld_vm;" - + "if (glb[cls]) return false;" - + "glb[cls] = function() {" - + " return vm.loadClass(n,cls);" - + "};" - + "return true;" - ) - @Override - protected boolean requireReference(String internalClassName) { - throw new UnsupportedOperationException(); - } - - @Override - protected void requireScript(String resourcePath) { - } - - @Override - protected String assignClass(String className) { - return "arguments[0][arguments[1]]="; - } -} diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java Wed Dec 12 14:07:53 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java Fri Dec 14 11:15:37 2012 +0100 @@ -17,8 +17,6 @@ */ package org.apidesign.vm4brwsr; -import java.io.IOException; -import java.io.InputStream; import javax.script.Invocable; import javax.script.ScriptContext; import javax.script.ScriptEngine; @@ -40,50 +38,32 @@ @BeforeClass public void compileTheCode() throws Exception { StringBuilder sb = new StringBuilder(); - - sb.append("\nfunction test(clazz, as, method) {"); - sb.append("\n var l = new lazyVM(this);"); - sb.append("\n var c = l.loadClass(clazz, as);"); + sb.append("\nvar data = {};"); + sb.append("\nfunction test(clazz, method) {"); + sb.append("\n if (!data.bck2brwsr) data.bck2brwsr = bck2brwsr(function(name) { return loader.get(name); });"); + sb.append("\n var c = data.bck2brwsr.loadClass(clazz);"); sb.append("\n return c[method]();"); sb.append("\n}"); - sb.append("\nfunction lazyVM(global) {"); - sb.append("\n var self = this;"); - sb.append("\n var glb = global;"); - sb.append("\n lazyVM.prototype.loadClass = function(res, name) {"); - sb.append("\n var script = org_apidesign_vm4brwsr_VMLazy(true)." - + "toJavaScript__Ljava_lang_String_2Ljava_lang_Object_2Ljava_lang_Object_2_3B(" - + " glb, self," - + " loader.get(res + '.class')" - + ");"); - sb.append("\n try {"); - sb.append("\n new Function(script)(glb, name);"); - sb.append("\n } catch (ex) {"); - sb.append("\n throw 'Cannot compile ' + res + ' error: ' + ex + ' script:\\n' + script;"); - sb.append("\n };"); - sb.append("\n return glb[name](true);"); - sb.append("\n };"); - sb.append("\n"); - sb.append("\n}\n"); - + ScriptEngine[] arr = { null }; code = StaticMethodTest.compileClass(sb, arr, - "org/apidesign/vm4brwsr/VMLazy" + "org/apidesign/vm4brwsr/VM" ); - arr[0].getContext().setAttribute("loader", new FindBytes(), ScriptContext.ENGINE_SCOPE); + arr[0].getContext().setAttribute("loader", new BytesLoader(), ScriptContext.ENGINE_SCOPE); codeSeq = sb; } @Test public void invokeStaticMethod() throws Exception { assertExec("Trying to get -1", "test", Double.valueOf(-1), - "org/apidesign/vm4brwsr/StaticMethod", "org_apidesign_vm4brwsr_StaticMethod", "minusOne__I" + StaticMethod.class.getName(), "minusOne__I" ); } @Test public void loadDependantClass() throws Exception { - assertExec("Trying to get zero", "test", Double.valueOf(0), - "org/apidesign/vm4brwsr/InstanceSub", "org_apidesign_vm4brwsr_InstanceSub", "recallDbl__D" + assertExec("Expecting zero", "test", Double.valueOf(0), + InstanceSub.class.getName(), "recallDbl__D" ); } @@ -104,29 +84,4 @@ } assertEquals(ret, expRes, msg + "was: " + ret + "\n" + StaticMethodTest.dumpJS(codeSeq)); } - - public static final class FindBytes { - public byte[] get(String name) throws IOException { - InputStream is = VMLazyTest.class.getClassLoader().getResourceAsStream(name); - if (is == null) { - throw new IOException("Can't find " + name); - } - byte[] arr = new byte[is.available()]; - int len = is.read(arr); - if (len != arr.length) { - throw new IOException("Read only " + len + " wanting " + arr.length); - } - /* - System.err.print("loader['" + name + "'] = ["); - for (int i = 0; i < arr.length; i++) { - if (i > 0) { - System.err.print(", "); - } - System.err.print(arr[i]); - } - System.err.println("]"); - */ - return arr; - } - } } diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java Wed Dec 12 14:07:53 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java Fri Dec 14 11:15:37 2012 +0100 @@ -35,13 +35,43 @@ private static CharSequence codeSeq; private static Invocable code; - @Test public void compareTheGeneratedCode() throws Exception { - byte[] arr = readClass("/org/apidesign/vm4brwsr/Array.class"); + @Test public void compareGeneratedCodeForArrayClass() throws Exception { + compareCode("/org/apidesign/vm4brwsr/Array.class"); + } + + @Test public void compareGeneratedCodeForClassesClass() throws Exception { + compareCode("/org/apidesign/vm4brwsr/Classes.class"); + } + + @BeforeClass + public void compileTheCode() throws Exception { + StringBuilder sb = new StringBuilder(); + code = StaticMethodTest.compileClass(sb, + "org/apidesign/vm4brwsr/VMinVM" + ); + codeSeq = sb; + } + + private static byte[] readClass(String res) throws IOException { + InputStream is1 = VMinVMTest.class.getResourceAsStream(res); + assertNotNull(is1, "Stream found"); + byte[] arr = new byte[is1.available()]; + int len = is1.read(arr); + is1.close(); + if (len != arr.length) { + throw new IOException("Wrong len " + len + " for arr: " + arr.length); + } + return arr; + } + + private void compareCode(final String nm) throws Exception, IOException { + byte[] arr = readClass(nm); String ret1 = VMinVM.toJavaScript(arr); Object ret; try { - ret = code.invokeFunction(VMinVM.class.getName().replace('.', '_'), true); + ret = code.invokeFunction("bck2brwsr"); + ret = code.invokeMethod(ret, "loadClass", VMinVM.class.getName()); ret = code.invokeMethod(ret, "toJavaScript__Ljava_lang_String_2_3B", arr); } catch (Exception ex) { File f = File.createTempFile("execution", ".js"); @@ -64,27 +94,14 @@ assertTrue(ret instanceof String, "It is string: " + ret); - assertEquals((String)ret, ret1.toString(), "The code is the same"); - } - - @BeforeClass - public void compileTheCode() throws Exception { - StringBuilder sb = new StringBuilder(); - code = StaticMethodTest.compileClass(sb, - "org/apidesign/vm4brwsr/VMinVM" - ); - codeSeq = sb; - } - - private static byte[] readClass(String res) throws IOException { - InputStream is1 = VMinVMTest.class.getResourceAsStream(res); - assertNotNull(is1, "Stream found"); - byte[] arr = new byte[is1.available()]; - int len = is1.read(arr); - is1.close(); - if (len != arr.length) { - throw new IOException("Wrong len " + len + " for arr: " + arr.length); + if (!ret1.toString().equals(ret)) { + StringBuilder msg = new StringBuilder("Difference found between "); + msg.append(StaticMethodTest.dumpJS(ret1)); + msg.append(" "); + msg.append(StaticMethodTest.dumpJS((CharSequence) ret)); + msg.append(" compiled by "); + msg.append(StaticMethodTest.dumpJS(codeSeq)); + fail(msg.toString()); } - return arr; } } diff -r ec7d8bc17725 -r 15cbc8cb2163 vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareStringsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/tck/CompareStringsTest.java Fri Dec 14 11:15:37 2012 +0100 @@ -0,0 +1,43 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.vm4brwsr.tck; + +import org.apidesign.vm4brwsr.Compare; +import org.apidesign.vm4brwsr.CompareVMs; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class CompareStringsTest { + @Compare public String deleteLastTwoCharacters() { + StringBuilder sb = new StringBuilder(); + sb.append("453.0"); + if (sb.toString().endsWith(".0")) { + final int l = sb.length(); + sb.delete(l - 2, l); + } + return sb.toString().toString(); + } + + @Factory + public static Object[] create() { + return CompareVMs.create(CompareStringsTest.class); + } +}