# HG changeset patch # User Jaroslav Tulach # Date 1348143601 -7200 # Node ID 361b76189f8d3c88e24a117601d278fd37eb9fc0 # Parent cb0cfba1b863dc5279dcdfbae86924d4ee8015f9 The compilation to JavaScript now identifies list of external references and provides them back to the caller diff -r cb0cfba1b863 -r 361b76189f8d src/main/java/org/apidesign/java4browser/ByteCodeToJavaScript.java --- a/src/main/java/org/apidesign/java4browser/ByteCodeToJavaScript.java Thu Sep 20 10:39:54 2012 +0200 +++ b/src/main/java/org/apidesign/java4browser/ByteCodeToJavaScript.java Thu Sep 20 14:20:01 2012 +0200 @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Collection; import java.util.List; import static org.netbeans.modules.classfile.ByteCodes.*; import org.netbeans.modules.classfile.CPClassInfo; @@ -39,21 +40,37 @@ public final class ByteCodeToJavaScript { private final ClassFile jc; private final Appendable out; + private final Collection references; - private ByteCodeToJavaScript(ClassFile jc, Appendable out) { + private ByteCodeToJavaScript( + ClassFile jc, Appendable out, Collection references + ) { this.jc = jc; this.out = out; + this.references = references; } - - /** Converts a given class file to a JavaScript version. - * @param fileName the name of the file we are reading + + /** + * Converts a given class file to a JavaScript version. + * * @param classFile input stream with code of the .class file * @param out a {@link StringBuilder} or similar to generate the output to + * @param references a write only collection where the system adds list of + * other classes that were referenced and should be loaded in order the + * generated JavaScript code works properly. The names are in internal + * JVM form so String is java/lang/String. Can be null + * if one is not interested in knowing references * @throws IOException if something goes wrong during read or write or translating */ - public static void compile(String fileName, InputStream classFile, Appendable out) throws IOException { + + public static void compile( + InputStream classFile, Appendable out, + Collection references + ) throws IOException { ClassFile jc = new ClassFile(classFile, true); - ByteCodeToJavaScript compiler = new ByteCodeToJavaScript(jc, out); + ByteCodeToJavaScript compiler = new ByteCodeToJavaScript( + jc, out, references + ); for (Method m : jc.getMethods()) { if (m.isStatic()) { compiler.generateStaticMethod(m); @@ -69,7 +86,7 @@ out.append("function java_lang_Object(){}\n"); // XXX temporary out.append("function java_lang_Object_consV(self){}\n"); // XXX temporary - final String className = jc.getName().getExternalName().replace('.', '_'); + final String className = jc.getName().getInternalName().replace('/', '_'); out.append("\nfunction ").append(className); out.append("() {"); for (Method m : jc.getMethods()) { @@ -87,12 +104,12 @@ ClassName sc = jc.getSuperClass(); if (sc != null) { out.append("\n ").append(className) - .append(".prototype = new ").append(sc.getExternalName().replace('.', '_')); + .append(".prototype = new ").append(sc.getInternalName().replace('/', '_')); } } private void generateStaticMethod(Method m) throws IOException { out.append("\nfunction ").append( - jc.getName().getExternalName().replace('.', '_') + jc.getName().getInternalName().replace('/', '_') ).append('_').append(findMethodName(m)); out.append('('); String space = ""; @@ -110,28 +127,32 @@ } out.append(") {").append("\n"); final Code code = m.getCode(); - int len = code.getMaxLocals(); - for (int index = args.size(), i = args.size(); i < len; i++) { - out.append(" var "); - out.append("arg").append(String.valueOf(i)).append(";\n"); + if (code != null) { + int len = code.getMaxLocals(); + for (int index = args.size(), i = args.size(); i < len; i++) { + out.append(" var "); + out.append("arg").append(String.valueOf(i)).append(";\n"); + } + out.append(" var stack = new Array("); + out.append(Integer.toString(code.getMaxStack())); + out.append(");\n"); + produceCode(code.getByteCodes()); + } else { + out.append(" /* no code found for ").append(m.getTypeSignature()).append(" */\n"); } - out.append(" var stack = new Array("); - out.append(Integer.toString(code.getMaxStack())); - out.append(");\n"); - produceCode(code.getByteCodes()); out.append("}"); } private void generateMethodReference(Method m) throws IOException { final String name = findMethodName(m); out.append("\n this.").append(name).append(" = ") - .append(jc.getName().getExternalName().replace('.', '_')) + .append(jc.getName().getInternalName().replace('/', '_')) .append('_').append(name).append(";"); } private void generateInstanceMethod(Method m) throws IOException { out.append("\nfunction ").append( - jc.getName().getExternalName().replace('.', '_') + jc.getName().getInternalName().replace('/', '_') ).append('_').append(findMethodName(m)); out.append("(arg0"); String space = ","; @@ -148,15 +169,19 @@ } out.append(") {").append("\n"); final Code code = m.getCode(); - int len = code.getMaxLocals(); - for (int index = args.size(), i = args.size(); i < len; i++) { - out.append(" var "); - out.append("arg").append(String.valueOf(i + 1)).append(";\n"); + if (code != null) { + int len = code.getMaxLocals(); + for (int index = args.size(), i = args.size(); i < len; i++) { + out.append(" var "); + out.append("arg").append(String.valueOf(i + 1)).append(";\n"); + } + out.append(";\n var stack = new Array("); + out.append(Integer.toString(code.getMaxStack())); + out.append(");\n"); + produceCode(code.getByteCodes()); + } else { + out.append(" /* no code found for ").append(m.getTypeSignature()).append(" */\n"); } - out.append(";\n var stack = new Array("); - out.append(Integer.toString(code.getMaxStack())); - out.append(");\n"); - produceCode(code.getByteCodes()); out.append("}"); } @@ -405,8 +430,9 @@ int indx = readIntArg(byteCodes, i); CPClassInfo ci = jc.getConstantPool().getClass(indx); out.append("stack.push("); - out.append("new ").append(ci.getClassName().getExternalName().replace('.','_')); + out.append("new ").append(ci.getClassName().getInternalName().replace('/','_')); out.append(");"); + addReference(ci.getClassName().getInternalName()); i += 2; break; } @@ -426,17 +452,21 @@ case bc_getstatic: { int indx = readIntArg(byteCodes, i); CPFieldInfo fi = (CPFieldInfo) jc.getConstantPool().get(indx); - out.append("stack.push(").append(fi.getClassName().getExternalName().replace('.', '_')); + final String in = fi.getClassName().getInternalName(); + out.append("stack.push(").append(in.replace('/', '_')); out.append('_').append(fi.getFieldName()).append(");"); i += 2; + addReference(in); break; } case bc_putstatic: { int indx = readIntArg(byteCodes, i); CPFieldInfo fi = (CPFieldInfo) jc.getConstantPool().get(indx); - out.append(fi.getClassName().getExternalName().replace('.', '_')); + final String in = fi.getClassName().getInternalName(); + out.append(in.replace('/', '_')); out.append('_').append(fi.getFieldName()).append(" = stack.pop();"); i += 2; + addReference(in); break; } case bc_putfield: { @@ -451,7 +481,7 @@ int indx = readIntArg(byteCodes, i); CPClassInfo ci = jc.getConstantPool().getClass(indx); out.append("stack.push(stack.pop().$instOf_") - .append(ci.getClassName().getExternalName().replace('.', '_')) + .append(ci.getClassName().getInternalName().replace('/', '_')) .append(" ? 1 : 0);"); i += 2; } @@ -541,7 +571,7 @@ private void generateStaticField(Variable v) throws IOException { out.append("\nvar ") - .append(jc.getName().getExternalName().replace('.', '_')) + .append(jc.getName().getInternalName().replace('/', '_')) .append('_').append(v.getName()).append(" = 0;"); } @@ -586,7 +616,8 @@ if (hasReturn[0]) { out.append("stack.push("); } - out.append(mi.getClassName().getInternalName().replace('/', '_')); + final String in = mi.getClassName().getInternalName(); + out.append(in.replace('/', '_')); out.append('_'); out.append(mn); out.append('('); @@ -606,6 +637,7 @@ } out.append("; }"); i += 2; + addReference(in); return i; } private int invokeVirtualMethod(byte[] byteCodes, int i) @@ -639,6 +671,12 @@ i += 2; return i; } + + private void addReference(String cn) { + if (references != null) { + references.add(cn); + } + } private void outType(final String d, StringBuilder out) { if (d.charAt(0) == 'L') { diff -r cb0cfba1b863 -r 361b76189f8d src/test/java/org/apidesign/java4browser/InstanceTest.java --- a/src/test/java/org/apidesign/java4browser/InstanceTest.java Thu Sep 20 10:39:54 2012 +0200 +++ b/src/test/java/org/apidesign/java4browser/InstanceTest.java Thu Sep 20 14:20:01 2012 +0200 @@ -76,7 +76,10 @@ private static void assertExec(String msg, String methodName, Object expRes, Object... args) throws Exception { StringBuilder sb = new StringBuilder(); - Invocable i = StaticMethodTest.compileClass(sb, "Instance.class", "InstanceSub.class"); + Invocable i = StaticMethodTest.compileClass(sb, + "org/apidesign/java4browser/Instance", + "org/apidesign/java4browser/InstanceSub" + ); Object ret = null; try { diff -r cb0cfba1b863 -r 361b76189f8d src/test/java/org/apidesign/java4browser/StaticMethodTest.java --- a/src/test/java/org/apidesign/java4browser/StaticMethodTest.java Thu Sep 20 10:39:54 2012 +0200 +++ b/src/test/java/org/apidesign/java4browser/StaticMethodTest.java Thu Sep 20 14:20:01 2012 +0200 @@ -19,6 +19,10 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; @@ -131,7 +135,7 @@ private static void assertExec(String msg, String methodName, Object expRes, Object... args) throws Exception { StringBuilder sb = new StringBuilder(); - Invocable i = compileClass(sb, "StaticMethod.class"); + Invocable i = compileClass(sb, "org/apidesign/java4browser/StaticMethod"); Object ret = null; try { @@ -153,12 +157,12 @@ static Invocable compileClass(StringBuilder sb, String... names) throws ScriptException, IOException { for (String name : names) { - InputStream is = StaticMethodTest.class.getResourceAsStream(name); + InputStream is = StaticMethodTest.class.getClassLoader().getResourceAsStream(name + ".class"); assertNotNull(is, "Class file found"); if (sb == null) { sb = new StringBuilder(); } - ByteCodeToJavaScript.compile(name, is, sb); + ByteCodeToJavaScript.compile(is, sb, null); } ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine js = sem.getEngineByExtension("js");