Can call RetroLambda processor in JAR files. jdk8
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 13 Sep 2014 13:44:01 +0200
branchjdk8
changeset 167835daab73e225
parent 1675 cd50c1894ce5
child 1679 93f4fbc4d1b7
Can call RetroLambda processor in JAR files.
rt/aot/pom.xml
rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/Bck2BrwsrJars.java
rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/BytecodeProcessor.java
rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/RetroLambda.java
     1.1 --- a/rt/aot/pom.xml	Sun Aug 17 20:15:56 2014 +0200
     1.2 +++ b/rt/aot/pom.xml	Sat Sep 13 13:44:01 2014 +0200
     1.3 @@ -27,5 +27,11 @@
     1.4              <version>${project.version}</version>
     1.5              <type>jar</type>
     1.6          </dependency>
     1.7 +        <dependency>
     1.8 +            <groupId>net.orfjackal.retrolambda</groupId>
     1.9 +            <artifactId>retrolambda</artifactId>
    1.10 +            <version>1.6.1</version>
    1.11 +            <type>jar</type>
    1.12 +        </dependency>
    1.13      </dependencies>
    1.14  </project>
     2.1 --- a/rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/Bck2BrwsrJars.java	Sun Aug 17 20:15:56 2014 +0200
     2.2 +++ b/rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/Bck2BrwsrJars.java	Sat Sep 13 13:44:01 2014 +0200
     2.3 @@ -25,8 +25,10 @@
     2.4  import java.net.URL;
     2.5  import java.util.ArrayList;
     2.6  import java.util.Enumeration;
     2.7 +import java.util.HashMap;
     2.8  import java.util.HashSet;
     2.9  import java.util.List;
    2.10 +import java.util.Map;
    2.11  import java.util.Set;
    2.12  import java.util.jar.JarEntry;
    2.13  import java.util.jar.JarFile;
    2.14 @@ -65,23 +67,26 @@
    2.15       */
    2.16      public static Bck2Brwsr configureFrom(Bck2Brwsr c, File jar) throws IOException {
    2.17          final JarFile jf = new JarFile(jar);
    2.18 -        List<String> classes = new ArrayList<>();
    2.19 +        final List<String> classes = new ArrayList<>();
    2.20          List<String> resources = new ArrayList<>();
    2.21          Set<String> exported = new HashSet<>();
    2.22 -
    2.23 -        listJAR(jf, classes, resources, exported);
    2.24 -        
    2.25 -        String cp = jf.getManifest().getMainAttributes().getValue("Class-Path"); // NOI18N
    2.26 -        String[] classpath = cp == null ? new String[0] : cp.split(" ");
    2.27 -
    2.28          class JarRes extends EmulationResources implements Bck2Brwsr.Resources {
    2.29 -
    2.30 +            JarRes() {
    2.31 +                super(classes);
    2.32 +            }
    2.33              @Override
    2.34              public InputStream get(String resource) throws IOException {
    2.35                  InputStream is = jf.getInputStream(new ZipEntry(resource));
    2.36                  return is == null ? super.get(resource) : is;
    2.37              }
    2.38          }
    2.39 +        JarRes jarRes = new JarRes();
    2.40 +
    2.41 +        listJAR(jf, jarRes, resources, exported);
    2.42 +        
    2.43 +        String cp = jf.getManifest().getMainAttributes().getValue("Class-Path"); // NOI18N
    2.44 +        String[] classpath = cp == null ? new String[0] : cp.split(" ");
    2.45 +
    2.46          if (c == null) {
    2.47              c = Bck2Brwsr.newCompiler();
    2.48          }
    2.49 @@ -91,11 +96,11 @@
    2.50              .addClasses(classes.toArray(new String[classes.size()]))
    2.51              .addExported(exported.toArray(new String[exported.size()]))
    2.52              .addResources(resources.toArray(new String[resources.size()]))
    2.53 -            .resources(new JarRes());
    2.54 +            .resources(jarRes);
    2.55      }
    2.56      
    2.57      private static void listJAR(
    2.58 -        JarFile j, List<String> classes,
    2.59 +        JarFile j, EmulationResources classes,
    2.60          List<String> resources, Set<String> keep
    2.61      ) throws IOException {
    2.62          Enumeration<JarEntry> en = j.entries();
    2.63 @@ -114,7 +119,7 @@
    2.64                  keep.add(pkg);
    2.65              }
    2.66              if (n.endsWith(".class")) {
    2.67 -                classes.add(n.substring(0, n.length() - 6));
    2.68 +                classes.addClassResource(n);
    2.69              } else {
    2.70                  resources.add(n);
    2.71                  if (n.startsWith("META-INF/services/") && keep != null) {
    2.72 @@ -142,8 +147,51 @@
    2.73              }
    2.74          }
    2.75      }
    2.76 +    
    2.77 +    static byte[] readFrom(InputStream is) throws IOException {
    2.78 +        int expLen = is.available();
    2.79 +        if (expLen < 1) {
    2.80 +            expLen = 1;
    2.81 +        }
    2.82 +        byte[] arr = new byte[expLen];
    2.83 +        int pos = 0;
    2.84 +        for (;;) {
    2.85 +            int read = is.read(arr, pos, arr.length - pos);
    2.86 +            if (read == -1) {
    2.87 +                break;
    2.88 +            }
    2.89 +            pos += read;
    2.90 +            if (pos == arr.length) {
    2.91 +                byte[] tmp = new byte[arr.length * 2];
    2.92 +                System.arraycopy(arr, 0, tmp, 0, arr.length);
    2.93 +                arr = tmp;
    2.94 +            }
    2.95 +        }
    2.96 +        if (pos != arr.length) {
    2.97 +            byte[] tmp = new byte[pos];
    2.98 +            System.arraycopy(arr, 0, tmp, 0, pos);
    2.99 +            arr = tmp;
   2.100 +        }
   2.101 +        return arr;
   2.102 +    }
   2.103 +    
   2.104  
   2.105      static class EmulationResources implements Bck2Brwsr.Resources {
   2.106 +        private final List<String> classes;
   2.107 +        private final Map<String,byte[]> converted = new HashMap<>();
   2.108 +        private final BytecodeProcessor proc;
   2.109 +
   2.110 +        protected EmulationResources(List<String> classes) {
   2.111 +            this.classes = classes;
   2.112 +            BytecodeProcessor p;
   2.113 +            try {
   2.114 +                Class<?> bpClass = Class.forName("org.apidesign.bck2brwsr.aot.RetroLambda");
   2.115 +                p = (BytecodeProcessor) bpClass.newInstance();
   2.116 +            } catch (Throwable t) {
   2.117 +                p = null;
   2.118 +            }
   2.119 +            this.proc = p;
   2.120 +        }
   2.121  
   2.122          @Override
   2.123          public InputStream get(String name) throws IOException {
   2.124 @@ -162,6 +210,23 @@
   2.125              }
   2.126              return u.openStream();
   2.127          }
   2.128 +
   2.129 +        private void addClassResource(String n) throws IOException {
   2.130 +            if (proc != null) {
   2.131 +                try (InputStream is = this.get(n)) {
   2.132 +                    Map<String, byte[]> conv = proc.process(n, readFrom(is), this);
   2.133 +                    if (conv != null) {
   2.134 +                        if (!conv.containsKey(n)) {
   2.135 +                            throw new IOException("Cannot find " + n + " among " + conv);
   2.136 +                        }
   2.137 +                        converted.putAll(conv);
   2.138 +                        return;
   2.139 +                    }
   2.140 +                }
   2.141 +            }
   2.142 +            classes.add(n.substring(0, n.length() - 6));
   2.143 +        }
   2.144      }
   2.145      
   2.146 +    
   2.147  }
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/BytecodeProcessor.java	Sat Sep 13 13:44:01 2014 +0200
     3.3 @@ -0,0 +1,40 @@
     3.4 +/**
     3.5 + * Back 2 Browser Bytecode Translator
     3.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     3.7 + *
     3.8 + * This program is free software: you can redistribute it and/or modify
     3.9 + * it under the terms of the GNU General Public License as published by
    3.10 + * the Free Software Foundation, version 2 of the License.
    3.11 + *
    3.12 + * This program is distributed in the hope that it will be useful,
    3.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    3.15 + * GNU General Public License for more details.
    3.16 + *
    3.17 + * You should have received a copy of the GNU General Public License
    3.18 + * along with this program. Look for COPYING file in the top folder.
    3.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    3.20 + */
    3.21 +package org.apidesign.bck2brwsr.aot;
    3.22 +
    3.23 +import java.io.IOException;
    3.24 +import java.util.Map;
    3.25 +import org.apidesign.vm4brwsr.Bck2Brwsr;
    3.26 +
    3.27 +/** Replace bytecode of a single class with many new bytecodes.
    3.28 + *
    3.29 + * @author Jaroslav Tulach
    3.30 + */
    3.31 +interface BytecodeProcessor {
    3.32 +    /** Does the conversion.
    3.33 +     * 
    3.34 +     * @param className the resource of the class to replace
    3.35 +     * @param byteCode the bytecode of the class
    3.36 +     * @param resources access to other resources in the system
    3.37 +     * @return map of resource to bytecode which must include at least 
    3.38 +     *   one element of name <code>className</code>
    3.39 +     * @throws IOException 
    3.40 +     */
    3.41 +    public Map<String,byte[]> process(String className, byte[] byteCode, Bck2Brwsr.Resources resources)
    3.42 +    throws IOException;
    3.43 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/RetroLambda.java	Sat Sep 13 13:44:01 2014 +0200
     4.3 @@ -0,0 +1,121 @@
     4.4 +/**
     4.5 + * Back 2 Browser Bytecode Translator
     4.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4.7 + *
     4.8 + * This program is free software: you can redistribute it and/or modify
     4.9 + * it under the terms of the GNU General Public License as published by
    4.10 + * the Free Software Foundation, version 2 of the License.
    4.11 + *
    4.12 + * This program is distributed in the hope that it will be useful,
    4.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    4.15 + * GNU General Public License for more details.
    4.16 + *
    4.17 + * You should have received a copy of the GNU General Public License
    4.18 + * along with this program. Look for COPYING file in the top folder.
    4.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    4.20 + */
    4.21 +package org.apidesign.bck2brwsr.aot;
    4.22 +
    4.23 +import java.io.IOException;
    4.24 +import java.io.InputStream;
    4.25 +import java.util.Arrays;
    4.26 +import java.util.HashMap;
    4.27 +import java.util.Map;
    4.28 +import net.orfjackal.retrolambda.LambdaClassBackporter;
    4.29 +import net.orfjackal.retrolambda.LambdaClassDumper;
    4.30 +import net.orfjackal.retrolambda.LambdaClassSaver;
    4.31 +import net.orfjackal.retrolambda.LambdaReifier;
    4.32 +import net.orfjackal.retrolambda.LambdaUsageBackporter;
    4.33 +import net.orfjackal.retrolambda.asm.Opcodes;
    4.34 +import org.apidesign.bck2brwsr.core.ExtraJavaScript;
    4.35 +import org.apidesign.vm4brwsr.Bck2Brwsr;
    4.36 +
    4.37 +/**
    4.38 + *
    4.39 + * @author Jaroslav Tulach
    4.40 + */
    4.41 +@ExtraJavaScript(processByteCode = false, resource="")
    4.42 +final class RetroLambda extends LambdaClassSaver implements BytecodeProcessor {
    4.43 +    private Map<String,byte[]> converted;
    4.44 +    
    4.45 +    public RetroLambda() {
    4.46 +        super(null, Opcodes.V1_7);
    4.47 +    }
    4.48 +
    4.49 +    @Override
    4.50 +    public void saveIfLambda(String className, byte[] bytecode) {
    4.51 +        if (LambdaReifier.isLambdaClassToReify(className)) {
    4.52 +            try {
    4.53 +                System.out.println("Saving lambda class: " + className);
    4.54 +                byte[] backportedBytecode = LambdaClassBackporter.transform(bytecode, Opcodes.V1_7);
    4.55 +                putBytecode(className, backportedBytecode);
    4.56 +            } catch (Throwable t) {
    4.57 +                // print to stdout to keep in sync with other log output
    4.58 +                throw new IllegalStateException("ERROR: Failed to backport lambda class: " + className);
    4.59 +            }
    4.60 +        }
    4.61 +    }
    4.62 +
    4.63 +    private void putBytecode(String className, byte[] backportedBytecode) {
    4.64 +        if (converted == null) {
    4.65 +            converted = new HashMap<>();
    4.66 +        }
    4.67 +        converted.put(className, backportedBytecode);
    4.68 +    }
    4.69 +    
    4.70 +    @Override
    4.71 +    public Map<String, byte[]> process(
    4.72 +        String className, byte[] byteCode, Bck2Brwsr.Resources resources
    4.73 +    ) throws IOException {
    4.74 +        int minor = byteCode[4] << 8 | byteCode[5];
    4.75 +        int major = byteCode[6] << 8 | byteCode[7];
    4.76 +        System.err.println("processing: " + className + " major: "+ major + " minor: " + minor);
    4.77 +        if (major <= 51) {
    4.78 +            return null;
    4.79 +        }
    4.80 +        
    4.81 +        ClassLoader prev = Thread.currentThread().getContextClassLoader();
    4.82 +        try (LambdaClassDumper dumper = new LambdaClassDumper(this)) {
    4.83 +            Thread.currentThread().setContextClassLoader(new ResLdr(resources));
    4.84 +            dumper.install();
    4.85 +            
    4.86 +            byte[] newB = LambdaUsageBackporter.transform(byteCode, Opcodes.V1_7);
    4.87 +            if (!Arrays.equals(newB, byteCode)) {
    4.88 +                putBytecode(className, newB);
    4.89 +            }
    4.90 +        } finally {
    4.91 +            Thread.currentThread().setContextClassLoader(prev);
    4.92 +        }
    4.93 +        
    4.94 +        Map<String, byte[]> ret = converted;
    4.95 +        converted = null;
    4.96 +        return ret;
    4.97 +    }
    4.98 +   
    4.99 +    private static final class ResLdr extends ClassLoader {
   4.100 +        private final Bck2Brwsr.Resources res;
   4.101 +
   4.102 +        public ResLdr(Bck2Brwsr.Resources res) {
   4.103 +            this.res = res;
   4.104 +        }
   4.105 +        
   4.106 +        @Override
   4.107 +        public Class<?> loadClass(String name) throws ClassNotFoundException {
   4.108 +            Class<?> c = findLoadedClass(name);
   4.109 +            if (c != null) {
   4.110 +                return c;
   4.111 +            }
   4.112 +            String r = name.replace('.', '/') + ".class";
   4.113 +            try (InputStream is = res.get(r)) {
   4.114 +                if (is == null) {
   4.115 +                    throw new ClassNotFoundException(name);
   4.116 +                }
   4.117 +                byte[] arr = Bck2BrwsrJars.readFrom(is);
   4.118 +                return defineClass(name, arr, 0, arr.length);
   4.119 +            } catch (IOException e) {
   4.120 +                return super.loadClass(name);
   4.121 +            }
   4.122 +        }
   4.123 +    }    
   4.124 +}