rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/RetroLambda.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 13 Sep 2014 13:44:01 +0200
branchjdk8
changeset 1678 35daab73e225
child 1684 3238bffeaf12
permissions -rw-r--r--
Can call RetroLambda processor in JAR files.
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program. Look for COPYING file in the top folder.
    16  * If not, see http://opensource.org/licenses/GPL-2.0.
    17  */
    18 package org.apidesign.bck2brwsr.aot;
    19 
    20 import java.io.IOException;
    21 import java.io.InputStream;
    22 import java.util.Arrays;
    23 import java.util.HashMap;
    24 import java.util.Map;
    25 import net.orfjackal.retrolambda.LambdaClassBackporter;
    26 import net.orfjackal.retrolambda.LambdaClassDumper;
    27 import net.orfjackal.retrolambda.LambdaClassSaver;
    28 import net.orfjackal.retrolambda.LambdaReifier;
    29 import net.orfjackal.retrolambda.LambdaUsageBackporter;
    30 import net.orfjackal.retrolambda.asm.Opcodes;
    31 import org.apidesign.bck2brwsr.core.ExtraJavaScript;
    32 import org.apidesign.vm4brwsr.Bck2Brwsr;
    33 
    34 /**
    35  *
    36  * @author Jaroslav Tulach
    37  */
    38 @ExtraJavaScript(processByteCode = false, resource="")
    39 final class RetroLambda extends LambdaClassSaver implements BytecodeProcessor {
    40     private Map<String,byte[]> converted;
    41     
    42     public RetroLambda() {
    43         super(null, Opcodes.V1_7);
    44     }
    45 
    46     @Override
    47     public void saveIfLambda(String className, byte[] bytecode) {
    48         if (LambdaReifier.isLambdaClassToReify(className)) {
    49             try {
    50                 System.out.println("Saving lambda class: " + className);
    51                 byte[] backportedBytecode = LambdaClassBackporter.transform(bytecode, Opcodes.V1_7);
    52                 putBytecode(className, backportedBytecode);
    53             } catch (Throwable t) {
    54                 // print to stdout to keep in sync with other log output
    55                 throw new IllegalStateException("ERROR: Failed to backport lambda class: " + className);
    56             }
    57         }
    58     }
    59 
    60     private void putBytecode(String className, byte[] backportedBytecode) {
    61         if (converted == null) {
    62             converted = new HashMap<>();
    63         }
    64         converted.put(className, backportedBytecode);
    65     }
    66     
    67     @Override
    68     public Map<String, byte[]> process(
    69         String className, byte[] byteCode, Bck2Brwsr.Resources resources
    70     ) throws IOException {
    71         int minor = byteCode[4] << 8 | byteCode[5];
    72         int major = byteCode[6] << 8 | byteCode[7];
    73         System.err.println("processing: " + className + " major: "+ major + " minor: " + minor);
    74         if (major <= 51) {
    75             return null;
    76         }
    77         
    78         ClassLoader prev = Thread.currentThread().getContextClassLoader();
    79         try (LambdaClassDumper dumper = new LambdaClassDumper(this)) {
    80             Thread.currentThread().setContextClassLoader(new ResLdr(resources));
    81             dumper.install();
    82             
    83             byte[] newB = LambdaUsageBackporter.transform(byteCode, Opcodes.V1_7);
    84             if (!Arrays.equals(newB, byteCode)) {
    85                 putBytecode(className, newB);
    86             }
    87         } finally {
    88             Thread.currentThread().setContextClassLoader(prev);
    89         }
    90         
    91         Map<String, byte[]> ret = converted;
    92         converted = null;
    93         return ret;
    94     }
    95    
    96     private static final class ResLdr extends ClassLoader {
    97         private final Bck2Brwsr.Resources res;
    98 
    99         public ResLdr(Bck2Brwsr.Resources res) {
   100             this.res = res;
   101         }
   102         
   103         @Override
   104         public Class<?> loadClass(String name) throws ClassNotFoundException {
   105             Class<?> c = findLoadedClass(name);
   106             if (c != null) {
   107                 return c;
   108             }
   109             String r = name.replace('.', '/') + ".class";
   110             try (InputStream is = res.get(r)) {
   111                 if (is == null) {
   112                     throw new ClassNotFoundException(name);
   113                 }
   114                 byte[] arr = Bck2BrwsrJars.readFrom(is);
   115                 return defineClass(name, arr, 0, arr.length);
   116             } catch (IOException e) {
   117                 return super.loadClass(name);
   118             }
   119         }
   120     }    
   121 }