# HG changeset patch # User Jaroslav Tulach # Date 1410625985 -7200 # Node ID e709c530c2276df3966ed7daf63e19c779c0609a # Parent 7a9492920b6122ed6cf85abccfb7dc360249fc4c# Parent baa80d712c776b28cabfab4134e171fa9e5d1215 Lambda's work OK in AOT mode. Defender methods work OK. Merging. diff -r 7a9492920b61 -r e709c530c227 launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/CompileCP.java --- a/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/CompileCP.java Tue Sep 09 12:45:22 2014 +0200 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/CompileCP.java Sat Sep 13 18:33:05 2014 +0200 @@ -77,26 +77,9 @@ } if (s != null) { File root = new File(s); - List arr = new ArrayList<>(); - List classes = new ArrayList<>(); - listDir(root, null, classes, arr); StringWriter w = new StringWriter(); try { - Bck2Brwsr.newCompiler() - .addRootClasses(classes.toArray(new String[0])) - .addResources(arr.toArray(new String[0])) - .library() - //.obfuscation(ObfuscationLevel.FULL) - .resources(new EmulationResources() { - @Override - public InputStream get(String resource) throws IOException { - if (r != null) { - final URL url = r.get(resource, 0); - return url == null ? null : url.openStream(); - } - return super.get(resource); - } - }) + Bck2BrwsrJars.configureFrom(null, root) .generate(w); w.flush(); return w.toString(); @@ -109,23 +92,6 @@ return null; } - - private static void listDir(File f, String pref, List classes, List resources) throws IOException { - File[] arr = f.listFiles(); - if (arr == null) { - if (f.getName().endsWith(".class")) { - classes.add(pref + f.getName().substring(0, f.getName().length() - 6)); - } else { - resources.add(pref + f.getName()); - } - } else { - for (File ch : arr) { - - listDir(ch, pref == null ? "" : pref + f.getName() + "/", classes, resources); - } - } - } - static void compileVM(StringBuilder sb, final Res r) throws IOException { final Bck2Brwsr rt; try { @@ -155,26 +121,4 @@ } }).generate(sb); } - - static class EmulationResources implements Bck2Brwsr.Resources { - - @Override - public InputStream get(String name) throws IOException { - Enumeration en = Bck2BrwsrJars.class.getClassLoader().getResources(name); - URL u = null; - while (en.hasMoreElements()) { - u = en.nextElement(); - } - if (u == null) { - LOG.log(Level.WARNING, "Cannot find {0}", name); - return null; - } - if (u.toExternalForm().contains("/rt.jar!")) { - LOG.log(Level.WARNING, "{0}No bootdelegation for ", name); - return null; - } - return u.openStream(); - } - } - } diff -r 7a9492920b61 -r e709c530c227 rt/aot/pom.xml --- a/rt/aot/pom.xml Tue Sep 09 12:45:22 2014 +0200 +++ b/rt/aot/pom.xml Sat Sep 13 18:33:05 2014 +0200 @@ -27,5 +27,11 @@ ${project.version} jar + + net.orfjackal.retrolambda + retrolambda + 1.6.1 + jar + diff -r 7a9492920b61 -r e709c530c227 rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/Bck2BrwsrJars.java --- a/rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/Bck2BrwsrJars.java Tue Sep 09 12:45:22 2014 +0200 +++ b/rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/Bck2BrwsrJars.java Sat Sep 13 18:33:05 2014 +0200 @@ -18,15 +18,19 @@ package org.apidesign.bck2brwsr.aot; import java.io.BufferedReader; +import java.io.ByteArrayInputStream; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -43,7 +47,7 @@ */ public final class Bck2BrwsrJars { private static final Logger LOG = Logger.getLogger(Bck2BrwsrJars.class.getName()); - + private Bck2BrwsrJars() { } @@ -64,24 +68,30 @@ * @throws IOException if something goes wrong */ public static Bck2Brwsr configureFrom(Bck2Brwsr c, File jar) throws IOException { + if (jar.isDirectory()) { + return configureDir(c, jar); + } final JarFile jf = new JarFile(jar); - List classes = new ArrayList<>(); + final List classes = new ArrayList<>(); List resources = new ArrayList<>(); Set exported = new HashSet<>(); - - listJAR(jf, classes, resources, exported); - - String cp = jf.getManifest().getMainAttributes().getValue("Class-Path"); // NOI18N - String[] classpath = cp == null ? new String[0] : cp.split(" "); - class JarRes extends EmulationResources implements Bck2Brwsr.Resources { - + JarRes() { + super(classes); + } @Override public InputStream get(String resource) throws IOException { InputStream is = jf.getInputStream(new ZipEntry(resource)); return is == null ? super.get(resource) : is; } } + JarRes jarRes = new JarRes(); + + listJAR(jf, jarRes, resources, exported); + + String cp = jf.getManifest().getMainAttributes().getValue("Class-Path"); // NOI18N + String[] classpath = cp == null ? new String[0] : cp.split(" "); + if (c == null) { c = Bck2Brwsr.newCompiler(); } @@ -91,11 +101,11 @@ .addClasses(classes.toArray(new String[classes.size()])) .addExported(exported.toArray(new String[exported.size()])) .addResources(resources.toArray(new String[resources.size()])) - .resources(new JarRes()); + .resources(jarRes); } private static void listJAR( - JarFile j, List classes, + JarFile j, EmulationResources classes, List resources, Set keep ) throws IOException { Enumeration en = j.entries(); @@ -114,7 +124,7 @@ keep.add(pkg); } if (n.endsWith(".class")) { - classes.add(n.substring(0, n.length() - 6)); + classes.addClassResource(n); } else { resources.add(n); if (n.startsWith("META-INF/services/") && keep != null) { @@ -142,11 +152,59 @@ } } } + + static byte[] readFrom(InputStream is) throws IOException { + int expLen = is.available(); + if (expLen < 1) { + expLen = 1; + } + byte[] arr = new byte[expLen]; + int pos = 0; + for (;;) { + int read = is.read(arr, pos, arr.length - pos); + if (read == -1) { + break; + } + pos += read; + if (pos == arr.length) { + byte[] tmp = new byte[arr.length * 2]; + System.arraycopy(arr, 0, tmp, 0, arr.length); + arr = tmp; + } + } + if (pos != arr.length) { + byte[] tmp = new byte[pos]; + System.arraycopy(arr, 0, tmp, 0, pos); + arr = tmp; + } + return arr; + } + static class EmulationResources implements Bck2Brwsr.Resources { + private final List classes; + private final Map converted = new HashMap<>(); + private final BytecodeProcessor proc; + + protected EmulationResources(List classes) { + this.classes = classes; + BytecodeProcessor p; + try { + Class bpClass = Class.forName("org.apidesign.bck2brwsr.aot.RetroLambda"); + p = (BytecodeProcessor) bpClass.newInstance(); + } catch (Throwable t) { + p = null; + } + this.proc = p; + } @Override public InputStream get(String name) throws IOException { + byte[] arr = converted.get(name); + if (arr != null) { + return new ByteArrayInputStream(arr); + } + Enumeration en = Bck2BrwsrJars.class.getClassLoader().getResources(name); URL u = null; while (en.hasMoreElements()) { @@ -162,6 +220,83 @@ } return u.openStream(); } + + private void addClassResource(String n) throws IOException { + if (proc != null) { + try (InputStream is = this.get(n)) { + Map conv = proc.process(n, readFrom(is), this); + if (conv != null) { + boolean found = false; + for (Map.Entry entrySet : conv.entrySet()) { + String res = entrySet.getKey(); + byte[] bytes = entrySet.getValue(); + if (res.equals(n)) { + found = true; + } + assert res.endsWith(".class") : "Wrong resource: " + res; + converted.put(res, bytes); + classes.add(res.substring(0, res.length() - 6)); + } + if (!found) { + throw new IOException("Cannot find " + n + " among " + conv); + } + return; + } + } + } + classes.add(n.substring(0, n.length() - 6)); + } + } + + private static Bck2Brwsr configureDir(Bck2Brwsr c, final File dir) throws IOException { + List arr = new ArrayList<>(); + List classes = new ArrayList<>(); + class DirRes extends EmulationResources { + public DirRes(List classes) { + super(classes); + } + + @Override + public InputStream get(String name) throws IOException { + InputStream is = super.get(name); + if (is != null) { + return is; + } + File r = new File(dir, name.replace('/', File.separatorChar)); + if (r.exists()) { + return new FileInputStream(r); + } + return null; + } + } + DirRes dirRes = new DirRes(classes); + listDir(dir, null, dirRes, arr); + if (c == null) { + c = Bck2Brwsr.newCompiler(); + } + return c + .addRootClasses(classes.toArray(new String[0])) + .addResources(arr.toArray(new String[0])) + .library() + //.obfuscation(ObfuscationLevel.FULL) + .resources(dirRes); + } + + private static void listDir( + File f, String pref, EmulationResources res, List resources + ) throws IOException { + File[] arr = f.listFiles(); + if (arr == null) { + if (f.getName().endsWith(".class")) { + res.addClassResource(pref + f.getName()); + } else { + resources.add(pref + f.getName()); + } + } else { + for (File ch : arr) { + listDir(ch, pref == null ? "" : pref + f.getName() + "/", res, resources); + } + } } } diff -r 7a9492920b61 -r e709c530c227 rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/BytecodeProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/BytecodeProcessor.java Sat Sep 13 18:33:05 2014 +0200 @@ -0,0 +1,40 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.aot; + +import java.io.IOException; +import java.util.Map; +import org.apidesign.vm4brwsr.Bck2Brwsr; + +/** Replace bytecode of a single class with many new bytecodes. + * + * @author Jaroslav Tulach + */ +interface BytecodeProcessor { + /** Does the conversion. + * + * @param className the resource of the class to replace + * @param byteCode the bytecode of the class + * @param resources access to other resources in the system + * @return map of resource to bytecode which must include at least + * one element of name className + * @throws IOException + */ + public Map process(String className, byte[] byteCode, Bck2Brwsr.Resources resources) + throws IOException; +} diff -r 7a9492920b61 -r e709c530c227 rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/RetroLambda.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/aot/src/main/java/org/apidesign/bck2brwsr/aot/RetroLambda.java Sat Sep 13 18:33:05 2014 +0200 @@ -0,0 +1,125 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.aot; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import net.orfjackal.retrolambda.LambdaClassBackporter; +import net.orfjackal.retrolambda.LambdaClassDumper; +import net.orfjackal.retrolambda.LambdaClassSaver; +import net.orfjackal.retrolambda.LambdaReifier; +import net.orfjackal.retrolambda.LambdaUsageBackporter; +import net.orfjackal.retrolambda.asm.Opcodes; +import org.apidesign.bck2brwsr.core.ExtraJavaScript; +import org.apidesign.vm4brwsr.Bck2Brwsr; + +/** + * + * @author Jaroslav Tulach + */ +@ExtraJavaScript(processByteCode = false, resource="") +final class RetroLambda extends LambdaClassSaver implements BytecodeProcessor { + private Map converted; + + public RetroLambda() { + super(null, Opcodes.V1_7); + } + + @Override + public void saveIfLambda(String className, byte[] bytecode) { + if (LambdaReifier.isLambdaClassToReify(className)) { + try { + byte[] backportedBytecode = LambdaClassBackporter.transform(bytecode, Opcodes.V1_7); + putBytecode(className + ".class", backportedBytecode); + } catch (Throwable t) { + // print to stdout to keep in sync with other log output + throw new IllegalStateException("ERROR: Failed to backport lambda class: " + className); + } + } + } + + private void putBytecode(String className, byte[] backportedBytecode) { + assert className.endsWith(".class") : "Full resource: " + className; + if (converted == null) { + converted = new HashMap<>(); + } + converted.put(className, backportedBytecode); + } + + @Override + public Map process( + String className, byte[] byteCode, Bck2Brwsr.Resources resources + ) throws IOException { + int minor = byteCode[4] << 8 | byteCode[5]; + int major = byteCode[6] << 8 | byteCode[7]; + if (major <= 51) { + return null; + } + + ClassLoader prev = Thread.currentThread().getContextClassLoader(); + try (LambdaClassDumper dumper = new LambdaClassDumper(this)) { + Thread.currentThread().setContextClassLoader(new ResLdr(resources)); + dumper.install(); + + byte[] newB = LambdaUsageBackporter.transform(byteCode, Opcodes.V1_7); + if (!Arrays.equals(newB, byteCode)) { + putBytecode(className, newB); + } + } catch (Throwable t) { + t.printStackTrace(); + } finally { + Thread.currentThread().setContextClassLoader(prev); + } + + Map ret = converted; + converted = null; + return ret; + } + + private static final class ResLdr extends ClassLoader { + private final Bck2Brwsr.Resources res; + + public ResLdr(Bck2Brwsr.Resources res) { + this.res = res; + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException { + Class c = findLoadedClass(name); + if (c != null) { + return c; + } + if (name.startsWith("java.")) { + return super.loadClass(name); + } + String r = name.replace('.', '/') + ".class"; + try (InputStream is = res.get(r)) { + if (is == null) { + throw new ClassNotFoundException(name); + } + byte[] arr = Bck2BrwsrJars.readFrom(is); + return defineClass(name, arr, 0, arr.length); + } catch (IOException e) { + return super.loadClass(name); + } + } + } +} diff -r 7a9492920b61 -r e709c530c227 rt/emul/mini/src/main/java/java/lang/Class.java --- a/rt/emul/mini/src/main/java/java/lang/Class.java Tue Sep 09 12:45:22 2014 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/Class.java Sat Sep 13 18:33:05 2014 +0200 @@ -94,6 +94,11 @@ private static final int ENUM = 0x00004000; private static final int SYNTHETIC = 0x00001000; + /* Backing store of user-defined values pertaining to this class. + * Maintained by the ClassValue class. + */ + transient Object classValueMap; + /* * Constructor. Only the Java Virtual Machine creates Class * objects. diff -r 7a9492920b61 -r e709c530c227 rt/emul/mini/src/main/java/java/lang/ClassLoader.java --- a/rt/emul/mini/src/main/java/java/lang/ClassLoader.java Tue Sep 09 12:45:22 2014 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/ClassLoader.java Sat Sep 13 18:33:05 2014 +0200 @@ -499,13 +499,13 @@ * @since 1.1 */ protected final Class findLoadedClass(String name) { - if (!checkName(name)) + try { + return Class.forName(name); + } catch (ClassNotFoundException ex) { return null; - return findLoadedClass0(name); + } } - private native final Class findLoadedClass0(String name); - /** * Sets the signers of a class. This should be invoked after defining a * class.

