rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/RetroLambda.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 17 Jan 2017 07:04:06 +0100
changeset 1985 cd1cc103a03c
parent 1787 ea12a3bb4b33
permissions -rw-r--r--
Implementation of ClassValue for bck2brwsr
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012-2015 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.nio.file.Path;
    23 import java.util.Arrays;
    24 import java.util.HashMap;
    25 import java.util.Map;
    26 import net.orfjackal.retrolambda.Transformers;
    27 import net.orfjackal.retrolambda.asm.ClassReader;
    28 import net.orfjackal.retrolambda.asm.Opcodes;
    29 import net.orfjackal.retrolambda.files.OutputDirectory;
    30 import net.orfjackal.retrolambda.interfaces.ClassHierarchyAnalyzer;
    31 import net.orfjackal.retrolambda.lambdas.LambdaClassDumper;
    32 import net.orfjackal.retrolambda.lambdas.LambdaClassSaver;
    33 import org.apidesign.bck2brwsr.core.ExtraJavaScript;
    34 import org.apidesign.vm4brwsr.Bck2Brwsr;
    35 
    36 /**
    37  *
    38  * @author Jaroslav Tulach
    39  */
    40 @ExtraJavaScript(processByteCode = false, resource="")
    41 final class RetroLambda extends OutputDirectory implements BytecodeProcessor {
    42     private Map<String,byte[]> converted;
    43     private final Transformers transformers;
    44     private final LambdaClassSaver saver;
    45     
    46     public RetroLambda() {
    47         super(null);
    48         transformers = new Transformers(Opcodes.V1_7, false, new ClassHierarchyAnalyzer());
    49         saver = new LambdaClassSaver(this, transformers);
    50     }
    51 
    52     @Override
    53     public void writeFile(Path relativePath, byte[] content) throws IOException {
    54         throw new UnsupportedOperationException();
    55     }
    56 
    57     @Override
    58     public void writeClass(byte[] bytecode) throws IOException {
    59         if (bytecode == null) {
    60             return;
    61         }
    62         ClassReader cr = new ClassReader(bytecode);
    63         String className = cr.getClassName();
    64         putBytecode(className + ".class", bytecode);
    65     }
    66 
    67     private void putBytecode(String className, byte[] backportedBytecode) {
    68         assert className.endsWith(".class") : "Full resource: " + className;
    69         if (converted == null) {
    70             converted = new HashMap<>();
    71         }
    72         converted.put(className, backportedBytecode);
    73     }
    74     
    75     @Override
    76     public Map<String, byte[]> process(
    77         String className, byte[] byteCode, Bck2Brwsr.Resources resources
    78     ) throws IOException {
    79         int minor = byteCode[4] << 8 | byteCode[5];
    80         int major = byteCode[6] << 8 | byteCode[7];
    81         if (major <= 51) {
    82             return null;
    83         }
    84         
    85         ClassLoader prev = Thread.currentThread().getContextClassLoader();
    86         try (LambdaClassDumper dumper = new LambdaClassDumper(saver)) {
    87             Thread.currentThread().setContextClassLoader(new ResLdr(resources));
    88             dumper.install();
    89             
    90             byte[] newB = transformers.backportClass(new ClassReader(byteCode));
    91             if (!Arrays.equals(newB, byteCode)) {
    92                 putBytecode(className, newB);
    93             }
    94         } catch (Throwable t) {
    95             t.printStackTrace();
    96         } finally {
    97             Thread.currentThread().setContextClassLoader(prev);
    98         }
    99         
   100         Map<String, byte[]> ret = converted;
   101         converted = null;
   102         return ret;
   103     }
   104    
   105     private static final class ResLdr extends ClassLoader {
   106         private final Bck2Brwsr.Resources res;
   107 
   108         public ResLdr(Bck2Brwsr.Resources res) {
   109             this.res = res;
   110         }
   111         
   112         @Override
   113         public Class<?> loadClass(String name) throws ClassNotFoundException {
   114             Class<?> c = findLoadedClass(name);
   115             if (c != null) {
   116                 return c;
   117             }
   118             if (name.startsWith("java.")) {
   119                 return super.loadClass(name);
   120             }
   121             String r = name.replace('.', '/') + ".class";
   122             try (InputStream is = res.get(r)) {
   123                 if (is == null) {
   124                     throw new ClassNotFoundException(name);
   125                 }
   126                 byte[] arr = Bck2BrwsrJars.readFrom(is);
   127                 return defineClass(name, arr, 0, arr.length);
   128             } catch (IOException e) {
   129                 return super.loadClass(name);
   130             }
   131         }
   132     }    
   133 }