kr.debop4j.core.reflect.ConstructorAccess.java Source code

Java tutorial

Introduction

Here is the source code for kr.debop4j.core.reflect.ConstructorAccess.java

Source

/*
 * Copyright 2011-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package kr.debop4j.core.reflect;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;

import java.lang.reflect.Modifier;

import static kr.debop4j.core.Guard.shouldNotBeNull;
import static org.objectweb.asm.Opcodes.*;

/**
 * ?? ??? , ?? ?  ?? .
 *
 * @author ? ( sunghyouk.bae@gmail.com )
 * @since 13. 1. 21
 */
@Slf4j
public abstract class ConstructorAccess<T> {

    @Getter
    boolean nonStaticMemberClass;

    /** Constructor for top-level classes and static nested classes. */
    public abstract T newInstance();

    /** Constructor for inner classes (non-static nested classes) - except static nested classes */
    public abstract T newInstance(Object enclosingInstance);

    private static final Object syncLock = new Object();

    /**  ? ???  ? ?. */
    @SuppressWarnings("unchecked")
    public static <T> ConstructorAccess<T> get(Class<T> type) {
        shouldNotBeNull(type, "type");

        log.trace("[{}]? ???  ? .", type.getName());

        Class enclosingType = type.getEnclosingClass();
        boolean isNonStaticMemberClass = enclosingType != null && type.isMemberClass()
                && !Modifier.isStatic(type.getModifiers());

        String className = type.getName();
        String accessClassName = className + "ConstructorAccess";
        if (accessClassName.startsWith("java."))
            accessClassName = ReflectConsts.BASE_PACKAGE + "." + accessClassName;

        Class accessClass = null;
        AccessClassLoader loader = AccessClassLoader.get(type);
        shouldNotBeNull(loader, "loader");

        synchronized (syncLock) {
            try {
                accessClass = loader.loadClass(accessClassName);
            } catch (ClassNotFoundException ignored) {
                String accessClassNameInternal = accessClassName.replace('.', '/');
                String classNameInternal = className.replace('.', '/');
                String enclosingClassNameInternal;

                if (!isNonStaticMemberClass) {
                    enclosingClassNameInternal = null;
                    try {
                        type.getConstructor((Class[]) null);
                    } catch (Exception ex) {
                        throw new RuntimeException("[" + type.getName()
                                + "] ? ? .  ??  .",
                                ex);
                    }
                } else {
                    enclosingClassNameInternal = enclosingType.getName().replace('.', '/');
                    try {
                        type.getConstructor(enclosingType); // Inner classes should have this.
                    } catch (Exception ex) {
                        throw new RuntimeException(
                                "Non-static member class  ?  .  ?? . type="
                                        + type.getName(),
                                ex);
                    }
                }

                ClassWriter cw = new ClassWriter(0);
                cw.visit(V1_1, ACC_PUBLIC + ACC_SUPER, accessClassNameInternal, null,
                        ReflectConsts.CONSTRUCTOR_ACCESS_PATH, null);

                insertConstructor(cw);
                insertNewInstance(cw, classNameInternal);
                insertNewInstanceInner(cw, classNameInternal, enclosingClassNameInternal);

                cw.visitEnd();
                accessClass = loader.defineClass(accessClassName, cw.toByteArray());
            }
        }

        try {
            ConstructorAccess<T> access = (ConstructorAccess<T>) accessClass.newInstance();
            access.nonStaticMemberClass = isNonStaticMemberClass;

            log.trace(
                    " ???   ConstructorAccess  . accessClassName=[{}]",
                    accessClassName);
            return access;
        } catch (Exception ex) {
            throw new RuntimeException("Error constructing constructor access class: [" + accessClassName + "]",
                    ex);
        }
    }

    /** ?? . */
    static private void insertConstructor(ClassWriter cw) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, ReflectConsts.CONSTRUCTOR_ACCESS_PATH, "<init>", "()V");
        mv.visitInsn(RETURN);
        mv.visitMaxs(1, 1);
        mv.visitEnd();
    }

    /**  ? . */
    static void insertNewInstance(ClassWriter cw, String classNameInternal) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "()Ljava/lang/Object;", null, null);
        mv.visitCode();
        mv.visitTypeInsn(NEW, classNameInternal);
        mv.visitInsn(DUP);
        mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "<init>", "()V");
        mv.visitInsn(ARETURN);
        mv.visitMaxs(2, 1);
        mv.visitEnd();
    }

    /** inner ?  */
    static void insertNewInstanceInner(ClassWriter cw, String classNameInternal,
            String enclosingClassNameInternal) {
        MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "newInstance", "(Ljava/lang/Object;)Ljava/lang/Object;", null,
                null);
        mv.visitCode();
        if (enclosingClassNameInternal != null) {
            mv.visitTypeInsn(NEW, classNameInternal);
            mv.visitInsn(DUP);
            mv.visitVarInsn(ALOAD, 1);
            mv.visitTypeInsn(CHECKCAST, enclosingClassNameInternal);
            mv.visitInsn(DUP);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
            mv.visitInsn(POP);
            mv.visitMethodInsn(INVOKESPECIAL, classNameInternal, "<init>",
                    "(L" + enclosingClassNameInternal + ";)V");
            mv.visitInsn(ARETURN);
            mv.visitMaxs(4, 2);
        } else {
            mv.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException");
            mv.visitInsn(DUP);
            mv.visitLdcInsn("Not an inner class.");
            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException", "<init>",
                    "(Ljava/lang/String;)V");
            mv.visitInsn(ATHROW);
            mv.visitMaxs(3, 2);
        }
        mv.visitEnd();
    }
}