jaroslav@1644: /** jaroslav@1644: * Back 2 Browser Bytecode Translator jaroslav@1644: * Copyright (C) 2012 Jaroslav Tulach jaroslav@1644: * jaroslav@1644: * This program is free software: you can redistribute it and/or modify jaroslav@1644: * it under the terms of the GNU General Public License as published by jaroslav@1644: * the Free Software Foundation, version 2 of the License. jaroslav@1644: * jaroslav@1644: * This program is distributed in the hope that it will be useful, jaroslav@1644: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@1644: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@1644: * GNU General Public License for more details. jaroslav@1644: * jaroslav@1644: * You should have received a copy of the GNU General Public License jaroslav@1644: * along with this program. Look for COPYING file in the top folder. jaroslav@1644: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@1644: */ jaroslav@1644: package org.apidesign.vm4brwsr; jaroslav@1644: jaroslav@1645: import java.io.ByteArrayInputStream; jaroslav@1645: import java.io.IOException; jaroslav@1644: import java.io.InputStream; jaroslav@1644: import java.lang.invoke.CallSite; jaroslav@1644: import java.lang.invoke.MethodHandles; jaroslav@1644: import java.lang.invoke.MethodType; jaroslav@1644: import java.lang.reflect.Method; jaroslav@1645: import java.net.URL; jaroslav@1645: import java.util.Enumeration; jaroslav@1644: import org.objectweb.asm.ClassReader; jaroslav@1644: import org.objectweb.asm.ClassVisitor; jaroslav@1644: import org.objectweb.asm.ClassWriter; jaroslav@1644: import org.objectweb.asm.Handle; jaroslav@1644: import org.objectweb.asm.MethodVisitor; jaroslav@1644: import org.objectweb.asm.Opcodes; jaroslav@1644: import static org.objectweb.asm.Opcodes.ASM4; jaroslav@1644: import static org.objectweb.asm.Opcodes.INVOKESTATIC; jaroslav@1644: import static org.testng.Assert.*; jaroslav@1645: import org.testng.annotations.AfterClass; jaroslav@1644: import org.testng.annotations.BeforeClass; jaroslav@1644: import org.testng.annotations.Test; jaroslav@1644: jaroslav@1644: /** jaroslav@1644: * jaroslav@1644: * @author Jaroslav Tulach jaroslav@1644: */ jaroslav@1644: public class InvokeDynamicTest { jaroslav@1644: private static Class invokeDynamicClass; jaroslav@1645: private static byte[] invokeDynamicBytes; jaroslav@1645: private static TestVM code; jaroslav@1644: jaroslav@1644: @Test public void simpleDynamicInJava() throws Exception { jaroslav@1644: Method m = invokeDynamicClass.getMethod("dynamicSay"); jaroslav@1644: Object ret = m.invoke(m); jaroslav@1644: assertEquals(ret, "Hello from Dynamic!"); jaroslav@1644: } jaroslav@1644: jaroslav@1645: @Test public void simpleDynamicInJS() throws Exception { jaroslav@1645: code.assertExec( jaroslav@1645: "Invoke dynamic can return a value", InvokeDynamic.class, jaroslav@1645: "dynamic__Ljava_lang_String_2", jaroslav@1645: "Hello from Dynamic!" jaroslav@1645: ); jaroslav@1645: } jaroslav@1644: jaroslav@1644: jaroslav@1644: @AfterClass jaroslav@1644: public static void releaseTheCode() { jaroslav@1644: code = null; jaroslav@1644: } jaroslav@1644: jaroslav@1644: // jaroslav@1644: // the following code is inspired by jaroslav@1644: // https://code.google.com/p/indy-maven-plugin/ jaroslav@1644: // which I don't want to use, as it is not in a public repository jaroslav@1644: // jaroslav@1644: @BeforeClass jaroslav@1644: public static void prepareClass() throws Exception { jaroslav@1645: InputStream is = InvokeDynamic.class.getResourceAsStream("InvokeDynamic.class"); jaroslav@1645: assertNotNull(is, "Class found"); jaroslav@1644: jaroslav@1645: ClassReader reader = new ClassReader(is); jaroslav@1644: ClassWriter writer = new ClassWriter(reader, 0); jaroslav@1644: jaroslav@1644: reader.accept( jaroslav@1644: new ClassVisitor(ASM4, writer) { jaroslav@1644: @Override jaroslav@1644: public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions jaroslav@1644: ) { jaroslav@1644: MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); jaroslav@1644: return new InvokeDynamicProcessor(mv); jaroslav@1644: } jaroslav@1644: }, jaroslav@1644: 0); jaroslav@1645: is.close(); jaroslav@1645: invokeDynamicBytes = writer.toByteArray(); jaroslav@1644: ClassLoader l = new ClassLoader() { jaroslav@1644: @Override jaroslav@1644: public Class loadClass(String name) throws ClassNotFoundException { jaroslav@1644: if (name.equals(InvokeDynamic.class.getName())) { jaroslav@1644: return defineClass(name, invokeDynamicBytes, 0, invokeDynamicBytes.length); jaroslav@1644: } jaroslav@1644: return super.loadClass(name); jaroslav@1644: } jaroslav@1644: }; jaroslav@1644: invokeDynamicClass = l.loadClass(InvokeDynamic.class.getName()); jaroslav@1645: jaroslav@1645: code = TestVM.compileClass( jaroslav@1645: null, null, new EmulationResourcesWithException(), jaroslav@1645: InvokeDynamic.class.getName().replace('.', '/') jaroslav@1645: ); jaroslav@1644: } jaroslav@1644: jaroslav@1644: jaroslav@1644: private static class InvokeDynamicProcessor extends MethodVisitor { jaroslav@1644: InvokeDynamicProcessor(MethodVisitor mv) { jaroslav@1644: super(ASM4, mv); jaroslav@1644: } jaroslav@1644: jaroslav@1644: @Override jaroslav@1644: public void visitMethodInsn(int opcode, String owner, String name, String desc) { jaroslav@1644: if (opcode == INVOKESTATIC) { jaroslav@1644: if (name.startsWith("TEST_dynamic_")) { jaroslav@1644: final String shortName = name.substring(13); jaroslav@1644: Handle mh = new Handle( jaroslav@1644: Opcodes.H_INVOKESTATIC, owner, shortName, jaroslav@1644: MethodType.methodType( jaroslav@1644: CallSite.class, jaroslav@1644: MethodHandles.Lookup.class, jaroslav@1644: String.class, jaroslav@1644: MethodType.class jaroslav@1644: ).toMethodDescriptorString() jaroslav@1644: ); jaroslav@1644: super.visitInvokeDynamicInsn(shortName, desc, mh); jaroslav@1644: return; jaroslav@1644: } jaroslav@1644: } jaroslav@1644: super.visitMethodInsn(opcode, owner, name, desc); jaroslav@1644: } jaroslav@1644: } jaroslav@1644: jaroslav@1645: private static class EmulationResourcesWithException implements Bck2Brwsr.Resources { jaroslav@1645: @Override jaroslav@1645: public InputStream get(String name) throws IOException { jaroslav@1645: if ("org/apidesign/vm4brwsr/InvokeDynamic.class".equals(name)) { jaroslav@1645: return new ByteArrayInputStream(invokeDynamicBytes); jaroslav@1645: } jaroslav@1645: if ("java/net/URI.class".equals(name)) { jaroslav@1645: // skip jaroslav@1645: return null; jaroslav@1645: } jaroslav@1645: if ("java/net/URLConnection.class".equals(name)) { jaroslav@1645: // skip jaroslav@1645: return null; jaroslav@1645: } jaroslav@1645: if ("java/lang/System.class".equals(name)) { jaroslav@1645: // skip jaroslav@1645: return null; jaroslav@1645: } jaroslav@1645: Enumeration en = InvokeDynamicTest.class.getClassLoader().getResources(name); jaroslav@1645: URL u = null; jaroslav@1645: while (en.hasMoreElements()) { jaroslav@1645: u = en.nextElement(); jaroslav@1645: } jaroslav@1645: if (u == null) { jaroslav@1645: throw new IOException("Can't find " + name); jaroslav@1645: } jaroslav@1645: if (u.toExternalForm().contains("rt.jar!")) { jaroslav@1645: throw new IOException("No emulation for " + u); jaroslav@1645: } jaroslav@1645: return u.openStream(); jaroslav@1645: } jaroslav@1645: } jaroslav@1644: jaroslav@1644: }