Eliminating references to java.util classes that have too huge transitive closure.
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 +}