Can call RetroLambda processor in JAR files.
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 +}