asmclassgenerator.java

来自「Groovy动态语言 运行在JVM中的动态语言 可以方便的处理业务逻辑变化大的业」· Java 代码 · 共 1,558 行 · 第 1/5 页

JAVA
1,558
字号
                internalBaseClassName,
                BytecodeHelper.getClassInternalNames(classNode.getInterfaces())
            );            
            cw.visitSource(sourceFile,null);
            
            if (classNode.isInterface()) {
                ClassNode owner = classNode;
                if (owner instanceof InnerClassNode) {
                    owner = owner.getOuterClass();
                }
                String outerClassName = owner.getName();
                String name = outerClassName + "$" + context.getNextInnerClassIdx();
                interfaceClassLoadingClass = new InnerClassNode(owner, name, 4128, ClassHelper.OBJECT_TYPE);
                
                super.visitClass(classNode);
                createInterfaceSyntheticStaticFields();                
            } else {
                super.visitClass(classNode);
                createMopMethods();
                createSyntheticStaticFields();
            }
            
            for (Iterator iter = innerClasses.iterator(); iter.hasNext();) {
                ClassNode innerClass = (ClassNode) iter.next();
                String innerClassName = innerClass.getName();
                String innerClassInternalName = BytecodeHelper.getClassInternalName(innerClassName);
                {
                    int index = innerClassName.lastIndexOf('$');
                    if (index>=0) innerClassName = innerClassName.substring(index+1);
                }
                String outerClassName = internalClassName; // default for inner classes
                MethodNode enclosingMethod = innerClass.getEnclosingMethod();
                if (enclosingMethod != null) {
                    // local inner classes do not specify the outer class name
                    outerClassName = null;
                    innerClassName = null;
                }
                cw.visitInnerClass(
                    innerClassInternalName,
                    outerClassName,
                    innerClassName,
                    innerClass.getModifiers());
            }
            //TODO: an inner class should have an entry of itself
            cw.visitEnd();
        }
        catch (GroovyRuntimeException e) {
            e.setModule(classNode.getModule());
            throw e;
        }
    }
   
    private void createMopMethods() {
        visitMopMethodList(classNode.getMethods(), true);
        visitMopMethodList(classNode.getSuperClass().getAllDeclaredMethods(), false);
    }

    private String[] buildExceptions(ClassNode[] exceptions) {
        if (exceptions==null) return null;
        String[] ret = new String[exceptions.length];
        for (int i = 0; i < exceptions.length; i++) {
            ret[i] = BytecodeHelper.getClassInternalName(exceptions[i]);
        }
        return ret;
    }
    
    /**
     * filters a list of method for MOP methods. For all methods that are no 
     * MOP methods a MOP method is created if the method is not public and the
     * call would be a call on "this" (isThis == true). If the call is not on
     * "this", then the call is a call on "super" and all methods are used, 
     * unless they are already a MOP method
     *  
     * @see #generateMopCalls(LinkedList, boolean)
     *  
     * @param methods unfiltered list of methods for MOP 
     * @param isThis  if true, then we are creating a MOP method on "this", "super" else 
     */
    private void visitMopMethodList(List methods, boolean isThis){
        LinkedList mopCalls = new LinkedList();
        for (Iterator iter = methods.iterator(); iter.hasNext();) {
            MethodNode mn = (MethodNode) iter.next();
            if ((mn.getModifiers() & ACC_ABSTRACT) !=0 ) continue;
            // no this$ methods for protected/public isThis=true
            // super$ method for protected/public isThis=false
            // --> results in XOR
            if (isThis ^ (mn.getModifiers() & (ACC_PUBLIC|ACC_PROTECTED)) == 0) continue; 
            String methodName = mn.getName();
            if (isMopMethod(methodName) || methodName.startsWith("<")) continue;
            String name = getMopMethodName(mn,isThis);
            if (containsMethod(methods,name,mn.getParameters())) continue;
            mopCalls.add(mn);
        }
        generateMopCalls(mopCalls, isThis);
        mopCalls.clear();
    }
    
    private boolean containsMethod(List methods, String name, Parameter[] paras) {
        for (Iterator iter = methods.iterator(); iter.hasNext();) {
            MethodNode element = (MethodNode) iter.next();
            if (element.getName().equals(name) && equalParameterTypes(paras,element.getParameters())) return true;
        }
        return false;
    }
    
    private boolean equalParameterTypes(Parameter[] p1, Parameter[] p2) {
        if (p1.length!=p2.length) return false;
        for (int i=0; i<p1.length; i++) {
            if (!p1[i].getType().equals(p2[i].getType())) return false;
        }
        return true;
    }
    
    /**
     * generates a Meta Object Protocoll method, that is used to call a non public
     * method, or to make a call to super.
     * @param mopCalls list of methods a mop call method should be generated for
     * @param useThis true if "this" should be used for the naming
     */
    private void generateMopCalls(LinkedList mopCalls, boolean useThis) {
        for (Iterator iter = mopCalls.iterator(); iter.hasNext();) {
            MethodNode method = (MethodNode) iter.next();
            String name = getMopMethodName(method,useThis);
            Parameter[] parameters = method.getParameters();
            String methodDescriptor = BytecodeHelper.getMethodDescriptor(method.getReturnType(), method.getParameters());
            cv = cw.visitMethod(Opcodes.ACC_PUBLIC & Opcodes.ACC_SYNTHETIC, name, methodDescriptor, null, null);
            cv.visitVarInsn(ALOAD,0);
            BytecodeHelper helper = new BytecodeHelper(cv);
            int newRegister = 1;
            for (int i=0; i<parameters.length; i++) {
                ClassNode type = parameters[i].getType();
                helper.load(parameters[i].getType(),newRegister);
                // increment to next register, double/long are using two places
                newRegister++;
                if (type == ClassHelper.double_TYPE || type == ClassHelper.long_TYPE) newRegister++;
            }
            cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(method.getDeclaringClass()), method.getName(), methodDescriptor); 
            helper.doReturn(method.getReturnType());
            cv.visitMaxs(0, 0);
            cv.visitEnd();
            classNode.addMethod(name,Opcodes.ACC_PUBLIC & Opcodes.ACC_SYNTHETIC,method.getReturnType(),parameters,null,null);
        }
    }

    /**
     * creates a MOP method name from a method
     * @param method the method to be called by the mop method
     * @param useThis if true, then it is a call on "this", "super" else
     * @return the mop method name
     */
    public static String getMopMethodName(MethodNode method, boolean useThis) {
        ClassNode declaringNode = method.getDeclaringClass();
        int distance = 0;
        for (;declaringNode!=null; declaringNode=declaringNode.getSuperClass()) {
            distance++;
        }
        return (useThis?"this":"super")+"$"+distance+"$"+method.getName();
    }
   
    /**
     * method to determine if a method is a MOP method. This is done by the
     * method name. If the name starts with "this$" or "super$", then it is
     * a MOP method
     * @param methodName name of the method to test
     * @return true if the method is a MOP method
     */
    public static boolean isMopMethod(String methodName) {
        return  methodName.startsWith("this$") || 
                methodName.startsWith("super$");
    }
    
    protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
        String methodType = BytecodeHelper.getMethodDescriptor(node.getReturnType(), node.getParameters());

        cv = cw.visitMethod(node.getModifiers(), node.getName(), methodType, null, buildExceptions(node.getExceptions()));
        helper = new BytecodeHelper(cv);
        if (!node.isAbstract()) { 
            Statement code = node.getCode();
            if (isConstructor && (code == null || !firstStatementIsSpecialConstructorCall(node))) {
                // invokes the super class constructor
                cv.visitVarInsn(ALOAD, 0);
                cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(classNode.getSuperClass()), "<init>", "()V");
            }
            
            compileStack.init(node.getVariableScope(),node.getParameters(),cv, classNode);
            
            // ensure we save the current (meta) class in a register
            (new ClassExpression(classNode)).visit(this);
            cv.visitInsn(POP);
            (new ClassExpression(ClassHelper.METACLASS_TYPE)).visit(this);
            cv.visitInsn(POP);
            
            // handle body
            super.visitConstructorOrMethod(node, isConstructor);
            if (!outputReturn || node.isVoidMethod()) {
                cv.visitInsn(RETURN);
            }
            compileStack.clear();
            
            // lets do all the exception blocks
            for (Iterator iter = exceptionBlocks.iterator(); iter.hasNext();) {
                Runnable runnable = (Runnable) iter.next();
                runnable.run();
            }
            exceptionBlocks.clear();
    
            cv.visitMaxs(0, 0);
        }
    }

    private boolean firstStatementIsSpecialConstructorCall(MethodNode node) {
        Statement code = node.getFirstStatement();
        if (code == null || !(code instanceof ExpressionStatement)) return false;

        Expression expression = ((ExpressionStatement)code).getExpression();
        if (!(expression instanceof ConstructorCallExpression)) return false;
        ConstructorCallExpression cce = (ConstructorCallExpression) expression;
        return cce.isSpecialCall();
    }

    public void visitConstructor(ConstructorNode node) {
        this.constructorNode = node;
        this.methodNode = null;
        outputReturn = false;
        super.visitConstructor(node);
    }

    public void visitMethod(MethodNode node) {
        this.constructorNode = null;
        this.methodNode = node;
        outputReturn = false;
        
        super.visitMethod(node);
    }

    public void visitField(FieldNode fieldNode) {
        onLineNumber(fieldNode, "visitField: " + fieldNode.getName());
        ClassNode t = fieldNode.getType();
        cw.visitField(
            fieldNode.getModifiers(),
            fieldNode.getName(),
            BytecodeHelper.getTypeDescription(t),
            null, //fieldValue,  //br  all the sudden that one cannot init the field here. init is done in static initilizer and instace intializer.
            null);
        visitAnnotations(fieldNode);
    }

    public void visitProperty(PropertyNode statement) {
        // the verifyer created the field and the setter/getter methods, so here is
        // not really something to do
        onLineNumber(statement, "visitProperty:" + statement.getField().getName());
        this.methodNode = null;
    }

    // GroovyCodeVisitor interface
    //-------------------------------------------------------------------------

    // Statements
    //-------------------------------------------------------------------------

    protected void visitStatement(Statement statement) {
        String name = statement.getStatementLabel();
        if (name!=null) {
            Label label = compileStack.createLocalLabel(name);
            cv.visitLabel(label);
        }
    }
    
    public void visitBlockStatement(BlockStatement block) {
        onLineNumber(block, "visitBlockStatement");
        visitStatement(block);
        
        compileStack.pushVariableScope(block.getVariableScope());
        super.visitBlockStatement(block);
        compileStack.pop();
    }

    public void visitForLoop(ForStatement loop) {
        onLineNumber(loop, "visitForLoop");
        visitStatement(loop);

        compileStack.pushLoop(loop.getVariableScope(),loop.getStatementLabel());

        //
        // Declare the loop counter.
        Variable variable = compileStack.defineVariable(loop.getVariable(),false);

        //
        // Then get the iterator and generate the loop control
        MethodCallExpression iterator = new MethodCallExpression(loop.getCollectionExpression(),"iterator",new ArgumentListExpression());
        iterator.visit(this);

        final int iteratorIdx = compileStack.defineTemporaryVariable("iterator", ClassHelper.make(java.util.Iterator.class),true);

        Label continueLabel = compileStack.getContinueLabel();
        Label breakLabel = compileStack.getBreakLabel();
        
        cv.visitLabel(continueLabel);
        cv.visitVarInsn(ALOAD, iteratorIdx);
        iteratorHasNextMethod.call(cv);
        // note: ifeq tests for ==0, a boolean is 0 if it is false
        cv.visitJumpInsn(IFEQ, breakLabel);
        
        cv.visitVarInsn(ALOAD, iteratorIdx);
        iteratorNextMethod.call(cv);
        helper.storeVar(variable);

        // Generate the loop body
        loop.getLoopBlock().visit(this);

        cv.visitJumpInsn(GOTO, continueLabel);        
        cv.visitLabel(breakLabel);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?