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

webit.script.asm.AsmNativeFactory Maven / Gradle / Ivy

// Copyright (c) 2013, Webit Team. All Rights Reserved.
package webit.script.asm;

import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import webit.script.asm.lib.ClassWriter;
import webit.script.asm.lib.Constants;
import webit.script.asm.lib.Label;
import webit.script.asm.lib.MethodWriter;
import webit.script.core.NativeFactory;
import webit.script.lang.MethodDeclare;
import webit.script.util.ClassUtil;

/**
 *
 * @author zqq
 */
public class AsmNativeFactory extends NativeFactory {

    private static final String[] METHOD_DECLARE = new String[]{"webit/script/lang/MethodDeclare"};

    @Override
    protected MethodDeclare createNativeConstructorDeclare(Constructor constructor) {
        MethodDeclare accessor = createMethodDeclare(constructor);
        if (accessor != null) {
            return accessor;
        }
        return super.createNativeConstructorDeclare(constructor);
    }

    @Override
    protected MethodDeclare createNativeMethodDeclare(Method method) {
        MethodDeclare accessor = createMethodDeclare(method);
        if (accessor != null) {
            return accessor;
        }
        return super.getNativeMethodDeclare(method);
    }

    protected MethodDeclare createMethodDeclare(Member member) {
        if (ClassUtil.isPublic(member.getDeclaringClass()) && ClassUtil.isPublic(member)) {
            MethodDeclare declare = CACHE.get(member);
            if (declare == null) {
                synchronized (CACHE) {
                    try {
                        declare = CACHE.get(member);
                        if (declare == null) {
                            declare = createAccessor(member);
                            CACHE.put(member, declare);
                        }
                    } catch (Exception e) {
                        logger.error("Failed to create ASMMethodDeclare for '{}'.", member, e);
                    } catch (Error e) {
                        logger.error("Failed to create ASMMethodDeclare for '{}'.", member, e);
                    }
                }
            }
            return declare;
        }
        return null;
    }

    static MethodDeclare createAccessor(Member obj) throws InstantiationException, IllegalAccessException {
        final String className = "webit.script.asm.Accessor".concat(ASMUtil.getSn());
        final ClassWriter classWriter = new ClassWriter(Constants.V1_5, Constants.ACC_PUBLIC + Constants.ACC_FINAL, ASMUtil.getInternalName(className), "java/lang/Object", METHOD_DECLARE);

        ASMUtil.visitConstructor(classWriter);

        final boolean isInterface;
        final boolean isStatic;
        final boolean isConstructor;
        final String ownerClass;
        final String destName;
        final String destDesc;
        final Class[] paramTypes;
        final Class returnType;

        if (obj instanceof Method) {
            Method method = (Method) obj;
            isInterface = method.getDeclaringClass().isInterface();
            isStatic = ClassUtil.isStatic(method);
            isConstructor = false;
            ownerClass = ASMUtil.getInternalName(method.getDeclaringClass().getName());
            destName = method.getName();
            destDesc = ASMUtil.getDescriptor(method);
            paramTypes = method.getParameterTypes();
            returnType = method.getReturnType();
        } else {
            Constructor constructor = (Constructor) obj;
            isInterface = false;
            isStatic = false;
            isConstructor = true;
            ownerClass = ASMUtil.getInternalName(constructor.getDeclaringClass().getName());
            destName = "";
            destDesc = ASMUtil.getDescriptor(constructor);
            paramTypes = constructor.getParameterTypes();
            returnType = constructor.getDeclaringClass();
        }

        final int paramTypesLen = paramTypes.length;
        final MethodWriter m = classWriter.visitMethod(Constants.ACC_PUBLIC, "invoke", "(Lwebit/script/Context;[Ljava/lang/Object;)Ljava/lang/Object;", null);

        if (paramTypesLen == 0) {
            if (isStatic) {
                m.invokeStatic(ownerClass, destName, destDesc);
                ASMUtil.visitBoxIfNeed(m, returnType);
                m.visitInsn(Constants.ARETURN);
            } else if (isConstructor) {
                m.visitTypeInsn(Constants.NEW, ownerClass);
                m.visitInsn(Constants.DUP);
                m.visitMethodInsn(Constants.INVOKESPECIAL, ownerClass, "", "()V");
                m.visitInsn(Constants.ARETURN);
            } else {
                Label toExcaption = new Label();
                m.visitVarInsn(Constants.ALOAD, 2);
                m.visitJumpInsn(Constants.IFNULL, toExcaption);
                m.visitVarInsn(Constants.ALOAD, 2);
                m.visitInsn(Constants.ARRAYLENGTH);
                m.visitJumpInsn(Constants.IFEQ, toExcaption);
                m.visitVarInsn(Constants.ALOAD, 2);
                m.visitInsn(Constants.ICONST_0);
                m.visitInsn(Constants.AALOAD);
                m.visitJumpInsn(Constants.IFNULL, toExcaption);
                m.visitVarInsn(Constants.ALOAD, 2);
                m.visitInsn(Constants.ICONST_0);
                m.visitInsn(Constants.AALOAD);
                m.checkCast(ownerClass);
                m.visitMethodInsn(isInterface ? Constants.INVOKEINTERFACE
                        : Constants.INVOKEVIRTUAL, ownerClass, destName, destDesc);
                ASMUtil.visitBoxIfNeed(m, returnType);
                m.visitInsn(Constants.ARETURN);
                m.visitLabel(toExcaption);
                ASMUtil.visitScriptRuntimeException(m, "First argument can't be null.");
            }
        } else {
            if (isConstructor) {
                m.visitTypeInsn(Constants.NEW, ownerClass);
                m.visitInsn(Constants.DUP);
            }

            m.visitVarInsn(Constants.ALOAD, 2);

            m.push(isStatic || isConstructor ? paramTypesLen : paramTypesLen + 1);
            m.invokeStatic("webit/script/util/ArrayUtil", "ensureMinSize", "([Ljava/lang/Object;I)[Ljava/lang/Object;");
            m.visitVarInsn(Constants.ASTORE, 2);

            int paramCount = 0;
            if (!isStatic && !isConstructor) {
                m.visitVarInsn(Constants.ALOAD, 2);
                m.visitInsn(Constants.ICONST_0);
                m.visitInsn(Constants.AALOAD);
                m.checkCast(ownerClass);
                paramCount++;
            }

            for (Class paramType : paramTypes) {
                m.visitVarInsn(Constants.ALOAD, 2);
                m.push(paramCount);
                m.visitInsn(Constants.AALOAD);
                m.checkCast(ASMUtil.getBoxedInternalName(paramType));
                ASMUtil.visitUnboxIfNeed(m, paramType);
                paramCount++;
            }

            //Invoke Method
            m.visitMethodInsn(isStatic ? Constants.INVOKESTATIC
                    : isConstructor ? Constants.INVOKESPECIAL : isInterface ? Constants.INVOKEINTERFACE
                    : Constants.INVOKEVIRTUAL, ownerClass, destName, destDesc);
            ASMUtil.visitBoxIfNeed(m, returnType);
            m.visitInsn(Constants.ARETURN);
        }
        m.visitMaxs();

        return (MethodDeclare) ASMUtil.loadClass(className, classWriter).newInstance();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy