Eliminating references to java.util classes that have too huge transitive closure. javap
authorJaroslav Tulach <jtulach@netbeans.org>
Fri, 16 Nov 2012 07:36:32 +0100
branchjavap
changeset 16270e7710a65dc
parent 161 978301fdb4ec
child 164 eb7c3802b3b7
Eliminating references to java.util classes that have too huge transitive closure.
vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java
vm/src/main/java/org/apidesign/vm4brwsr/GenJS.java
vm/src/main/java/org/apidesign/vm4brwsr/Main.java
vm/src/main/java/org/apidesign/vm4brwsr/StringArray.java
     1.1 --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Thu Nov 15 08:12:52 2012 +0100
     1.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Fri Nov 16 07:36:32 2012 +0100
     1.3 @@ -19,9 +19,6 @@
     1.4  
     1.5  import java.io.IOException;
     1.6  import java.io.InputStream;
     1.7 -import java.util.ArrayList;
     1.8 -import java.util.Collection;
     1.9 -import java.util.List;
    1.10  import org.apidesign.bck2brwsr.core.ExtraJavaScript;
    1.11  import org.apidesign.bck2brwsr.core.JavaScriptBody;
    1.12  import sun.tools.javap.AnnotationParser;
    1.13 @@ -34,63 +31,57 @@
    1.14   *
    1.15   * @author Jaroslav Tulach <jtulach@netbeans.org>
    1.16   */
    1.17 -public final class ByteCodeToJavaScript {
    1.18 -    private final ClassData jc;
    1.19 +public abstract class ByteCodeToJavaScript {
    1.20 +    private ClassData jc;
    1.21      private final Appendable out;
    1.22 -    private final Collection<? super String> references;
    1.23  
    1.24 -    private ByteCodeToJavaScript(
    1.25 -        ClassData jc, Appendable out, Collection<? super String> references
    1.26 -    ) {
    1.27 -        this.jc = jc;
    1.28 +    protected ByteCodeToJavaScript(Appendable out) {
    1.29          this.out = out;
    1.30 -        this.references = references;
    1.31      }
    1.32 +    
    1.33 +    /* Collects additional required resources.
    1.34 +     * 
    1.35 +     * @param internalClassName 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>. 
    1.38 +     */
    1.39 +    protected abstract boolean requireReference(String internalClassName);
    1.40 +    
    1.41 +    /*
    1.42 +     * @param resourcePath name of resources to read
    1.43 +     */
    1.44 +    protected abstract void requireScript(String resourcePath);
    1.45  
    1.46      /**
    1.47       * Converts a given class file to a JavaScript version.
    1.48       *
    1.49       * @param classFile input stream with code of the .class file
    1.50 -     * @param out a {@link StringBuilder} or similar to generate the output to
    1.51 -     * @param references a write only collection where the system adds list of
    1.52 -     *   other classes that were referenced and should be loaded in order the
    1.53 -     *   generated JavaScript code works properly. The names are in internal 
    1.54 -     *   JVM form so String is <code>java/lang/String</code>. Can be <code>null</code>
    1.55 -     *   if one is not interested in knowing references
    1.56 -     * @param scripts write only collection with names of resources to read
    1.57       * @return the initialization code for this class, if any. Otherwise <code>null</code>
    1.58       * 
    1.59       * @throws IOException if something goes wrong during read or write or translating
    1.60       */
    1.61      
    1.62 -    public static String compile(
    1.63 -        InputStream classFile, Appendable out,
    1.64 -        Collection<? super String> references,
    1.65 -        Collection<? super String> scripts
    1.66 -    ) throws IOException {
    1.67 -        ClassData jc = new ClassData(classFile);
    1.68 +    public String compile(InputStream classFile) throws IOException {
    1.69 +        this.jc = new ClassData(classFile);
    1.70          byte[] arrData = jc.findAnnotationData(true);
    1.71          String[] arr = findAnnotation(arrData, jc, ExtraJavaScript.class.getName(), "resource", "processByteCode");
    1.72          if (arr != null) {
    1.73 -            scripts.add(arr[0]);
    1.74 +            requireScript(arr[0]);
    1.75              if ("0".equals(arr[1])) {
    1.76                  return null;
    1.77              }
    1.78          }
    1.79 -        ByteCodeToJavaScript compiler = new ByteCodeToJavaScript(
    1.80 -            jc, out, references
    1.81 -        );
    1.82 -        List<String> toInitilize = new ArrayList<String>();
    1.83 +        StringArray toInitilize = new StringArray();
    1.84          for (MethodData m : jc.getMethods()) {
    1.85              if (m.isStatic()) {
    1.86 -                compiler.generateStaticMethod(m, toInitilize);
    1.87 +                generateStaticMethod(m, toInitilize);
    1.88              } else {
    1.89 -                compiler.generateInstanceMethod(m);
    1.90 +                generateInstanceMethod(m);
    1.91              }
    1.92          }
    1.93          for (FieldData v : jc.getFields()) {
    1.94              if (v.isStatic()) {
    1.95 -                compiler.generateStaticField(v);
    1.96 +                generateStaticField(v);
    1.97              }
    1.98          }
    1.99          
   1.100 @@ -119,7 +110,7 @@
   1.101          }
   1.102          for (MethodData m : jc.getMethods()) {
   1.103              if (!m.getName().contains("<init>") && !m.getName().contains("<cinit>")) {
   1.104 -                compiler.generateMethodReference("\n  p.", m);
   1.105 +                generateMethodReference("\n  p.", m);
   1.106              }
   1.107          }
   1.108          out.append("\n  p.$instOf_").append(className).append(" = true;");
   1.109 @@ -130,12 +121,12 @@
   1.110          out.append("\n}");
   1.111          out.append("\n").append(className).append("_proto();");
   1.112          StringBuilder sb = new StringBuilder();
   1.113 -        for (String init : toInitilize) {
   1.114 +        for (String init : toInitilize.toArray()) {
   1.115              sb.append("\n").append(init).append("();");
   1.116          }
   1.117          return sb.toString();
   1.118      }
   1.119 -    private void generateStaticMethod(MethodData m, List<String> toInitilize) throws IOException {
   1.120 +    private void generateStaticMethod(MethodData m, StringArray toInitilize) throws IOException {
   1.121          if (javaScriptBody(m, true)) {
   1.122              return;
   1.123          }
   1.124 @@ -952,10 +943,8 @@
   1.125      }
   1.126      
   1.127      private void addReference(String cn) throws IOException {
   1.128 -        if (references != null) {
   1.129 -            if (references.add(cn)) {
   1.130 -                out.append(" /* needs ").append(cn).append(" */");
   1.131 -            }
   1.132 +        if (requireReference(cn)) {
   1.133 +            out.append(" /* needs ").append(cn).append(" */");
   1.134          }
   1.135      }
   1.136  
     2.1 --- a/vm/src/main/java/org/apidesign/vm4brwsr/GenJS.java	Thu Nov 15 08:12:52 2012 +0100
     2.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/GenJS.java	Fri Nov 16 07:36:32 2012 +0100
     2.3 @@ -20,49 +20,33 @@
     2.4  import java.io.IOException;
     2.5  import java.io.InputStream;
     2.6  import java.net.URL;
     2.7 -import java.util.ArrayList;
     2.8 -import java.util.Arrays;
     2.9 -import java.util.Collections;
    2.10  import java.util.Enumeration;
    2.11 -import java.util.HashMap;
    2.12 -import java.util.Iterator;
    2.13 -import java.util.LinkedHashSet;
    2.14 -import java.util.LinkedList;
    2.15 -import java.util.List;
    2.16 -import java.util.Map;
    2.17  
    2.18  /** Generator of JavaScript from bytecode of classes on classpath of the VM.
    2.19   *
    2.20   * @author Jaroslav Tulach <jtulach@netbeans.org>
    2.21   */
    2.22 -final class GenJS {
    2.23 -    private GenJS() {}
    2.24 +final class GenJS extends ByteCodeToJavaScript {
    2.25 +    public GenJS(Appendable out) {
    2.26 +        super(out);
    2.27 +    }
    2.28      
    2.29      static void compile(Appendable out, String... names) throws IOException {
    2.30 -        compile(out, Arrays.asList(names));
    2.31 +        compile(out, StringArray.asList(names));
    2.32      }
    2.33 -    static void compile(Appendable out, List<String> names) throws IOException {
    2.34 +    static void compile(Appendable out, StringArray names) throws IOException {
    2.35          compile(GenJS.class.getClassLoader(), out, names);
    2.36      }
    2.37 -    static void compile(ClassLoader l, Appendable out, List<String> names) throws IOException {
    2.38 -        final Map<String,String> processed = new HashMap<String, String>();
    2.39 -        for (String baseClass : names) {
    2.40 -            LinkedHashSet<String> toProcess = new LinkedHashSet<String>() {
    2.41 -                @Override
    2.42 -                public boolean add(String e) {
    2.43 -                    if (processed.containsKey(e)) {
    2.44 -                        return false;
    2.45 -                    }
    2.46 -                    return super.add(e);
    2.47 -                }
    2.48 -            };
    2.49 -            toProcess.add(baseClass);
    2.50 +    static void compile(ClassLoader l, Appendable out, StringArray names) throws IOException {
    2.51 +        StringArray processed = new StringArray();
    2.52 +        StringArray initCode = new StringArray();
    2.53 +        for (String baseClass : names.toArray()) {
    2.54 +            GenJS js = new GenJS(out);
    2.55 +            js.references.add(baseClass);
    2.56              for (;;) {
    2.57                  String name = null;
    2.58 -                Iterator<String> it = toProcess.iterator();
    2.59 -                while (it.hasNext() && name == null) {
    2.60 -                    String n = it.next();
    2.61 -                    if (processed.get(n) != null) {
    2.62 +                for (String n : js.references.toArray()) {
    2.63 +                    if (processed.contains(n)) {
    2.64                          continue;
    2.65                      }
    2.66                      name = n;
    2.67 @@ -70,18 +54,14 @@
    2.68                  if (name == null) {
    2.69                      break;
    2.70                  }
    2.71 -                if (name.startsWith("sun/")) {
    2.72 -                    processed.put(name, "");
    2.73 -                    continue;
    2.74 -                }            
    2.75                  InputStream is = loadClass(l, name);
    2.76                  if (is == null) {
    2.77                      throw new IOException("Can't find class " + name); 
    2.78                  }
    2.79 -                LinkedList<String> scripts = new LinkedList<String>();
    2.80                  try {
    2.81 -                    String initCode = ByteCodeToJavaScript.compile(is, out, toProcess, scripts);
    2.82 -                    processed.put(name, initCode == null ? "" : initCode);
    2.83 +                    String ic = js.compile(is);
    2.84 +                    processed.add(name);
    2.85 +                    initCode.add(ic == null ? "" : ic);
    2.86                  } catch (RuntimeException ex) {
    2.87                      if (out instanceof CharSequence) {
    2.88                          CharSequence seq = (CharSequence)out;
    2.89 @@ -100,7 +80,7 @@
    2.90                          );
    2.91                      }
    2.92                  }
    2.93 -                for (String resource : scripts) {
    2.94 +                for (String resource : js.scripts.toArray()) {
    2.95                      while (resource.startsWith("/")) {
    2.96                          resource = resource.substring(1);
    2.97                      }
    2.98 @@ -112,14 +92,14 @@
    2.99                  }
   2.100              }
   2.101  
   2.102 -            List<String> toInit = new ArrayList<String>(toProcess);
   2.103 -            Collections.reverse(toInit);
   2.104 +            StringArray toInit = StringArray.asList(js.references.toArray());
   2.105 +            toInit.reverse();
   2.106  
   2.107 -            for (String clazz : toInit) {
   2.108 -                String initCode = processed.get(clazz);
   2.109 -                if (initCode != null && !initCode.isEmpty()) {
   2.110 -                    out.append(initCode).append("\n");
   2.111 -                    processed.put(clazz, "");
   2.112 +            for (String ic : toInit.toArray()) {
   2.113 +                int indx = processed.indexOf(ic);
   2.114 +                if (indx >= 0) {
   2.115 +                    out.append(initCode.toArray()[indx]).append("\n");
   2.116 +                    initCode.toArray()[indx] = "";
   2.117                  }
   2.118              }
   2.119  
   2.120 @@ -180,6 +160,9 @@
   2.121          if (u == null) {
   2.122              throw new IOException("Can't find " + name);
   2.123          }
   2.124 +        if (u.toExternalForm().contains("rt.jar!")) {
   2.125 +            throw new IOException("No emulation for " + u);
   2.126 +        }
   2.127          return u.openStream();
   2.128      }
   2.129  
   2.130 @@ -188,4 +171,21 @@
   2.131          compile(sb, name);
   2.132          return sb.toString().toString();
   2.133      }
   2.134 +
   2.135 +    private StringArray scripts = new StringArray();
   2.136 +    private StringArray references = new StringArray();
   2.137 +    
   2.138 +    @Override
   2.139 +    protected boolean requireReference(String cn) {
   2.140 +        if (references.contains(cn)) {
   2.141 +            return false;
   2.142 +        }
   2.143 +        references.add(cn);
   2.144 +        return true;
   2.145 +    }
   2.146 +
   2.147 +    @Override
   2.148 +    protected void requireScript(String resourcePath) {
   2.149 +        scripts.add(resourcePath);
   2.150 +    }
   2.151  }
     3.1 --- a/vm/src/main/java/org/apidesign/vm4brwsr/Main.java	Thu Nov 15 08:12:52 2012 +0100
     3.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/Main.java	Fri Nov 16 07:36:32 2012 +0100
     3.3 @@ -21,8 +21,6 @@
     3.4  import java.io.FileWriter;
     3.5  import java.io.IOException;
     3.6  import java.io.Writer;
     3.7 -import java.util.Arrays;
     3.8 -import java.util.List;
     3.9  
    3.10  /** Generator of JavaScript from bytecode of classes on classpath of the VM
    3.11   * with a Main method.
    3.12 @@ -39,7 +37,8 @@
    3.13          }
    3.14          
    3.15          Writer w = new BufferedWriter(new FileWriter(args[0]));
    3.16 -        List<String> classes = Arrays.asList(args).subList(1, args.length);
    3.17 +        StringArray classes = StringArray.asList(args);
    3.18 +        classes.delete(0);
    3.19          GenJS.compile(w, classes);
    3.20          w.close();
    3.21      }
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/StringArray.java	Fri Nov 16 07:36:32 2012 +0100
     4.3 @@ -0,0 +1,84 @@
     4.4 +/*
     4.5 + * To change this template, choose Tools | Templates
     4.6 + * and open the template in the editor.
     4.7 + */
     4.8 +package org.apidesign.vm4brwsr;
     4.9 +
    4.10 +/**
    4.11 + *
    4.12 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    4.13 + */
    4.14 +class StringArray {
    4.15 +    private String[] arr;
    4.16 +
    4.17 +    public StringArray() {
    4.18 +    }
    4.19 +
    4.20 +    private StringArray(String[] arr) {
    4.21 +        this.arr = arr;
    4.22 +    }
    4.23 +    
    4.24 +    public void add(String s) {
    4.25 +        if (arr == null) {
    4.26 +            arr = new String[1];
    4.27 +        } else {
    4.28 +            String[] tmp = new String[arr.length + 1];
    4.29 +            for (int i = 0; i < arr.length; i++) {
    4.30 +                tmp[i] = arr[i];
    4.31 +            }
    4.32 +            arr = tmp;
    4.33 +        }
    4.34 +        arr[arr.length - 1] = s;
    4.35 +    }
    4.36 +    
    4.37 +    public String[] toArray() {
    4.38 +        return arr == null ? new String[0] : arr;
    4.39 +    }
    4.40 +    
    4.41 +    static StringArray asList(String[] names) {
    4.42 +        return new StringArray(names);
    4.43 +    }
    4.44 +
    4.45 +    void reverse() {
    4.46 +        for (int i = 0, j = arr.length; i < j; i++) {
    4.47 +            String s = arr[i];
    4.48 +            arr[i] = arr[--j];
    4.49 +            arr[j] = s;
    4.50 +        }
    4.51 +    }
    4.52 +
    4.53 +    boolean contains(String n) {
    4.54 +        if (arr == null) {
    4.55 +            return false;
    4.56 +        }
    4.57 +        for (int i = 0; i < arr.length; i++) {
    4.58 +            if (n.equals(arr[i])) {
    4.59 +                return true;
    4.60 +            }
    4.61 +        }
    4.62 +        return false;
    4.63 +    }
    4.64 +
    4.65 +    void delete(int indx) {
    4.66 +        if (arr == null) {
    4.67 +            return;
    4.68 +        }
    4.69 +        String[] tmp = new String[arr.length - 1];
    4.70 +        for (int i = 0, j = 0; i < arr.length; i++) {
    4.71 +            tmp[j] = arr[i];
    4.72 +            if (j == indx) {
    4.73 +                continue;
    4.74 +            }
    4.75 +        }
    4.76 +    }
    4.77 +
    4.78 +    int indexOf(String ic) {
    4.79 +        for (int i = 0; i < arr.length; i++) {
    4.80 +            if (ic.equals(arr[i])) {
    4.81 +                return i;
    4.82 +            }
    4.83 +        }
    4.84 +        return -1;
    4.85 +    }
    4.86 +    
    4.87 +}