2 * Back 2 Browser Bytecode Translator
3 * Copyright (C) 2012-2015 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
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.
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.
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.
18 package org.apidesign.bck2brwsr.aot;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.util.Arrays;
23 import java.util.HashMap;
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;
36 * @author Jaroslav Tulach
38 @ExtraJavaScript(processByteCode = false, resource="")
39 final class RetroLambda extends LambdaClassSaver implements BytecodeProcessor {
40 private Map<String,byte[]> converted;
42 public RetroLambda() {
43 super(null, Opcodes.V1_7);
47 public void saveIfLambda(String className, byte[] bytecode) {
48 if (LambdaReifier.isLambdaClassToReify(className)) {
50 byte[] backportedBytecode = LambdaClassBackporter.transform(bytecode, Opcodes.V1_7);
51 putBytecode(className + ".class", backportedBytecode);
52 } catch (Throwable t) {
53 // print to stdout to keep in sync with other log output
54 throw new IllegalStateException("ERROR: Failed to backport lambda class: " + className);
59 private void putBytecode(String className, byte[] backportedBytecode) {
60 assert className.endsWith(".class") : "Full resource: " + className;
61 if (converted == null) {
62 converted = new HashMap<>();
64 converted.put(className, backportedBytecode);
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];
77 ClassLoader prev = Thread.currentThread().getContextClassLoader();
78 try (LambdaClassDumper dumper = new LambdaClassDumper(this)) {
79 Thread.currentThread().setContextClassLoader(new ResLdr(resources));
82 byte[] newB = LambdaUsageBackporter.transform(byteCode, Opcodes.V1_7);
83 if (!Arrays.equals(newB, byteCode)) {
84 putBytecode(className, newB);
86 } catch (Throwable t) {
89 Thread.currentThread().setContextClassLoader(prev);
92 Map<String, byte[]> ret = converted;
97 private static final class ResLdr extends ClassLoader {
98 private final Bck2Brwsr.Resources res;
100 public ResLdr(Bck2Brwsr.Resources res) {
105 public Class<?> loadClass(String name) throws ClassNotFoundException {
106 Class<?> c = findLoadedClass(name);
110 if (name.startsWith("java.")) {
111 return super.loadClass(name);
113 String r = name.replace('.', '/') + ".class";
114 try (InputStream is = res.get(r)) {
116 throw new ClassNotFoundException(name);
118 byte[] arr = Bck2BrwsrJars.readFrom(is);
119 return defineClass(name, arr, 0, arr.length);
120 } catch (IOException e) {
121 return super.loadClass(name);