@@ -884,10 +884,6 @@ return false; } - private boolean checkName(String name) { - throw new UnsupportedOperationException(); - } - private Class findBootstrapClassOrNull(String name) { throw new UnsupportedOperationException(); } diff -r 7a9492920b61 -r e709c530c227 rt/emul/mini/src/main/java/java/lang/NoSuchMethodError.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/emul/mini/src/main/java/java/lang/NoSuchMethodError.java Sat Sep 13 18:33:05 2014 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1994, 2008, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +/** + * Thrown if an application tries to call a specified method of a + * class (either static or instance), and that class no longer has a + * definition of that method. + *

+ * Normally, this error is caught by the compiler; this error can + * only occur at run time if the definition of a class has + * incompatibly changed. + * + * @author unascribed + * @since JDK1.0 + */ +public +class NoSuchMethodError extends IncompatibleClassChangeError { + private static final long serialVersionUID = -3765521442372831335L; + + /** + * Constructs a NoSuchMethodError with no detail message. + */ + public NoSuchMethodError() { + super(); + } + + /** + * Constructs a NoSuchMethodError with the + * specified detail message. + * + * @param s the detail message. + */ + public NoSuchMethodError(String s) { + super(s); + } +} diff -r 7a9492920b61 -r e709c530c227 rt/emul/mini/src/main/java/java/lang/VirtualMachineError.java --- a/rt/emul/mini/src/main/java/java/lang/VirtualMachineError.java Tue Sep 09 12:45:22 2014 +0200 +++ b/rt/emul/mini/src/main/java/java/lang/VirtualMachineError.java Sat Sep 13 18:33:05 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 1997, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,8 +33,9 @@ * @author Frank Yellin * @since JDK1.0 */ -abstract public -class VirtualMachineError extends Error { +abstract public class VirtualMachineError extends Error { + private static final long serialVersionUID = 4161983926571568670L; + /** * Constructs a VirtualMachineError with no detail message. */ @@ -46,9 +47,43 @@ * Constructs a VirtualMachineError with the specified * detail message. * - * @param s the detail message. + * @param message the detail message. */ - public VirtualMachineError(String s) { - super(s); + public VirtualMachineError(String message) { + super(message); + } + + /** + * Constructs a {@code VirtualMachineError} with the specified + * detail message and cause.

Note that the detail message + * associated with {@code cause} is not automatically + * incorporated in this error's detail message. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link #getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A {@code null} value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.8 + */ + public VirtualMachineError(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructs an a {@code VirtualMachineError} with the specified + * cause and a detail message of {@code (cause==null ? null : + * cause.toString())} (which typically contains the class and + * detail message of {@code cause}). + * + * @param cause the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A {@code null} value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.8 + */ + public VirtualMachineError(Throwable cause) { + super(cause); } } diff -r 7a9492920b61 -r e709c530c227 rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java --- a/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Tue Sep 09 12:45:22 2014 +0200 +++ b/rt/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/MethodImpl.java Sat Sep 13 18:33:05 2014 +0200 @@ -53,7 +53,7 @@ + "var arr = new Array();\n" + "function check(m, verify) {\n" + " if (m.indexOf(prefix) === 0) {\n" - + " if (!c[m].cls) return;\n" + + " if (!c[m] || !c[m].cls) return;\n" + " if (verify) {\n" + " for (var i = 0; i < arr.length; i += 3) {\n" + " if (arr[i] === m) return;\n" diff -r 7a9492920b61 -r e709c530c227 rt/pom.xml --- a/rt/pom.xml Tue Sep 09 12:45:22 2014 +0200 +++ b/rt/pom.xml Sat Sep 13 18:33:05 2014 +0200 @@ -19,4 +19,14 @@ vmtest aot + + + + 1.8 + + + vm8 + + + diff -r 7a9492920b61 -r e709c530c227 rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java Tue Sep 09 12:45:22 2014 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeParser.java Sat Sep 13 18:33:05 2014 +0200 @@ -28,6 +28,7 @@ import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import org.apidesign.bck2brwsr.core.JavaScriptBody; import org.apidesign.bck2brwsr.core.JavaScriptPrototype; @@ -58,6 +59,9 @@ public static final int CONSTANT_METHOD = 10; public static final int CONSTANT_INTERFACEMETHOD = 11; public static final int CONSTANT_NAMEANDTYPE = 12; + public static final int CONSTANT_METHODHANDLE = 15; + public static final int CONSTANT_METHODTYPE = 16; + public static final int CONSTANT_INVOKEDYNAMIC = 18; /* Access Flags */ public static final int ACC_PUBLIC = 0x00000001; @@ -287,7 +291,7 @@ public static final int opc_invokespecial = 183; public static final int opc_invokestatic = 184; public static final int opc_invokeinterface = 185; -// public static final int opc_xxxunusedxxx = 186; + public static final int opc_invokedynamic = 186; public static final int opc_new = 187; public static final int opc_newarray = 188; public static final int opc_anewarray = 189; @@ -495,7 +499,7 @@ */ private static class CPX { - int cpx; + final int cpx; CPX(int cpx) { this.cpx = cpx; @@ -507,9 +511,9 @@ * * @author Sucheta Dambalkar (Adopted code from jdis) */ - private static class CPX2 { + static class CPX2 { - int cpx1, cpx2; + final int cpx1, cpx2; CPX2(int cpx1, int cpx2) { this.cpx1 = cpx1; @@ -538,6 +542,7 @@ private FieldData[] fields; private MethodData[] methods; private InnerClassData[] innerClasses; + private BootMethodData[] bootMethods; private int attributes_count; private AttrData[] attrs; private int source_cpx = 0; @@ -621,6 +626,12 @@ AttrData attr = new AttrData(this); attr.read(name_cpx); attrs[k] = attr; + } else if (getTag(name_cpx) == CONSTANT_UTF8 + && getString(name_cpx).equals("BootstrapMethods")) { + AttrData attr = new AttrData(this); + bootMethods = readBootstrapMethods(in); + attr.read(name_cpx); + attrs[k] = attr; } else { AttrData attr = new AttrData(this); attr.read(name_cpx, in); @@ -630,6 +641,22 @@ in.close(); } // end ClassData.read() + BootMethodData[] readBootstrapMethods(DataInputStream in) throws IOException { + int attr_len = in.readInt(); //attr_lengt + int number = in.readShort(); + BootMethodData[] arr = new BootMethodData[number]; + for (int i = 0; i < number; i++) { + int ref = in.readShort(); + int len = in.readShort(); + int[] args = new int[len]; + for (int j = 0; j < len; j++) { + args[j] = in.readShort(); + } + arr[i] = new BootMethodData(this, ref, args); + } + return arr; + } + /** * Reads and stores constant pool info. */ @@ -668,7 +695,15 @@ case CONSTANT_NAMEANDTYPE: cpool[i] = new CPX2(in.readUnsignedShort(), in.readUnsignedShort()); break; - + case CONSTANT_METHODHANDLE: + cpool[i] = new CPX2(in.readByte(), in.readUnsignedShort()); + break; + case CONSTANT_METHODTYPE: + cpool[i] = new CPX(in.readUnsignedShort()); + break; + case CONSTANT_INVOKEDYNAMIC: + cpool[i] = new CPX2(in.readUnsignedShort(), in.readUnsignedShort()); + break; case 0: default: throw new ClassFormatError("invalid constant type: " + (int) tags[i]); @@ -1107,8 +1142,12 @@ case CONSTANT_NAMEANDTYPE: return getName(((CPX2) x).cpx1) + ":" + StringValue(((CPX2) x).cpx2); + case CONSTANT_METHODHANDLE: + return "K" + ((CPX2)x).cpx1 + "@" + stringValue(((CPX2)x).cpx2, textual); + case CONSTANT_METHODTYPE: + return stringValue(((CPX)x).cpx, true); default: - return "UnknownTag"; //TBD + return "UnknownTag" + tag; //TBD } } @@ -1191,6 +1230,10 @@ return null; } } + + public BootMethodData getBootMethod(int indx) { + return bootMethods != null ? bootMethods[indx] : null; + } /** * Returns total constant pool entry count. @@ -1549,6 +1592,31 @@ return accflags; } } // end InnerClassData + + static class BootMethodData { + private final ClassData clazz; + final int method; + private final int[] args; + + private BootMethodData(ClassData clazz, int method, int[] args) { + this.clazz = clazz; + this.method = method; + this.args = args; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(clazz.stringValue(method, true)); + sb.append('('); + for (int indx : args) { + sb.append("\n "); + sb.append(clazz.stringValue(indx, true)); + } + sb.append(')'); + return sb.toString(); + } + } /** * Strores LineNumberTable data information. @@ -1800,7 +1868,7 @@ stackMap[i] = new StackMapData(in, this); } } - + /** * Return access of the method. */ diff -r 7a9492920b61 -r e709c530c227 rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Tue Sep 09 12:45:22 2014 +0200 +++ b/rt/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Sat Sep 13 18:33:05 2014 +0200 @@ -19,7 +19,7 @@ import java.io.IOException; import java.io.InputStream; -import java.util.Locale; +import org.apidesign.bck2brwsr.core.JavaScriptBody; import static org.apidesign.vm4brwsr.ByteCodeParser.*; /** Translator of the code inside class files to JavaScript. @@ -258,9 +258,22 @@ append("\n ").append(destObject).append(".").append(mn).append(".cls = CLS;"); } append("\n c.constructor = CLS;"); - append("\n function fillInstOf(x) {"); + append("\n function ").append(className).append("fillInstOf(x) {"); String instOfName = "$instOf_" + className; append("\n Object.defineProperty(x, '").append(instOfName).append("', { value : true });"); + if (jc.isInterface()) { + for (MethodData m : jc.getMethods()) { + if ((m.getAccess() & ACC_ABSTRACT) == 0 + && (m.getAccess() & ACC_STATIC) == 0 + && (m.getAccess() & ACC_PRIVATE) == 0) { + final String mn = findMethodName(m, new StringBuilder()); + append("\n try {"); + append("\n if (!x['").append(mn).append("']) Object.defineProperty(x, '").append(mn).append("', { value : c['").append(mn).append("']});"); + append("\n } catch (ignore) {"); + append("\n }"); + } + } + } for (String superInterface : jc.getSuperInterfaces()) { String intrfc = superInterface.replace('/', '_'); append("\n vm.").append(intrfc).append("(false)['fillInstOf'](x);"); @@ -268,8 +281,8 @@ } append("\n }"); append("\n try {"); - append("\n Object.defineProperty(c, 'fillInstOf', { value: fillInstOf });"); - append("\n fillInstOf(c);"); + append("\n Object.defineProperty(c, 'fillInstOf', { value: ").append(className).append("fillInstOf });"); + append("\n ").append(className).append("fillInstOf(c);"); append("\n } catch (ignore) {"); append("\n }"); // obfuscationDelegate.exportJSProperty(this, "c", instOfName); @@ -1051,6 +1064,49 @@ case opc_invokestatic: i = invokeStaticMethod(byteCodes, i, smapper, true); break; + case opc_invokedynamic: { + int indx = readUShortArg(byteCodes, i); + println("invoke dynamic: " + indx); + ByteCodeParser.CPX2 c2 = jc.getCpoolEntry(indx); + BootMethodData bm = jc.getBootMethod(c2.cpx1); + CPX2 methodHandle = jc.getCpoolEntry(bm.method); + println(" type: " + methodHandle.cpx1); + String[] mi = jc.getFieldInfoName(methodHandle.cpx2); + String mcn = mangleClassName(mi[0]); + char[] returnType = {'V'}; + StringBuilder cnt = new StringBuilder(); + String mn = findMethodName(mi, cnt, returnType); + println(" mi[0]: " + mi[0]); + println(" mi[1]: " + mi[1]); + println(" mi[2]: " + mi[2]); + println(" mn : " + mn); + println(" name and type: " + jc.stringValue(c2.cpx2, true)); + CPX2 nameAndType = jc.getCpoolEntry(c2.cpx2); + String type = jc.StringValue(nameAndType.cpx2); + String object = accessClass(mcn) + "(false)"; + if (mn.startsWith("cons_")) { + object += ".constructor"; + } + append("var metHan = "); + append(accessStaticMethod(object, mn, mi)); + append('('); + String lookup = accessClass("java_lang_invoke_MethodHandles") + "(false).findFor__Ljava_lang_invoke_MethodHandles$Lookup_2Ljava_lang_Class_2(CLS.$class)"; + append(lookup); + append(", '").append(mi[1]).append("', "); + String methodType = accessClass("java_lang_invoke_MethodType") + "(false).fromMethodDescriptorString__Ljava_lang_invoke_MethodType_2Ljava_lang_String_2Ljava_lang_ClassLoader_2("; + append(methodType).append("'").append(type).append("', null)"); +// if (numArguments > 0) { +// append(vars[0]); +// for (int j = 1; j < numArguments; ++j) { +// append(", "); +// append(vars[j]); +// } +// } + append(");"); + emit(smapper, this, "throw 'Invoke dynamic: ' + @1 + ': ' + metHan;", "" + indx); + i += 4; + break; + } case opc_new: { int indx = readUShortArg(byteCodes, i); String ci = jc.getClassName(indx); @@ -1557,7 +1613,7 @@ sb.append(ch); } else { sb.append("_0"); - String hex = Integer.toHexString(ch).toLowerCase(Locale.ENGLISH); + String hex = Integer.toHexString(ch).toLowerCase(); for (int m = hex.length(); m < 4; m++) { sb.append("0"); } @@ -2310,4 +2366,9 @@ append(Integer.toString(cc)); } } + + @JavaScriptBody(args = "msg", body = "") + private static void println(String msg) { + System.err.println(msg); + } } diff -r 7a9492920b61 -r e709c530c227 rt/vm/src/test/java/org/apidesign/vm4brwsr/TestVM.java --- a/rt/vm/src/test/java/org/apidesign/vm4brwsr/TestVM.java Tue Sep 09 12:45:22 2014 +0200 +++ b/rt/vm/src/test/java/org/apidesign/vm4brwsr/TestVM.java Sat Sep 13 18:33:05 2014 +0200 @@ -123,10 +123,18 @@ } static TestVM compileClass(StringBuilder sb, ScriptEngine[] eng, String... names) throws ScriptException, IOException { + return compileClass(sb, eng, new EmulationResources(), names); + } + static TestVM compileClass( + StringBuilder sb, + ScriptEngine[] eng, + Bck2Brwsr.Resources resources, + String... names + ) throws ScriptException, IOException { if (sb == null) { sb = new StringBuilder(); } - Bck2Brwsr.generate(sb, new EmulationResources(), names); + Bck2Brwsr.generate(sb, resources, names); ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine js = sem.getEngineByExtension("js"); if (eng != null) { diff -r 7a9492920b61 -r e709c530c227 rt/vm8/pom.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm8/pom.xml Sat Sep 13 18:33:05 2014 +0200 @@ -0,0 +1,74 @@ + + + 4.0.0 + + org.apidesign.bck2brwsr + rt + 1.0-SNAPSHOT + + vm8 + Bck2Brwsr on JDK8 + jar + + + + org.apache.maven.plugins + maven-compiler-plugin + 2.3.2 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-surefire-plugin + + + brwsr + + + + + + + + org.testng + testng + test + + + org.apidesign.bck2brwsr + vm4brwsr + ${project.version} + test + jar + + + org.ow2.asm + asm-debug-all + 4.1 + test + jar + + + ${project.groupId} + emul + ${project.version} + test + + + org.apidesign.bck2brwsr + vmtest + ${project.version} + test + jar + + + ${project.groupId} + launcher.http + ${project.version} + test + + + \ No newline at end of file diff -r 7a9492920b61 -r e709c530c227 rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/Defaults.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/Defaults.java Sat Sep 13 18:33:05 2014 +0200 @@ -0,0 +1,65 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vm8; + +public interface Defaults { + public static int staticValue() { + return 42; + } + + public default int value() { + return 42; + } + + public static Defaults create(int type) { + class X implements Defaults { + } + class Y implements Defaults { + @Override + public int value() { + return 7; + } + } + class Z implements DoubleDefaults { + } + switch (type) { + case 0: return new X(); + case 1: return new Y(); + default: return new Z(); + } + } + + public static int defaultValue() { + return create(0).value(); + } + + public static int myValue() { + return create(1).value(); + } + + public static int sndValue() { + return create(2).value(); + } + + public interface DoubleDefaults extends Defaults { + @Override + public default int value() { + return 84; + } + } +} diff -r 7a9492920b61 -r e709c530c227 rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/DefaultsTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/DefaultsTest.java Sat Sep 13 18:33:05 2014 +0200 @@ -0,0 +1,41 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vm8; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +public class DefaultsTest { + @Compare public int callStatic() throws Exception { + return Defaults.defaultValue(); + } + + @Compare public int overridenValue() throws Exception { + return Defaults.myValue(); + } + + @Compare public int doubleDefault() throws Exception { + return Defaults.sndValue(); + } + + @Factory public static Object[] create() { + return VMTest.create(DefaultsTest.class); + } + +} diff -r 7a9492920b61 -r e709c530c227 rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/Lambdas.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/Lambdas.java Sat Sep 13 18:33:05 2014 +0200 @@ -0,0 +1,36 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vm8; + +/** + * + * @author Jaroslav Tulach + */ +public class Lambdas { + private static void fewTimes(Runnable r, int cnt) { + while (cnt-- > 0) { + r.run(); + } + } + + public static String compound() { + StringBuilder sb = new StringBuilder(); + fewTimes(() -> sb.append('X'), 10); + return sb.toString(); + } +} diff -r 7a9492920b61 -r e709c530c227 rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/LambdasTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rt/vm8/src/test/java/org/apidesign/bck2brwsr/vm8/LambdasTest.java Sat Sep 13 18:33:05 2014 +0200 @@ -0,0 +1,37 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.vm8; + +import org.apidesign.bck2brwsr.vmtest.Compare; +import org.apidesign.bck2brwsr.vmtest.VMTest; +import org.testng.annotations.Factory; + +/** + * + * @author Jaroslav Tulach + */ +public class LambdasTest { + @Compare public String StringverifyJSTime() throws Exception { + return Lambdas.compound(); + } + + @Factory public static Object[] create() { + return VMTest.create(LambdasTest.class); + } +} +