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