jaroslav@1678: /** jaroslav@1678: * Back 2 Browser Bytecode Translator jaroslav@1678: * Copyright (C) 2012 Jaroslav Tulach jaroslav@1678: * jaroslav@1678: * This program is free software: you can redistribute it and/or modify jaroslav@1678: * it under the terms of the GNU General Public License as published by jaroslav@1678: * the Free Software Foundation, version 2 of the License. jaroslav@1678: * jaroslav@1678: * This program is distributed in the hope that it will be useful, jaroslav@1678: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@1678: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@1678: * GNU General Public License for more details. jaroslav@1678: * jaroslav@1678: * You should have received a copy of the GNU General Public License jaroslav@1678: * along with this program. Look for COPYING file in the top folder. jaroslav@1678: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@1678: */ jaroslav@1678: package org.apidesign.bck2brwsr.aot; jaroslav@1678: jaroslav@1678: import java.io.IOException; jaroslav@1678: import java.io.InputStream; jaroslav@1678: import java.util.Arrays; jaroslav@1678: import java.util.HashMap; jaroslav@1678: import java.util.Map; jaroslav@1678: import net.orfjackal.retrolambda.LambdaClassBackporter; jaroslav@1678: import net.orfjackal.retrolambda.LambdaClassDumper; jaroslav@1678: import net.orfjackal.retrolambda.LambdaClassSaver; jaroslav@1678: import net.orfjackal.retrolambda.LambdaReifier; jaroslav@1678: import net.orfjackal.retrolambda.LambdaUsageBackporter; jaroslav@1678: import net.orfjackal.retrolambda.asm.Opcodes; jaroslav@1678: import org.apidesign.bck2brwsr.core.ExtraJavaScript; jaroslav@1678: import org.apidesign.vm4brwsr.Bck2Brwsr; jaroslav@1678: jaroslav@1678: /** jaroslav@1678: * jaroslav@1678: * @author Jaroslav Tulach jaroslav@1678: */ jaroslav@1678: @ExtraJavaScript(processByteCode = false, resource="") jaroslav@1678: final class RetroLambda extends LambdaClassSaver implements BytecodeProcessor { jaroslav@1678: private Map converted; jaroslav@1678: jaroslav@1678: public RetroLambda() { jaroslav@1678: super(null, Opcodes.V1_7); jaroslav@1678: } jaroslav@1678: jaroslav@1678: @Override jaroslav@1678: public void saveIfLambda(String className, byte[] bytecode) { jaroslav@1678: if (LambdaReifier.isLambdaClassToReify(className)) { jaroslav@1678: try { jaroslav@1678: byte[] backportedBytecode = LambdaClassBackporter.transform(bytecode, Opcodes.V1_7); jaroslav@1684: putBytecode(className + ".class", backportedBytecode); jaroslav@1678: } catch (Throwable t) { jaroslav@1678: // print to stdout to keep in sync with other log output jaroslav@1678: throw new IllegalStateException("ERROR: Failed to backport lambda class: " + className); jaroslav@1678: } jaroslav@1678: } jaroslav@1678: } jaroslav@1678: jaroslav@1678: private void putBytecode(String className, byte[] backportedBytecode) { jaroslav@1684: assert className.endsWith(".class") : "Full resource: " + className; jaroslav@1678: if (converted == null) { jaroslav@1678: converted = new HashMap<>(); jaroslav@1678: } jaroslav@1678: converted.put(className, backportedBytecode); jaroslav@1678: } jaroslav@1678: jaroslav@1678: @Override jaroslav@1678: public Map process( jaroslav@1678: String className, byte[] byteCode, Bck2Brwsr.Resources resources jaroslav@1678: ) throws IOException { jaroslav@1678: int minor = byteCode[4] << 8 | byteCode[5]; jaroslav@1678: int major = byteCode[6] << 8 | byteCode[7]; jaroslav@1678: System.err.println("processing: " + className + " major: "+ major + " minor: " + minor); jaroslav@1678: if (major <= 51) { jaroslav@1678: return null; jaroslav@1678: } jaroslav@1678: jaroslav@1678: ClassLoader prev = Thread.currentThread().getContextClassLoader(); jaroslav@1678: try (LambdaClassDumper dumper = new LambdaClassDumper(this)) { jaroslav@1678: Thread.currentThread().setContextClassLoader(new ResLdr(resources)); jaroslav@1678: dumper.install(); jaroslav@1678: jaroslav@1678: byte[] newB = LambdaUsageBackporter.transform(byteCode, Opcodes.V1_7); jaroslav@1678: if (!Arrays.equals(newB, byteCode)) { jaroslav@1678: putBytecode(className, newB); jaroslav@1678: } jaroslav@1684: } catch (Throwable t) { jaroslav@1684: t.printStackTrace(); jaroslav@1678: } finally { jaroslav@1678: Thread.currentThread().setContextClassLoader(prev); jaroslav@1678: } jaroslav@1678: jaroslav@1678: Map ret = converted; jaroslav@1678: converted = null; jaroslav@1678: return ret; jaroslav@1678: } jaroslav@1678: jaroslav@1678: private static final class ResLdr extends ClassLoader { jaroslav@1678: private final Bck2Brwsr.Resources res; jaroslav@1678: jaroslav@1678: public ResLdr(Bck2Brwsr.Resources res) { jaroslav@1678: this.res = res; jaroslav@1678: } jaroslav@1678: jaroslav@1678: @Override jaroslav@1678: public Class loadClass(String name) throws ClassNotFoundException { jaroslav@1678: Class c = findLoadedClass(name); jaroslav@1678: if (c != null) { jaroslav@1678: return c; jaroslav@1678: } jaroslav@1684: if (name.startsWith("java.")) { jaroslav@1684: return super.loadClass(name); jaroslav@1684: } jaroslav@1678: String r = name.replace('.', '/') + ".class"; jaroslav@1678: try (InputStream is = res.get(r)) { jaroslav@1678: if (is == null) { jaroslav@1678: throw new ClassNotFoundException(name); jaroslav@1678: } jaroslav@1678: byte[] arr = Bck2BrwsrJars.readFrom(is); jaroslav@1678: return defineClass(name, arr, 0, arr.length); jaroslav@1678: } catch (IOException e) { jaroslav@1678: return super.loadClass(name); jaroslav@1678: } jaroslav@1678: } jaroslav@1678: } jaroslav@1678: }