/*
* 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.utils;
import javax.annotation.Nullable;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.annotation.RetentionPolicy;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.TraceClassVisitor;
import net.bytebuddy.description.annotation.AnnotatedCodeElement;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.annotation.AnnotationList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeDescription.ForLoadedType;
@SuppressWarnings("Duplicates")
public final class AsmUtils implements Opcodes
{
public static final TypeDescription.ForLoadedType VOID = new TypeDescription.ForLoadedType(Void.class);
public static final TypeDescription.ForLoadedType BOOLEAN = new TypeDescription.ForLoadedType(Boolean.class);
public static final TypeDescription.ForLoadedType BYTE = new TypeDescription.ForLoadedType(Byte.class);
public static final TypeDescription.ForLoadedType SHORT = new TypeDescription.ForLoadedType(Short.class);
public static final TypeDescription.ForLoadedType CHARACTER = new TypeDescription.ForLoadedType(Character.class);
public static final TypeDescription.ForLoadedType INTEGER = new TypeDescription.ForLoadedType(Integer.class);
public static final TypeDescription.ForLoadedType LONG = new TypeDescription.ForLoadedType(Long.class);
public static final TypeDescription.ForLoadedType FLOAT = new TypeDescription.ForLoadedType(Float.class);
public static final TypeDescription.ForLoadedType DOUBLE = new TypeDescription.ForLoadedType(Double.class);
public static final TypeDescription.ForLoadedType VOID_P = new TypeDescription.ForLoadedType(void.class);
public static final TypeDescription.ForLoadedType BOOLEAN_P = new TypeDescription.ForLoadedType(boolean.class);
public static final TypeDescription.ForLoadedType BYTE_P = new TypeDescription.ForLoadedType(byte.class);
public static final TypeDescription.ForLoadedType SHORT_P = new TypeDescription.ForLoadedType(short.class);
public static final TypeDescription.ForLoadedType CHAR_P = new TypeDescription.ForLoadedType(char.class);
public static final TypeDescription.ForLoadedType INT_P = new TypeDescription.ForLoadedType(int.class);
public static final TypeDescription.ForLoadedType LONG_P = new TypeDescription.ForLoadedType(long.class);
public static final TypeDescription.ForLoadedType FLOAT_P = new TypeDescription.ForLoadedType(float.class);
public static final TypeDescription.ForLoadedType DOUBLE_P = new TypeDescription.ForLoadedType(double.class);
@Nullable
static final MethodHandle typeHandle;
static
{
MethodHandle r;
try
{
Field typeField = ForLoadedType.class.getDeclaredField("type");
typeField.setAccessible(true);
r = MethodHandles.lookup().unreflectGetter(typeField);
}
catch (Exception e)
{
e.printStackTrace();
r = null;
}
typeHandle = r;
}
private AsmUtils()
{
}
public static void printBytecodeSource(ClassWriter classWriter, OutputStream outputStream)
{
ClassReader cr = new ClassReader(classWriter.toByteArray());
cr.accept(new TraceClassVisitor(new PrintWriter(outputStream)), 0);
}
public static boolean isPutField(int code)
{
switch (code)
{
case PUTFIELD:
case PUTSTATIC:
return true;
default:
return false;
}
}
public static boolean isInvokeCode(int code)
{
switch (code)
{
case INVOKEDYNAMIC:
case INVOKEINTERFACE:
case INVOKESPECIAL:
case INVOKESTATIC:
case INVOKEVIRTUAL:
return true;
default:
return false;
}
}
public static boolean isLoadCode(int code)
{
switch (code)
{
case ALOAD:
case ILOAD:
case LLOAD:
case FLOAD:
case DLOAD:
return true;
default:
return false;
}
}
public static boolean isStoreCode(int code)
{
switch (code)
{
case ASTORE:
case ISTORE:
case LSTORE:
case FSTORE:
case DSTORE:
return true;
default:
return false;
}
}
public static boolean isReturnCode(int code)
{
switch (code)
{
case RETURN:
case ARETURN:
case IRETURN:
case LRETURN:
case FRETURN:
case DRETURN:
return true;
default:
return false;
}
}
public static int getReturnCode(TypeDescription fieldType)
{
if (fieldType.isPrimitive())
{
if (BOOLEAN_P.equals(fieldType) || BYTE_P.equals(fieldType) || CHAR_P.equals(fieldType) ||
SHORT_P.equals(fieldType) || INT_P.equals(fieldType))
{
return IRETURN;
}
if (LONG_P.equals(fieldType))
{
return LRETURN;
}
if (FLOAT_P.equals(fieldType))
{
return FRETURN;
}
if (DOUBLE_P.equals(fieldType))
{
return DRETURN;
}
else
{
throw new IllegalStateException("Unknown store method");
}
}
else
{
return ARETURN;
}
}
public static int getStoreCode(TypeDescription fieldType)
{
if (fieldType.isPrimitive())
{
if (BOOLEAN_P.equals(fieldType) || BYTE_P.equals(fieldType) || CHAR_P.equals(fieldType) ||
SHORT_P.equals(fieldType) || INT_P.equals(fieldType))
{
return ISTORE;
}
if (LONG_P.equals(fieldType))
{
return LSTORE;
}
if (FLOAT_P.equals(fieldType))
{
return FSTORE;
}
if (DOUBLE_P.equals(fieldType))
{
return DSTORE;
}
else
{
throw new IllegalStateException("Unknown store method");
}
}
else
{
return ASTORE;
}
}
public static int getLoadCode(TypeDescription fieldType)
{
if (fieldType.isPrimitive())
{
if (BOOLEAN_P.equals(fieldType) || BYTE_P.equals(fieldType) || CHAR_P.equals(fieldType) ||
SHORT_P.equals(fieldType) || INT_P.equals(fieldType))
{
return ILOAD;
}
if (LONG_P.equals(fieldType))
{
return LLOAD;
}
if (FLOAT_P.equals(fieldType))
{
return FLOAD;
}
if (DOUBLE_P.equals(fieldType))
{
return DLOAD;
}
else
{
throw new IllegalStateException("Unknown load method");
}
}
else
{
return ALOAD;
}
}
public static int printLineNumber(MethodVisitor mv, int lineNumber)
{
if (lineNumber == - 1)
{
return - 1;
}
Label label = new Label();
mv.visitLabel(label);
mv.visitLineNumber(lineNumber, label);
return lineNumber + 1;
}
public static void storeInt(MethodVisitor mv, int i)
{
switch (i)
{
case 0:
mv.visitInsn(ICONST_0);
break;
case 1:
mv.visitInsn(ICONST_1);
break;
case 2:
mv.visitInsn(ICONST_2);
break;
case 3:
mv.visitInsn(ICONST_3);
break;
case 4:
mv.visitInsn(ICONST_4);
break;
default:
mv.visitIntInsn(BIPUSH, i);
break;
}
}
/**
* If given type is primitive type {@link TypeDescription#isPrimitive()} then it will return
* wrapper type for it. Like: boolean.class {@literal ->} Boolean.class
* If given type isn't primitive, then it will return given type.
*
* @param type
* type to get wrapper of it.
*
* @return non-primitive type.
*/
public static TypeDescription getWrapperClass(TypeDescription type)
{
if (! type.isPrimitive())
{
return type;
}
if (type.equals(BOOLEAN_P))
{
return BOOLEAN;
}
if (type.equals(BYTE_P))
{
return BYTE;
}
if (type.equals(SHORT_P))
{
return SHORT;
}
if (type.equals(CHAR_P))
{
return CHARACTER;
}
if (type.equals(INT_P))
{
return INTEGER;
}
if (type.equals(LONG_P))
{
return LONG;
}
if (type.equals(FLOAT_P))
{
return FLOAT;
}
if (type.equals(DOUBLE_P))
{
return DOUBLE;
}
if (type.equals(VOID_P))
{
return VOID;
}
throw new Error("Unknown primitive type?"); // not possible?
}
public static AnnotationList getAnnotationList(AnnotatedCodeElement element)
{
if (element instanceof TypeDescription)
{
return ((TypeDescription) element).getInheritedAnnotations();
}
else
{
return element.getDeclaredAnnotations();
}
}
public static Annotation[] getAnnotations(AnnotatedCodeElement element, RetentionPolicy... policies)
{
Set<RetentionPolicy> policySet = Set.of(policies);
AnnotationList annotationList = getAnnotationList(element);
return getAnnotations(annotationList.visibility(policySet::contains));
}
@SuppressWarnings({"unchecked", "rawtypes"})
public static Annotation[] getAnnotations(Collection<AnnotationDescription> annotations)
{
Collection<Annotation> col = new ArrayList<>(annotations.size());
for (AnnotationDescription annotation : annotations)
{
TypeDescription annotationType = annotation.getAnnotationType();
try
{
Class<?> forName = Class.forName(annotationType.getActualName());
if (! forName.isAnnotation())
{
continue;
}
col.add(annotation.prepare((Class) forName).load());
}
catch (ClassNotFoundException ignored)
{
}
}
return col.toArray(new Annotation[col.size()]);
}
@SuppressWarnings("unchecked")
public static <T> Class<T> toClass(TypeDescription type)
{
if ((type instanceof TypeDescription.ForLoadedType) && (typeHandle != null))
{
try
{
return (Class<T>) typeHandle.invoke(type);
}
catch (Throwable throwable)
{
throwable.printStackTrace();
}
}
ClassLoader classLoader = StackWalker.getInstance().getCallerClass().getClassLoader();
try
{
return (Class<T>) Class.forName(type.getActualName(), false, classLoader);
}
catch (ClassNotFoundException e)
{
throw new RuntimeException(e);
}
}
}