org.diorite.inject.impl.utils.AsmUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.diorite.inject.impl.utils.AsmUtils.java

Source

/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016. Diorite (by Bartomiej 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);
        }
    }
}