All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.orfjackal.retrolambda.lambdas.InnerClassLambdaMetafactoryTransformer Maven / Gradle / Ivy

The newest version!
// Copyright © 2013-2018 Esko Luontola and other Retrolambda contributors
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

package net.orfjackal.retrolambda.lambdas;

import com.esotericsoftware.minlog.Log;
import net.orfjackal.retrolambda.Agent;
import org.objectweb.asm.*;

import java.lang.instrument.*;
import java.security.ProtectionDomain;

public class InnerClassLambdaMetafactoryTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException {
        if (!"java/lang/invoke/InnerClassLambdaMetafactory".equals(className)) {
            return null;
        }

        try {
            byte[] transformed = transformMetafactory(bytes);
            Agent.enable();
            return transformed;
        } catch (Throwable e) {
            Log.error("Failed to transform " + className + ", cannot enable the Java agent. " +
                    "Please report an issue to Retrolambda with full logs. " +
                    "Probably you're running on an unsupported Java version.", e);
            return null;
        }
    }

    private byte[] transformMetafactory(byte[] bytes) {
        final boolean[] spinInnerClassFound = {false};
        final boolean[] toByteArrayFound = {false};
        ClassReader cr = new ClassReader(bytes);
        ClassWriter cw = new ClassWriter(cr, 0);
        ClassVisitor cv = new ClassVisitor(Opcodes.ASM7, cw) {
            @Override
            public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
                MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
                if (name.equals("spinInnerClass")) {
                    spinInnerClassFound[0] = true;
                    mv = new MethodVisitor(Opcodes.ASM7, mv) {
                        @Override
                        public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
                            super.visitMethodInsn(opcode, owner, name, desc, itf);
                            if (name.equals("toByteArray")) {
                                if (toByteArrayFound[0]) {
                                    throw new RuntimeException("Found multiple toByteArray calls");
                                } else {
                                    toByteArrayFound[0] = true;
                                }
                                mv.visitInsn(Opcodes.DUP);
                                mv.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Agent.class), "saveLambda", "([B)V", false);
                            }
                        }

                        @Override
                        public void visitMaxs(int maxStack, int maxLocals) {
                            super.visitMaxs(maxStack + 1, maxLocals);
                        }
                    };
                }
                return mv;
            }
        };
        cr.accept(cv, 0);
        if (!spinInnerClassFound[0]) {
            throw new RuntimeException("Could not find the spinInnerClass method");
        }
        if (!toByteArrayFound[0]) {
            throw new RuntimeException("Could not find the toByteArray call");
        }
        return cw.toByteArray();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy