com.dragome.compiler.invokedynamic.InvokeDynamicBackporter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dragome-bytecode-js-compiler Show documentation
Show all versions of dragome-bytecode-js-compiler Show documentation
Dragome SDK module: bytecode to javascript compiler
/*******************************************************************************
* Copyright (c) 2011-2014 Fernando Petrola
*
* This file is part of Dragome SDK.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
******************************************************************************/
package com.dragome.compiler.invokedynamic;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import com.dragome.commons.compiler.BytecodeTransformer;
public class InvokeDynamicBackporter implements BytecodeTransformer
{
public static byte[] transform(byte[] bytecode)
{
ClassNode classNode= new ClassNode(Opcodes.ASM5);
InvokeDynamicConverter invokeDynamicConverter= new InvokeDynamicConverter(classNode);
new ClassReader(bytecode).accept(invokeDynamicConverter, 0);
ClassWriter cw= new ClassWriter(ClassWriter.COMPUTE_MAXS);
classNode.accept(cw);
return cw.toByteArray();
}
private static class InvokeDynamicConverter extends ClassVisitor
{
private int classAccess;
private String className;
public InvokeDynamicConverter(ClassVisitor next)
{
super(Opcodes.ASM5, next);
}
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
{
super.visit(version, access, name, signature, superName, interfaces);
this.classAccess= access;
this.className= name;
}
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
{
if (isBridgeMethodOnInterface(access))
{
return null;
}
if (isNonAbstractMethodOnInterface(access) && !isClassInitializerMethod(name, desc, access))
{
// System.out.println("WARNING: Method '" + name + "' of interface '" + className + "' is non-abstract! " + "This will probably fail to run on Java 7 and below. " + "If you get this warning _without_ using Java 8's default methods, " + "please report a bug at https://github.com/orfjackal/retrolambda/issues " + "together with an SSCCE (http://www.sscce.org/)");
}
MethodVisitor mv= super.visitMethod(access, name, desc, signature, exceptions);
return new InvokeDynamicInsnConvertingMethodVisitor(api, mv, className);
}
private boolean isBridgeMethodOnInterface(int methodAccess)
{
return Flags.hasFlag(classAccess, Opcodes.ACC_INTERFACE) && Flags.hasFlag(methodAccess, Opcodes.ACC_BRIDGE);
}
private boolean isNonAbstractMethodOnInterface(int methodAccess)
{
return Flags.hasFlag(classAccess, Opcodes.ACC_INTERFACE) && !Flags.hasFlag(methodAccess, Opcodes.ACC_ABSTRACT);
}
private static boolean isClassInitializerMethod(String name, String desc, int methodAccess)
{
return name.equals("") && desc.equals("()V") && Flags.hasFlag(methodAccess, Opcodes.ACC_STATIC);
}
}
private static class InvokeDynamicInsnConvertingMethodVisitor extends MethodVisitor
{
private final String myClassName;
public InvokeDynamicInsnConvertingMethodVisitor(int api, MethodVisitor mv, String myClassName)
{
super(api, mv);
this.myClassName= myClassName;
}
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs)
{
backportLambda(name, Type.getType(desc), bsm, bsmArgs);
}
private void backportLambda(String invokedName, Type invokedType, Handle bsm, Object[] bsmArgs)
{
Type[] argumentTypes= Type.getArgumentTypes(invokedType.toString());
Type returnType= Type.getReturnType(invokedType.toString());
String returnTypeName= returnType.getClassName();
int length= argumentTypes.length;
createArrayWithParameters(length, argumentTypes);
this.visitLdcInsn(myClassName);
this.visitLdcInsn(invokedName);
this.visitLdcInsn(returnTypeName);
this.visitLdcInsn(invokedType.toString());
this.visitLdcInsn(bsmArgs[1].toString());
this.visitVarInsn(Opcodes.ALOAD, 20);
this.visitLdcInsn(bsm.getTag() == 5 ? "virtual" : "static");
String runnableSignature= "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;";
this.visitMethodInsn(INVOKESTATIC, "com/dragome/utils/DragomeCallsiteFactory", "create", runnableSignature, false);
}
private void createArrayWithParameters(int parametersCount, Type[] argumentTypes)
{
this.visitIntInsn(Opcodes.BIPUSH, parametersCount);
this.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
this.visitVarInsn(Opcodes.ASTORE, 20);
for (int i= parametersCount - 1; i >= 0; i--)
{
convertPrimitive(argumentTypes[i], i);
this.visitVarInsn(Opcodes.ASTORE, 21);
this.visitVarInsn(Opcodes.ALOAD, 20);
this.visitIntInsn(Opcodes.BIPUSH, i);
this.visitVarInsn(Opcodes.ALOAD, 21);
this.visitInsn(Opcodes.AASTORE);
}
}
private void convertPrimitive(Object tp, int i)
{
if (tp.equals(Type.BOOLEAN_TYPE))
{
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false);
}
else if (tp.equals(Type.BYTE_TYPE))
{
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false);
}
else if (tp.equals(Type.CHAR_TYPE))
{
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false);
}
else if (tp.equals(Type.SHORT_TYPE))
{
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false);
}
else if (tp.equals(Type.INT_TYPE))
{
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
}
else if (tp.equals(Type.LONG_TYPE))
{
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
i++;
}
else if (tp.equals(Type.FLOAT_TYPE))
{
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
}
else if (tp.equals(Type.DOUBLE_TYPE))
{
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
i++;
}
// else
// mv.visitVarInsn(Opcodes.ALOAD, i);
}
}
public byte[] transform(String className, byte[] bytecode)
{
return transform(bytecode);
}
public boolean requiresTransformation(String className)
{
return true;
}
}