# HG changeset patch # User Jaroslav Tulach # Date 1355299782 -3600 # Node ID f36b3c273de6e5f94720d400740b671493f52280 # Parent a20721a107171429b6ec0a004df4a5236ddd1641# Parent 503b158cc0931def2f6eed7790568a6b8dd321f8 Merging lazyvm into default branch - it seems to be stable enough to be used. diff -r a20721a10717 -r f36b3c273de6 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 Mon Dec 10 12:03:22 2012 +0100 +++ b/emul/src/main/resources/org/apidesign/vm4brwsr/emul/java_lang_String.js Wed Dec 12 09:09:42 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 a20721a10717 -r f36b3c273de6 javap/src/main/java/org/apidesign/javap/ClassData.java --- a/javap/src/main/java/org/apidesign/javap/ClassData.java Mon Dec 10 12:03:22 2012 +0100 +++ b/javap/src/main/java/org/apidesign/javap/ClassData.java Wed Dec 12 09:09:42 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 a20721a10717 -r f36b3c273de6 javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java --- a/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Mon Dec 10 12:03:22 2012 +0100 +++ b/javaquery/api/src/test/java/org/apidesign/bck2brwsr/htmlpage/ProcessPageTest.java Wed Dec 12 09:09:42 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 a20721a10717 -r f36b3c273de6 mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Bck2BrswrMojo.java --- a/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Bck2BrswrMojo.java Mon Dec 10 12:03:22 2012 +0100 +++ b/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/Bck2BrswrMojo.java Wed Dec 12 09:09:42 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 a20721a10717 -r f36b3c273de6 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 Wed Dec 12 09:09:42 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 a20721a10717 -r f36b3c273de6 vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Mon Dec 10 12:03:22 2012 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Wed Dec 12 09:09:42 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; @@ -29,7 +30,7 @@ * * @author Jaroslav Tulach */ -public abstract class ByteCodeToJavaScript { +abstract class ByteCodeToJavaScript { private ClassData jc; final Appendable out; @@ -56,9 +57,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. @@ -99,7 +103,7 @@ 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;"); @@ -139,7 +143,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;"); @@ -191,7 +196,7 @@ final String mn = findMethodName(m, argsCnt); out.append(prefix).append(mn).append(" = function"); if (mn.equals("class__V")) { - toInitilize.add(className(jc) + "(false)." + mn); + toInitilize.add(accessClass(className(jc)) + "(false)." + mn); } out.append('('); String space = ""; @@ -638,7 +643,7 @@ int indx = readIntArg(byteCodes, i); String ci = jc.getClassName(indx); out.append("s.push(new "); - out.append(ci.replace('/','_')); + out.append(accessClass(ci.replace('/','_'))); out.append("());"); addReference(ci); i += 2; @@ -731,7 +736,7 @@ case opc_getstatic: { int indx = readIntArg(byteCodes, i); String[] fi = jc.getFieldInfoName(indx); - out.append("s.push(").append(fi[0].replace('/', '_')); + out.append("s.push(").append(accessClass(fi[0].replace('/', '_'))); out.append('.').append(fi[1]).append(");"); i += 2; addReference(fi[0]); @@ -740,7 +745,7 @@ case opc_putstatic: { int indx = readIntArg(byteCodes, i); String[] fi = jc.getFieldInfoName(indx); - out.append(fi[0].replace('/', '_')); + out.append(accessClass(fi[0].replace('/', '_'))); out.append('.').append(fi[1]).append(" = s.pop();"); i += 2; addReference(fi[0]); @@ -951,7 +956,7 @@ out.append("s.push("); } final String in = mi[0]; - out.append(in.replace('/', '_')); + out.append(accessClass(in.replace('/', '_'))); out.append("(false)."); out.append(mn); out.append('('); @@ -1031,6 +1036,7 @@ String s = jc.stringValue(entryIndex, classRef); if (classRef[0] != null) { addReference(classRef[0]); + s = accessClass(s.replace('/', '_')) + "(false).constructor.$class"; } return s; } diff -r a20721a10717 -r f36b3c273de6 vm/src/main/java/org/apidesign/vm4brwsr/GenJS.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/GenJS.java Mon Dec 10 12:03:22 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 a20721a10717 -r f36b3c273de6 vm/src/main/java/org/apidesign/vm4brwsr/Main.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Mon Dec 10 12:03:22 2012 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Main.java Wed Dec 12 09:09:42 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; } @@ -39,7 +40,7 @@ Writer w = new BufferedWriter(new FileWriter(args[0])); StringArray classes = StringArray.asList(args); classes.delete(0); - GenJS.compile(w, classes); + Bck2Brwsr.generate(w, Main.class.getClassLoader(), classes.toArray()); w.close(); } } diff -r a20721a10717 -r f36b3c273de6 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 Wed Dec 12 09:09:42 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 a20721a10717 -r f36b3c273de6 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 Wed Dec 12 09:09:42 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 a20721a10717 -r f36b3c273de6 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 Wed Dec 12 09:09:42 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 a20721a10717 -r f36b3c273de6 vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Mon Dec 10 12:03:22 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Wed Dec 12 09:09:42 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 a20721a10717 -r f36b3c273de6 vm/src/test/java/org/apidesign/vm4brwsr/Classes.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Mon Dec 10 12:03:22 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Wed Dec 12 09:09:42 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 a20721a10717 -r f36b3c273de6 vm/src/test/java/org/apidesign/vm4brwsr/CompareVMs.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/CompareVMs.java Mon Dec 10 12:03:22 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/CompareVMs.java Wed Dec 12 09:09:42 2012 +0100 @@ -21,6 +21,7 @@ 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; @@ -104,26 +105,17 @@ 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); @@ -136,7 +128,8 @@ if (js) { try { compileTheCode(m.getDeclaringClass()); - Object inst = code.invokeFunction(m.getDeclaringClass().getName().replace('.', '_'), false); + 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); diff -r a20721a10717 -r f36b3c273de6 vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java Mon Dec 10 12:03:22 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/NumberTest.java Wed Dec 12 09:09:42 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 a20721a10717 -r f36b3c273de6 vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java Mon Dec 10 12:03:22 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/StaticMethodTest.java Wed Dec 12 09:09:42 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; @@ -269,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); @@ -295,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) { @@ -320,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 a20721a10717 -r f36b3c273de6 vm/src/test/java/org/apidesign/vm4brwsr/VMLazy.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/VMLazy.java Mon Dec 10 12:03:22 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 a20721a10717 -r f36b3c273de6 vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java Mon Dec 10 12:03:22 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/VMLazyTest.java Wed Dec 12 09:09:42 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 a20721a10717 -r f36b3c273de6 vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java Mon Dec 10 12:03:22 2012 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/VMinVMTest.java Wed Dec 12 09:09:42 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; } }