The compilation to JavaScript now identifies list of external references and provides them back to the caller
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 20 Sep 2012 14:20:01 +0200
changeset 18361b76189f8d
parent 17 cb0cfba1b863
child 19 2291e553464a
The compilation to JavaScript now identifies list of external references and provides them back to the caller
src/main/java/org/apidesign/java4browser/ByteCodeToJavaScript.java
src/test/java/org/apidesign/java4browser/InstanceTest.java
src/test/java/org/apidesign/java4browser/StaticMethodTest.java
     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");