/*
* The MIT License (MIT)
*
* Copyright (c) 2016. Diorite (by Bartłomiej Mazur (aka GotoFinal))
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.diorite.inject.impl.asm;
import javax.annotation.Nullable;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.instrument.ClassFileTransformer;
import java.util.Iterator;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.diorite.inject.Qualifier;
import org.diorite.inject.Qualifiers;
import org.diorite.inject.Scope;
import org.diorite.inject.impl.utils.AsmUtils;
import org.diorite.inject.impl.utils.Constants;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.description.method.MethodDescription.InDefinedShape;
import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeDescription.ForLoadedType;
import net.bytebuddy.dynamic.DynamicType.Loaded;
import net.bytebuddy.dynamic.DynamicType.Unloaded;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy.Default;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.Implementation.Context;
import net.bytebuddy.pool.TypePool;
public class QualifierAndScopeImplementationGenerator implements ClassFileTransformer, Opcodes
{
public static final String GENERATED_PREFIX = "org.diorite.di.generated.annotations";
public static final Object[] STACK = {};
public static final int HASHCODE_MULTI = 127;
//
// public QualifierAndScopeImplementationGenerator(DIController controller, Instrumentation instrumentation)
// {
// super(controller, instrumentation);
// }
//
// @Override
// public byte[] transform(DIController controller, Instrumentation instr, Module module, ClassLoader loader, String className, Class<?> clazz,
// ProtectionDomain pd, byte[] bytes) throws IllegalClassFormatException
// {
// if (clazz == null)
// {
// return null;
// }
// ClassReader classReader = new ClassReader(bytes);
// if ((classReader.getInterfaces().length != 1) || ! classReader.getInterfaces()[0].equals("java/lang/annotation/Annotation"))
// {
// return null;
// }
// try
// {
// TypePool pool = TypePool.Default.ofClassPath();
// TypeDescription description = pool.describe(className.replace('/', '.')).resolve();
// AnnotationList annotations = description.getInheritedAnnotations();
// if (! description.isAnnotation() || ! (annotations.isAnnotationPresent(Qualifier.class) || annotations.isAnnotationPresent(Scope.class)))
// {
// return null;
// }
// String name = GENERATED_PREFIX + "." + description.getName();
// Unloaded<Object> make = new ByteBuddy().subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
// .implement(pool.describe(Serializable.class.getName()).resolve(), description).name(name)
// .visit(new AnnotationImplementationVisitor(description)).make();
// Loaded<Object> load = make.load(ClassLoader.getSystemClassLoader(), Default.INJECTION);
// return null;
// }
// catch (Throwable e)
// {
// e.printStackTrace();
// return null;
// }
// }
@SuppressWarnings("unchecked")
@Nullable
public static <T extends Annotation> Class<? extends T> transform(Class<T> clazz)
{
if (! clazz.isAnnotation() || ! (clazz.isAnnotationPresent(Qualifier.class) || clazz.isAnnotationPresent(Scope.class)))
{
return null;
}
try
{
String name = GENERATED_PREFIX + "." + clazz.getName();
Unloaded<Object> make = new ByteBuddy(ClassFileVersion.JAVA_V9).subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)
.implement(Serializable.class, clazz).name(name)
.visit(new AnnotationImplementationVisitor(new ForLoadedType(clazz))).make();
Loaded<Object> load = make.load(ClassLoader.getSystemClassLoader(), Default.INJECTION);
return (Class<? extends T>) load.getLoaded();
}
catch (Throwable e)
{
throw new RuntimeException(e);
}
}
private static final class AnnotationImplementationVisitor implements AsmVisitorWrapper
{
private final TypeDescription clazz;
private AnnotationImplementationVisitor(TypeDescription clazz)
{
this.clazz = clazz;
}
@Override
public int mergeWriter(int flags)
{
return flags | ClassWriter.COMPUTE_MAXS;
}
@Override
public int mergeReader(int flags)
{
return flags;
}
@Override
public ClassVisitor wrap(TypeDescription typeDescription, ClassVisitor cv, Context context, TypePool typePool,
FieldList<FieldDescription.InDefinedShape> fieldList, MethodList<?> methodList, int i, int i1)
{
cv.visit(ClassFileVersion.JAVA_V9.getMinorMajorVersion(), typeDescription.getModifiers(), typeDescription.getInternalName(), null,
typeDescription.getSuperClass().asErasure().getInternalName(), typeDescription.getInterfaces().asErasures().toInternalNames());
TypeDescription clazz = this.clazz;
String internalName = clazz.getInternalName();
String descriptor = clazz.getDescriptor();
MethodList<InDefinedShape> declaredMethods = clazz.getDeclaredMethods();
int methodsSize = declaredMethods.size();
String implName = GENERATED_PREFIX + "." + clazz.getName();
String internalImplName = GENERATED_PREFIX.replace('.', '/') + "/" + internalName;
String descriptorImplName = "L" + GENERATED_PREFIX.replace('.', '/') + "/" + internalName + ";";
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
{
fv = cv.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, "serialVersionUID", "J", null, 0L);
fv.visitEnd();
}
if (methodsSize == 0)
{
{
fv = cv.visitField(ACC_PRIVATE + ACC_FINAL, "$_hashcode", "I", null, null);
fv.visitEnd();
}
{
fv = cv.visitField(ACC_PRIVATE + ACC_FINAL, "$_toString", "Ljava/lang/String;", null, null);
fv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ICONST_0);
mv.visitFieldInsn(PUTFIELD, internalImplName, "$_hashcode", "I");
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitVarInsn(ALOAD, 0);
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
mv.visitInsn(DUP);
//noinspection MagicNumber
mv.visitIntInsn(BIPUSH, 50);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(I)V", false);
mv.visitIntInsn(BIPUSH, '@');
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(C)Ljava/lang/StringBuilder;", false);
mv.visitLdcInsn(Type.getType(descriptor));
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
mv.visitLdcInsn("()");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
mv.visitFieldInsn(PUTFIELD, internalImplName, "$_toString", "Ljava/lang/String;");
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitInsn(RETURN);
Label l4 = new Label();
mv.visitLabel(l4);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l4, 0);
mv.visitMaxs(4, 1);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PUBLIC, "hashCode", "()I", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalImplName, "$_hashcode", "I");
mv.visitInsn(IRETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l1, 0);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(INSTANCEOF, internalName);
mv.visitInsn(IRETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l1, 0);
mv.visitLocalVariable("o", "Ljava/lang/Object;", null, l0, l1, 1);
mv.visitMaxs(1, 2);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalImplName, "$_toString", "Ljava/lang/String;");
mv.visitInsn(ARETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l1, 0);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PUBLIC, "annotationType", "()Ljava/lang/Class;", "()Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;",
null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLdcInsn(Type.getType(descriptor));
mv.visitInsn(ARETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l1, 0);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitTypeInsn(NEW, internalImplName);
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, internalImplName, "<init>", "()V", false);
mv.visitVarInsn(ASTORE, 0);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitTypeInsn(NEW, Constants.INSTANCE_SUPPLIER.getInternalName());
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, Constants.INSTANCE_SUPPLIER.getInternalName(), "<init>", "(Ljava/lang/Object;)V", false);
mv.visitVarInsn(ASTORE, 1);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitLdcInsn(Type.getType(descriptor));
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKESTATIC, Constants.QUALIFIERS.getInternalName(), "register",
"(Ljava/lang/Class;Ljava/util/function/Function;Ljava/util/function/Function;)V", false);
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitInsn(RETURN);
mv.visitLocalVariable("inst", descriptorImplName, null, l1, l3, 0);
mv.visitLocalVariable("supplier", Constants.INSTANCE_SUPPLIER.getDescriptor(), null, l2, l3, 1);
mv.visitMaxs(3, 2);
mv.visitEnd();
}
cv.visitEnd();
return cv;
}
{
fv = cv.visitField(ACC_PRIVATE + ACC_FINAL + ACC_STATIC, "DEFAULTS", "Ljava/util/Map;",
"Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;", null);
fv.visitEnd();
}
for (InDefinedShape method : declaredMethods)
{
fv = cv.visitField(ACC_PRIVATE + ACC_FINAL, method.getName(), method.getReturnType().asErasure().getDescriptor(), null, null);
fv.visitEnd();
}
{
fv = cv.visitField(ACC_PRIVATE + ACC_FINAL, "$_hashcode", "I", null, null);
fv.visitEnd();
}
{
fv = cv.visitField(ACC_PRIVATE + ACC_FINAL, "$_toString", "Ljava/lang/String;", null, null);
fv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/util/Map;)V", "(Ljava/util/Map<Ljava/lang/String;*>;)V", null);
mv.visitCode();
Label l0 = new Label();
Label l1 = new Label();
Label l2 = new Label();
mv.visitTryCatchBlock(l0, l1, l2, "java/lang/ClassCastException");
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitLabel(l0);
for (InDefinedShape method : declaredMethods)
{
String methodName = method.getName();
TypeDescription returnType = method.getReturnType().asErasure();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitLdcInsn(methodName);
mv.visitMethodInsn(INVOKESTATIC, internalImplName, "$_get", "(Ljava/util/Map;Ljava/lang/String;)Ljava/lang/Object;", false);
if (returnType.isPrimitive())
{
String wrapperInternal = AsmUtils.getWrapperClass(returnType).getInternalName();
mv.visitTypeInsn(CHECKCAST, wrapperInternal);
mv.visitMethodInsn(INVOKEVIRTUAL, wrapperInternal, returnType.getName() + "Value", "()" + returnType.getDescriptor(), false);
}
else
{
mv.visitTypeInsn(CHECKCAST, returnType.getInternalName());
}
mv.visitFieldInsn(PUTFIELD, internalImplName, methodName, returnType.getDescriptor());
}
mv.visitLabel(l1);
Label l9 = new Label();
mv.visitJumpInsn(GOTO, l9);
mv.visitLabel(l2);
mv.visitFrame(F_FULL, 2, new Object[]{internalImplName, "java/util/Map"}, 1,
new Object[]{"java/lang/ClassCastException"});
mv.visitVarInsn(ASTORE, 2);
Label l10 = new Label();
mv.visitLabel(l10);
mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
mv.visitInsn(DUP);
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
mv.visitLdcInsn("Wrong argument type: ");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V", false);
mv.visitInsn(ATHROW);
mv.visitLabel(l9);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, internalImplName, "$_initHashCode", "()I", false);
mv.visitFieldInsn(PUTFIELD, internalImplName, "$_hashcode", "I");
Label l11 = new Label();
mv.visitLabel(l11);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, internalImplName, "$_initToString", "()Ljava/lang/String;", false);
mv.visitFieldInsn(PUTFIELD, internalImplName, "$_toString", "Ljava/lang/String;");
Label l12 = new Label();
mv.visitLabel(l12);
mv.visitInsn(RETURN);
Label l13 = new Label();
mv.visitLabel(l13);
mv.visitLocalVariable("e", "Ljava/lang/ClassCastException;", null, l10, l9, 2);
mv.visitLocalVariable("this", descriptorImplName, null, l3, l13, 0);
mv.visitLocalVariable("data", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;*>;", l3, l13, 1);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PRIVATE + ACC_STATIC, "$_get", "(Ljava/util/Map;Ljava/lang/String;)Ljava/lang/Object;",
"<T:Ljava/lang/Object;>(Ljava/util/Map<Ljava/lang/String;*>;Ljava/lang/String;)TT;", null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
mv.visitVarInsn(ASTORE, 2);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitVarInsn(ALOAD, 2);
Label l2 = new Label();
mv.visitJumpInsn(IFNONNULL, l2);
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitFieldInsn(GETSTATIC, internalImplName, "DEFAULTS", "Ljava/util/Map;");
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true);
mv.visitVarInsn(ASTORE, 2);
Label l4 = new Label();
mv.visitLabel(l4);
mv.visitVarInsn(ALOAD, 2);
mv.visitJumpInsn(IFNONNULL, l2);
Label l5 = new Label();
mv.visitLabel(l5);
mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
mv.visitInsn(DUP);
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
mv.visitLdcInsn("Missing required annotation value: ");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V", false);
mv.visitInsn(ATHROW);
mv.visitLabel(l2);
mv.visitFrame(F_APPEND, 1, new Object[]{"java/lang/Object"}, 0, null);
mv.visitVarInsn(ALOAD, 2);
mv.visitInsn(ARETURN);
Label l6 = new Label();
mv.visitLabel(l6);
mv.visitLocalVariable("data", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;*>;", l0, l6, 0);
mv.visitLocalVariable("field", "Ljava/lang/String;", null, l0, l6, 1);
mv.visitLocalVariable("o", "Ljava/lang/Object;", null, l1, l6, 2);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
for (InDefinedShape method : declaredMethods)
{
String methodName = method.getName();
TypeDescription returnType = method.getReturnType().asErasure();
String returnTypeDescriptor = returnType.getDescriptor();
mv = cv.visitMethod(ACC_PUBLIC, methodName, "()" + returnTypeDescriptor, null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalImplName, methodName, returnTypeDescriptor);
if (returnType.isArray()) // array must be cloned.
{
mv.visitMethodInsn(INVOKEVIRTUAL, returnTypeDescriptor, "clone", "()Ljava/lang/Object;", false);
mv.visitTypeInsn(CHECKCAST, returnTypeDescriptor);
}
if (returnType.isPrimitive())
{
if (returnType.getName().equals(float.class.getName()))
{
mv.visitInsn(FRETURN);
}
else if (returnType.getName().equals(double.class.getName()))
{
mv.visitInsn(DRETURN);
}
else if (returnType.getName().equals(long.class.getName()))
{
mv.visitInsn(LRETURN);
}
else
{
mv.visitInsn(IRETURN);
}
}
else
{
mv.visitInsn(ARETURN);
}
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l1, 0);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PUBLIC, "<init>", "(" + descriptor + ")V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
Label l1 = new Label();
mv.visitLabel(l1);
for (InDefinedShape method : declaredMethods)
{
String methodName = method.getName();
TypeDescription returnType = method.getReturnType().asErasure();
String returnTypeDescriptor = returnType.getDescriptor();
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 1);
mv.visitMethodInsn(INVOKEINTERFACE, internalName, methodName, "()" + returnTypeDescriptor, true);
mv.visitFieldInsn(PUTFIELD, internalImplName, methodName, returnTypeDescriptor);
}
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, internalImplName, "$_initHashCode", "()I", false);
mv.visitFieldInsn(PUTFIELD, internalImplName, "$_hashcode", "I");
Label l8 = new Label();
mv.visitLabel(l8);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, internalImplName, "$_initToString", "()Ljava/lang/String;", false);
mv.visitFieldInsn(PUTFIELD, internalImplName, "$_toString", "Ljava/lang/String;");
Label l9 = new Label();
mv.visitLabel(l9);
mv.visitInsn(RETURN);
Label l10 = new Label();
mv.visitLabel(l10);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l10, 0);
mv.visitLocalVariable("data", descriptor, null, l0, l10, 1);
mv.visitMaxs(3, 2);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PUBLIC + ACC_STATIC, "unproxy", "(" + descriptor + ")" + descriptorImplName, null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitTypeInsn(INSTANCEOF, internalImplName);
Label l1 = new Label();
mv.visitJumpInsn(IFEQ, l1);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitVarInsn(ALOAD, 0);
mv.visitTypeInsn(CHECKCAST, internalImplName);
mv.visitInsn(ARETURN);
mv.visitLabel(l1);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitTypeInsn(NEW, internalImplName);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, internalImplName, "<init>", "(" + descriptor + ")V", false);
mv.visitInsn(ARETURN);
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitLocalVariable("ann", descriptor, null, l0, l3, 0);
mv.visitMaxs(3, 1);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PRIVATE, "$_initHashCode", "()I", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ISTORE, 1);
Label l1 = new Label();
for (InDefinedShape method : declaredMethods)
{
String methodName = method.getName();
TypeDescription returnType = method.getReturnType().asErasure();
String returnTypeDescriptor = returnType.getDescriptor();
Label labelX = new Label();
mv.visitLabel(labelX);
mv.visitVarInsn(ILOAD, 1);
mv.visitIntInsn(BIPUSH, HASHCODE_MULTI);
mv.visitLdcInsn(methodName);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "hashCode", "()I", false);
mv.visitInsn(IMUL);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalImplName, methodName, returnTypeDescriptor);
if (returnType.isPrimitive())
{
TypeDescription wrapperClass = AsmUtils.getWrapperClass(returnType);
mv.visitMethodInsn(INVOKESTATIC, wrapperClass.getInternalName(), "valueOf",
"(" + returnTypeDescriptor + ")" + wrapperClass.getDescriptor(), false);
mv.visitMethodInsn(INVOKEVIRTUAL, wrapperClass.getInternalName(), "hashCode", "()I", false);
}
else if (returnType.isArray())
{
if (returnType.getComponentType().isPrimitive())
{
mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "hashCode", "(" + returnTypeDescriptor + ")I", false);
}
else
{
mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "hashCode", "([Ljava/lang/Object;)I", false);
}
}
else
{
mv.visitMethodInsn(INVOKEVIRTUAL, returnType.getInternalName(), "hashCode", "()I", false);
}
mv.visitInsn(IXOR);
mv.visitInsn(IADD);
mv.visitVarInsn(ISTORE, 1);
}
mv.visitVarInsn(ILOAD, 1);
mv.visitInsn(IRETURN);
Label l7 = new Label();
mv.visitLabel(l7);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l7, 0);
mv.visitLocalVariable("sum", "I", null, l1, l7, 1);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PUBLIC, "hashCode", "()I", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalImplName, "$_hashcode", "I");
mv.visitInsn(IRETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l1, 0);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PUBLIC, "equals", "(Ljava/lang/Object;)Z", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(INSTANCEOF, internalName);
Label l1 = new Label();
mv.visitJumpInsn(IFNE, l1);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitInsn(ICONST_0);
mv.visitInsn(IRETURN);
mv.visitLabel(l1);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, internalName);
mv.visitVarInsn(ASTORE, 2);
Label l3 = new Label();
mv.visitLabel(l3);
Label l4 = new Label();
for (InDefinedShape method : declaredMethods)
{
String methodName = method.getName();
TypeDescription returnType = method.getReturnType().asErasure();
String returnTypeDescriptor = returnType.getDescriptor();
boolean isPrimitive = returnType.isPrimitive();
boolean isFloat =
isPrimitive && (returnType.getName().equals(float.class.getName()) || returnType.getName().equals(double.class.getName()));
TypeDescription wrapperClass = AsmUtils.getWrapperClass(returnType);
String wrapperClassInternalName = wrapperClass.getInternalName();
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalImplName, methodName, returnTypeDescriptor);
if (isFloat)
{
mv.visitMethodInsn(INVOKESTATIC, wrapperClassInternalName, "valueOf",
"(" + returnTypeDescriptor + ")" + wrapperClass.getDescriptor(), false);
}
mv.visitVarInsn(ALOAD, 2);
mv.visitMethodInsn(INVOKEINTERFACE, internalName, methodName, "()" + returnTypeDescriptor, true);
if (isPrimitive && ! isFloat)
{
if (returnType.getName().equals(long.class.getName()))
{
mv.visitInsn(LCMP);
mv.visitJumpInsn(IFNE, l4);
}
else
{
mv.visitJumpInsn(IF_ICMPNE, l4);
}
}
else
{
if (isFloat)
{
mv.visitMethodInsn(INVOKESTATIC, wrapperClassInternalName, "valueOf",
"(" + returnTypeDescriptor + ")" + wrapperClass.getDescriptor(), false);
}
if (returnType.isArray())
{
TypeDescription componentType = returnType.getComponentType();
if (componentType.isPrimitive())
{
mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "equals",
"(" + returnType.getDescriptor() + "" + returnType.getDescriptor() + ")Z", false);
}
else
{
mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "equals", "([Ljava/lang/Object;[Ljava/lang/Object;)Z", false);
}
}
else
{
mv.visitMethodInsn(INVOKEVIRTUAL, wrapperClassInternalName, "equals", "(Ljava/lang/Object;)Z", false);
}
mv.visitJumpInsn(IFEQ, l4);
}
}
mv.visitInsn(ICONST_1);
Label l6 = new Label();
mv.visitJumpInsn(GOTO, l6);
mv.visitLabel(l4);
mv.visitFrame(F_APPEND, 1, new Object[]{internalName}, 0, null);
mv.visitInsn(ICONST_0);
mv.visitLabel(l6);
mv.visitFrame(F_SAME1, 0, null, 1, new Object[]{INTEGER});
mv.visitInsn(IRETURN);
Label l7 = new Label();
mv.visitLabel(l7);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l7, 0);
mv.visitLocalVariable("o", "Ljava/lang/Object;", null, l0, l7, 1);
mv.visitLocalVariable("other", descriptor, null, l3, l7, 2);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PRIVATE, "$_initToString", "()Ljava/lang/String;", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
mv.visitInsn(DUP);
//noinspection MagicNumber
mv.visitIntInsn(SIPUSH, methodsSize * 30);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(I)V", false);
mv.visitIntInsn(BIPUSH, '@');
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(C)Ljava/lang/StringBuilder;", false);
mv.visitLdcInsn(Type.getType(descriptor));
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
mv.visitIntInsn(BIPUSH, '(');
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(C)Ljava/lang/StringBuilder;", false);
for (Iterator<InDefinedShape> iterator = declaredMethods.iterator(); iterator.hasNext(); )
{
InDefinedShape method = iterator.next();
String methodName = method.getName();
TypeDescription returnType = method.getReturnType().asErasure();
String returnTypeDescriptor = returnType.getDescriptor();
mv.visitLdcInsn(methodName);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
mv.visitIntInsn(BIPUSH, '=');
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(C)Ljava/lang/StringBuilder;", false);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalImplName, methodName, returnTypeDescriptor);
if (returnType.isArray())
{
TypeDescription componentType = returnType.getComponentType();
if (componentType.isPrimitive())
{
mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "toString", "(" + returnTypeDescriptor + ")Ljava/lang/String;", false);
}
else
{
mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "toString", "([Ljava/lang/Object;)Ljava/lang/String;", false);
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
}
else if (returnType.isPrimitive())
{
String type = returnType.getName();
if (type.equals(double.class.getName()) || type.equals(long.class.getName()) || type.equals(float.class.getName()) ||
type.equals(boolean.class.getName()) || type.equals(char.class.getName()))
{
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(" + returnTypeDescriptor + ")Ljava/lang/StringBuilder;",
false);
}
else
{
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", false);
}
}
else
{
if (! returnType.equals(ForLoadedType.STRING))
{
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false);
}
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
false);
}
if (iterator.hasNext())
{
mv.visitLdcInsn(", ");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
false);
}
}
mv.visitIntInsn(BIPUSH, ')');
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(C)Ljava/lang/StringBuilder;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
Label l4 = new Label();
mv.visitLabel(l4);
mv.visitInsn(ARETURN);
Label l5 = new Label();
mv.visitLabel(l5);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l5, 0);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, internalImplName, "$_toString", "Ljava/lang/String;");
mv.visitInsn(ARETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l1, 0);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_PUBLIC, "annotationType", "()Ljava/lang/Class;", "()Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;",
null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLdcInsn(Type.getType(descriptor));
mv.visitInsn(ARETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", descriptorImplName, null, l0, l1, 0);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
{
mv = cv.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLdcInsn(Type.getType(descriptor));
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getDeclaredMethods", "()[Ljava/lang/reflect/Method;", false);
mv.visitVarInsn(ASTORE, 0);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitTypeInsn(NEW, "java/util/HashMap");
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitInsn(ARRAYLENGTH);
mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "(I)V", false);
mv.visitFieldInsn(PUTSTATIC, internalImplName, "DEFAULTS", "Ljava/util/Map;");
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitVarInsn(ALOAD, 0);
mv.visitVarInsn(ASTORE, 1);
mv.visitVarInsn(ALOAD, 1);
mv.visitInsn(ARRAYLENGTH);
mv.visitVarInsn(ISTORE, 2);
mv.visitInsn(ICONST_0);
mv.visitVarInsn(ISTORE, 3);
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitFrame(F_FULL, 4, new Object[]{"[Ljava/lang/reflect/Method;", "[Ljava/lang/reflect/Method;", INTEGER, INTEGER}, 0, STACK);
mv.visitVarInsn(ILOAD, 3);
mv.visitVarInsn(ILOAD, 2);
Label l4 = new Label();
mv.visitJumpInsn(IF_ICMPGE, l4);
mv.visitVarInsn(ALOAD, 1);
mv.visitVarInsn(ILOAD, 3);
mv.visitInsn(AALOAD);
mv.visitVarInsn(ASTORE, 4);
Label l5 = new Label();
mv.visitLabel(l5);
mv.visitVarInsn(ALOAD, 4);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Method", "getDefaultValue", "()Ljava/lang/Object;", false);
mv.visitVarInsn(ASTORE, 5);
Label l6 = new Label();
mv.visitLabel(l6);
mv.visitVarInsn(ALOAD, 5);
Label l7 = new Label();
mv.visitJumpInsn(IFNULL, l7);
Label l8 = new Label();
mv.visitLabel(l8);
mv.visitFieldInsn(GETSTATIC, internalImplName, "DEFAULTS", "Ljava/util/Map;");
mv.visitVarInsn(ALOAD, 4);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Method", "getName", "()Ljava/lang/String;", false);
mv.visitVarInsn(ALOAD, 5);
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
mv.visitInsn(POP);
mv.visitLabel(l7);
mv.visitFrame(F_SAME, 0, null, 0, null);
mv.visitIincInsn(3, 1);
mv.visitJumpInsn(GOTO, l3);
mv.visitLabel(l4);
mv.visitFrame(F_CHOP, 3, null, 0, null);
mv.visitLdcInsn(Type.getType(descriptor));
//noinspection deprecation
mv.visitInvokeDynamicInsn("apply", "()Ljava/util/function/Function;",
new Handle(H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;" +
"Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)" +
"Ljava/lang/invoke/CallSite;"),
Type.getType("(Ljava/lang/Object;)Ljava/lang/Object;"),
new Handle(H_NEWINVOKESPECIAL, internalImplName, "<init>", "(Ljava/util/Map;)V"),
Type.getType("(Ljava/util/Map;)" + descriptor));
//noinspection deprecation
mv.visitInvokeDynamicInsn("apply", "()Ljava/util/function/Function;",
new Handle(H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;" +
"Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)" +
"Ljava/lang/invoke/CallSite;"),
Type.getType("(Ljava/lang/Object;)Ljava/lang/Object;"),
new Handle(H_INVOKESTATIC, internalImplName, "unproxy", "(" + descriptor + ")" + descriptorImplName),
Type.getType("(" + descriptor + ")" + descriptor));
mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Qualifiers.class), "register",
"(Ljava/lang/Class;Ljava/util/function/Function;Ljava/util/function/Function;)V", false);
Label l9 = new Label();
mv.visitLabel(l9);
mv.visitInsn(RETURN);
mv.visitLocalVariable("defaultValue", "Ljava/lang/Object;", null, l6, l7, 5);
mv.visitLocalVariable("method", "Ljava/lang/reflect/Method;", null, l5, l7, 4);
mv.visitLocalVariable("methods", "[Ljava/lang/reflect/Method;", null, l1, l9, 0);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
cv.visitEnd();
return cv;
}
}
}