The compilation to JavaScript now identifies list of external references and provides them back to the caller
1.1 --- a/src/main/java/org/apidesign/java4browser/ByteCodeToJavaScript.java Thu Sep 20 10:39:54 2012 +0200
1.2 +++ b/src/main/java/org/apidesign/java4browser/ByteCodeToJavaScript.java Thu Sep 20 14:20:01 2012 +0200
1.3 @@ -19,6 +19,7 @@
1.4
1.5 import java.io.IOException;
1.6 import java.io.InputStream;
1.7 +import java.util.Collection;
1.8 import java.util.List;
1.9 import static org.netbeans.modules.classfile.ByteCodes.*;
1.10 import org.netbeans.modules.classfile.CPClassInfo;
1.11 @@ -39,21 +40,37 @@
1.12 public final class ByteCodeToJavaScript {
1.13 private final ClassFile jc;
1.14 private final Appendable out;
1.15 + private final Collection<? super String> references;
1.16
1.17 - private ByteCodeToJavaScript(ClassFile jc, Appendable out) {
1.18 + private ByteCodeToJavaScript(
1.19 + ClassFile jc, Appendable out, Collection<? super String> references
1.20 + ) {
1.21 this.jc = jc;
1.22 this.out = out;
1.23 + this.references = references;
1.24 }
1.25 -
1.26 - /** Converts a given class file to a JavaScript version.
1.27 - * @param fileName the name of the file we are reading
1.28 +
1.29 + /**
1.30 + * Converts a given class file to a JavaScript version.
1.31 + *
1.32 * @param classFile input stream with code of the .class file
1.33 * @param out a {@link StringBuilder} or similar to generate the output to
1.34 + * @param references a write only collection where the system adds list of
1.35 + * other classes that were referenced and should be loaded in order the
1.36 + * generated JavaScript code works properly. The names are in internal
1.37 + * JVM form so String is <code>java/lang/String</code>. Can be <code>null</code>
1.38 + * if one is not interested in knowing references
1.39 * @throws IOException if something goes wrong during read or write or translating
1.40 */
1.41 - public static void compile(String fileName, InputStream classFile, Appendable out) throws IOException {
1.42 +
1.43 + public static void compile(
1.44 + InputStream classFile, Appendable out,
1.45 + Collection<? super String> references
1.46 + ) throws IOException {
1.47 ClassFile jc = new ClassFile(classFile, true);
1.48 - ByteCodeToJavaScript compiler = new ByteCodeToJavaScript(jc, out);
1.49 + ByteCodeToJavaScript compiler = new ByteCodeToJavaScript(
1.50 + jc, out, references
1.51 + );
1.52 for (Method m : jc.getMethods()) {
1.53 if (m.isStatic()) {
1.54 compiler.generateStaticMethod(m);
1.55 @@ -69,7 +86,7 @@
1.56 out.append("function java_lang_Object(){}\n"); // XXX temporary
1.57 out.append("function java_lang_Object_consV(self){}\n"); // XXX temporary
1.58
1.59 - final String className = jc.getName().getExternalName().replace('.', '_');
1.60 + final String className = jc.getName().getInternalName().replace('/', '_');
1.61 out.append("\nfunction ").append(className);
1.62 out.append("() {");
1.63 for (Method m : jc.getMethods()) {
1.64 @@ -87,12 +104,12 @@
1.65 ClassName sc = jc.getSuperClass();
1.66 if (sc != null) {
1.67 out.append("\n ").append(className)
1.68 - .append(".prototype = new ").append(sc.getExternalName().replace('.', '_'));
1.69 + .append(".prototype = new ").append(sc.getInternalName().replace('/', '_'));
1.70 }
1.71 }
1.72 private void generateStaticMethod(Method m) throws IOException {
1.73 out.append("\nfunction ").append(
1.74 - jc.getName().getExternalName().replace('.', '_')
1.75 + jc.getName().getInternalName().replace('/', '_')
1.76 ).append('_').append(findMethodName(m));
1.77 out.append('(');
1.78 String space = "";
1.79 @@ -110,28 +127,32 @@
1.80 }
1.81 out.append(") {").append("\n");
1.82 final Code code = m.getCode();
1.83 - int len = code.getMaxLocals();
1.84 - for (int index = args.size(), i = args.size(); i < len; i++) {
1.85 - out.append(" var ");
1.86 - out.append("arg").append(String.valueOf(i)).append(";\n");
1.87 + if (code != null) {
1.88 + int len = code.getMaxLocals();
1.89 + for (int index = args.size(), i = args.size(); i < len; i++) {
1.90 + out.append(" var ");
1.91 + out.append("arg").append(String.valueOf(i)).append(";\n");
1.92 + }
1.93 + out.append(" var stack = new Array(");
1.94 + out.append(Integer.toString(code.getMaxStack()));
1.95 + out.append(");\n");
1.96 + produceCode(code.getByteCodes());
1.97 + } else {
1.98 + out.append(" /* no code found for ").append(m.getTypeSignature()).append(" */\n");
1.99 }
1.100 - out.append(" var stack = new Array(");
1.101 - out.append(Integer.toString(code.getMaxStack()));
1.102 - out.append(");\n");
1.103 - produceCode(code.getByteCodes());
1.104 out.append("}");
1.105 }
1.106
1.107 private void generateMethodReference(Method m) throws IOException {
1.108 final String name = findMethodName(m);
1.109 out.append("\n this.").append(name).append(" = ")
1.110 - .append(jc.getName().getExternalName().replace('.', '_'))
1.111 + .append(jc.getName().getInternalName().replace('/', '_'))
1.112 .append('_').append(name).append(";");
1.113 }
1.114
1.115 private void generateInstanceMethod(Method m) throws IOException {
1.116 out.append("\nfunction ").append(
1.117 - jc.getName().getExternalName().replace('.', '_')
1.118 + jc.getName().getInternalName().replace('/', '_')
1.119 ).append('_').append(findMethodName(m));
1.120 out.append("(arg0");
1.121 String space = ",";
1.122 @@ -148,15 +169,19 @@
1.123 }
1.124 out.append(") {").append("\n");
1.125 final Code code = m.getCode();
1.126 - int len = code.getMaxLocals();
1.127 - for (int index = args.size(), i = args.size(); i < len; i++) {
1.128 - out.append(" var ");
1.129 - out.append("arg").append(String.valueOf(i + 1)).append(";\n");
1.130 + if (code != null) {
1.131 + int len = code.getMaxLocals();
1.132 + for (int index = args.size(), i = args.size(); i < len; i++) {
1.133 + out.append(" var ");
1.134 + out.append("arg").append(String.valueOf(i + 1)).append(";\n");
1.135 + }
1.136 + out.append(";\n var stack = new Array(");
1.137 + out.append(Integer.toString(code.getMaxStack()));
1.138 + out.append(");\n");
1.139 + produceCode(code.getByteCodes());
1.140 + } else {
1.141 + out.append(" /* no code found for ").append(m.getTypeSignature()).append(" */\n");
1.142 }
1.143 - out.append(";\n var stack = new Array(");
1.144 - out.append(Integer.toString(code.getMaxStack()));
1.145 - out.append(");\n");
1.146 - produceCode(code.getByteCodes());
1.147 out.append("}");
1.148 }
1.149
1.150 @@ -405,8 +430,9 @@
1.151 int indx = readIntArg(byteCodes, i);
1.152 CPClassInfo ci = jc.getConstantPool().getClass(indx);
1.153 out.append("stack.push(");
1.154 - out.append("new ").append(ci.getClassName().getExternalName().replace('.','_'));
1.155 + out.append("new ").append(ci.getClassName().getInternalName().replace('/','_'));
1.156 out.append(");");
1.157 + addReference(ci.getClassName().getInternalName());
1.158 i += 2;
1.159 break;
1.160 }
1.161 @@ -426,17 +452,21 @@
1.162 case bc_getstatic: {
1.163 int indx = readIntArg(byteCodes, i);
1.164 CPFieldInfo fi = (CPFieldInfo) jc.getConstantPool().get(indx);
1.165 - out.append("stack.push(").append(fi.getClassName().getExternalName().replace('.', '_'));
1.166 + final String in = fi.getClassName().getInternalName();
1.167 + out.append("stack.push(").append(in.replace('/', '_'));
1.168 out.append('_').append(fi.getFieldName()).append(");");
1.169 i += 2;
1.170 + addReference(in);
1.171 break;
1.172 }
1.173 case bc_putstatic: {
1.174 int indx = readIntArg(byteCodes, i);
1.175 CPFieldInfo fi = (CPFieldInfo) jc.getConstantPool().get(indx);
1.176 - out.append(fi.getClassName().getExternalName().replace('.', '_'));
1.177 + final String in = fi.getClassName().getInternalName();
1.178 + out.append(in.replace('/', '_'));
1.179 out.append('_').append(fi.getFieldName()).append(" = stack.pop();");
1.180 i += 2;
1.181 + addReference(in);
1.182 break;
1.183 }
1.184 case bc_putfield: {
1.185 @@ -451,7 +481,7 @@
1.186 int indx = readIntArg(byteCodes, i);
1.187 CPClassInfo ci = jc.getConstantPool().getClass(indx);
1.188 out.append("stack.push(stack.pop().$instOf_")
1.189 - .append(ci.getClassName().getExternalName().replace('.', '_'))
1.190 + .append(ci.getClassName().getInternalName().replace('/', '_'))
1.191 .append(" ? 1 : 0);");
1.192 i += 2;
1.193 }
1.194 @@ -541,7 +571,7 @@
1.195
1.196 private void generateStaticField(Variable v) throws IOException {
1.197 out.append("\nvar ")
1.198 - .append(jc.getName().getExternalName().replace('.', '_'))
1.199 + .append(jc.getName().getInternalName().replace('/', '_'))
1.200 .append('_').append(v.getName()).append(" = 0;");
1.201 }
1.202
1.203 @@ -586,7 +616,8 @@
1.204 if (hasReturn[0]) {
1.205 out.append("stack.push(");
1.206 }
1.207 - out.append(mi.getClassName().getInternalName().replace('/', '_'));
1.208 + final String in = mi.getClassName().getInternalName();
1.209 + out.append(in.replace('/', '_'));
1.210 out.append('_');
1.211 out.append(mn);
1.212 out.append('(');
1.213 @@ -606,6 +637,7 @@
1.214 }
1.215 out.append("; }");
1.216 i += 2;
1.217 + addReference(in);
1.218 return i;
1.219 }
1.220 private int invokeVirtualMethod(byte[] byteCodes, int i)
1.221 @@ -639,6 +671,12 @@
1.222 i += 2;
1.223 return i;
1.224 }
1.225 +
1.226 + private void addReference(String cn) {
1.227 + if (references != null) {
1.228 + references.add(cn);
1.229 + }
1.230 + }
1.231
1.232 private void outType(final String d, StringBuilder out) {
1.233 if (d.charAt(0) == 'L') {
2.1 --- a/src/test/java/org/apidesign/java4browser/InstanceTest.java Thu Sep 20 10:39:54 2012 +0200
2.2 +++ b/src/test/java/org/apidesign/java4browser/InstanceTest.java Thu Sep 20 14:20:01 2012 +0200
2.3 @@ -76,7 +76,10 @@
2.4
2.5 private static void assertExec(String msg, String methodName, Object expRes, Object... args) throws Exception {
2.6 StringBuilder sb = new StringBuilder();
2.7 - Invocable i = StaticMethodTest.compileClass(sb, "Instance.class", "InstanceSub.class");
2.8 + Invocable i = StaticMethodTest.compileClass(sb,
2.9 + "org/apidesign/java4browser/Instance",
2.10 + "org/apidesign/java4browser/InstanceSub"
2.11 + );
2.12
2.13 Object ret = null;
2.14 try {
3.1 --- a/src/test/java/org/apidesign/java4browser/StaticMethodTest.java Thu Sep 20 10:39:54 2012 +0200
3.2 +++ b/src/test/java/org/apidesign/java4browser/StaticMethodTest.java Thu Sep 20 14:20:01 2012 +0200
3.3 @@ -19,6 +19,10 @@
3.4
3.5 import java.io.IOException;
3.6 import java.io.InputStream;
3.7 +import java.util.Arrays;
3.8 +import java.util.Iterator;
3.9 +import java.util.Set;
3.10 +import java.util.TreeSet;
3.11 import javax.script.Invocable;
3.12 import javax.script.ScriptEngine;
3.13 import javax.script.ScriptEngineManager;
3.14 @@ -131,7 +135,7 @@
3.15
3.16 private static void assertExec(String msg, String methodName, Object expRes, Object... args) throws Exception {
3.17 StringBuilder sb = new StringBuilder();
3.18 - Invocable i = compileClass(sb, "StaticMethod.class");
3.19 + Invocable i = compileClass(sb, "org/apidesign/java4browser/StaticMethod");
3.20
3.21 Object ret = null;
3.22 try {
3.23 @@ -153,12 +157,12 @@
3.24
3.25 static Invocable compileClass(StringBuilder sb, String... names) throws ScriptException, IOException {
3.26 for (String name : names) {
3.27 - InputStream is = StaticMethodTest.class.getResourceAsStream(name);
3.28 + InputStream is = StaticMethodTest.class.getClassLoader().getResourceAsStream(name + ".class");
3.29 assertNotNull(is, "Class file found");
3.30 if (sb == null) {
3.31 sb = new StringBuilder();
3.32 }
3.33 - ByteCodeToJavaScript.compile(name, is, sb);
3.34 + ByteCodeToJavaScript.compile(is, sb, null);
3.35 }
3.36 ScriptEngineManager sem = new ScriptEngineManager();
3.37 ScriptEngine js = sem.getEngineByExtension("js");