classnode.java

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

JAVA
957
字号
/*
 * $Id: ClassNode.java 4216 2006-11-13 16:04:23Z blackdrag $
 * 
 * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
 * 
 * Redistribution and use of this software and associated documentation
 * ("Software"), with or without modification, are permitted provided that the
 * following conditions are met:
 *  1. Redistributions of source code must retain copyright statements and
 * notices. Redistributions must also contain a copy of this document.
 *  2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *  3. The name "groovy" must not be used to endorse or promote products
 * derived from this Software without prior written permission of The Codehaus.
 * For written permission, please contact info@codehaus.org.
 *  4. Products derived from this Software may not be called "groovy" nor may
 * "groovy" appear in their names without prior written permission of The
 * Codehaus. "groovy" is a registered trademark of The Codehaus.
 *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
 * 
 * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *  
 */
package org.codehaus.groovy.ast;

import groovy.lang.GroovyObject;

import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.EmptyStatement;
import org.codehaus.groovy.ast.stmt.Statement;
import org.objectweb.asm.Opcodes;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Represents a class in the AST.<br/>
 * A ClassNode should be created using the methods in ClassHelper. 
 * This ClassNode may be used to represent a class declaration or
 * any other type. This class uses a proxy meschanism allowing to
 * create a class for a plain name at ast creation time. In another 
 * phase of the compiler the real ClassNode for the plain name may be
 * found. To avoid the need of exchanging this ClassNode with an 
 * instance of the correct ClassNode the correct ClassNode is set as 
 * redirect. All method calls are then redirected to that ClassNode.
 * <br>
 * Note: the proxy mechanism is only allowed for classes being marked
 * as primary ClassNode which means they represent no actual class. 
 * The redirect itself can be any type of ClassNode
 *
 * @see org.codehaus.groovy.ast.ClassHelper
 * 
 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
 * @author Jochen Theodorou
 * @version $Revision: 4216 $
 */
public class ClassNode extends AnnotatedNode implements Opcodes {

	public static ClassNode[] EMPTY_ARRAY = new ClassNode[0];
    
    public static ClassNode THIS = new ClassNode(Object.class);
    public static ClassNode SUPER = new ClassNode(Object.class);
    
    private String name;
    private int modifiers;
    private ClassNode[] interfaces;
    private MixinNode[] mixins;
    private List constructors = new ArrayList();
    private List  objectInitializers = new ArrayList();
    private List methods = new ArrayList();
    private List fields = new ArrayList();
    private List properties = new ArrayList();
    private Map fieldIndex = new HashMap();
    private ModuleNode module;
    private CompileUnit compileUnit;
    private boolean staticClass = false;
    private boolean scriptBody = false;
    private boolean script;
    private ClassNode superClass;
    boolean isPrimaryNode;
    
    // use this to synchronize access for the lazy intit
    protected Object lazyInitLock = new Object();

    // clazz!=null when resolved
    protected Class clazz;
    // only false when this classNode is constructed from a class 
    private boolean lazyInitDone=true;
    // not null if if the ClassNode is an array 
    private ClassNode componentType = null;
    // if not null this instance is handled as proxy 
    // for the redirect
    private ClassNode redirect=null; 
    
    /**
     * Returns the ClassNode this ClassNode is redirecting to.
     */
    protected ClassNode redirect(){
        if (redirect==null) return this;
        return redirect.redirect();
    }
    
    /**
     * Sets this instance as proxy for the given ClassNode. 
     * @param cn the class to redirect to. If set to null the redirect will be removed
     */
    public void setRedirect(ClassNode cn) {
        if (isPrimaryNode) throw new GroovyBugError("tried to set a redirect for a primary ClassNode ("+getName()+"->"+cn.getName()+").");
        if (cn!=null) cn = cn.redirect();
        redirect = cn;
    }
    
    /**
     * Returns a ClassNode representing an array of the class
     * represented by this ClassNode
     */
    public ClassNode makeArray() {
        if (redirect!=null) return redirect().makeArray();
        ClassNode cn;
        if (clazz!=null) {
            Class ret = Array.newInstance(clazz,0).getClass();
            // don't use the ClassHelper here!
            cn = new ClassNode(ret,this);
        } else {
            cn = new ClassNode(this);
        }
        return cn;
    }
    
    /**
     * Returns if this instance is a primary ClassNode
     */
    public boolean isPrimaryClassNode(){
    	return redirect().isPrimaryNode || (componentType!= null && componentType.isPrimaryClassNode());
    }
    
    /**
     * Constructor used by makeArray() if no real class is available
     */
    private ClassNode(ClassNode componentType) {
        this(componentType.getName()+"[]", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
        this.componentType = componentType.redirect();
        isPrimaryNode=false;
    }
    
    /**
     * Constructor used by makeArray() if a real class is available
     */
    private ClassNode(Class c, ClassNode componentType) {
        this(c);
        this.componentType = componentType;
        isPrimaryNode=false;
    }
    
    /**
     * Creates a ClassNode from a real class. The resulting 
     * ClassNode will be no primary ClassNode.
     */
    public ClassNode(Class c) {
        this(c.getName(), c.getModifiers(), null, null ,MixinNode.EMPTY_ARRAY);
        clazz=c;
        lazyInitDone=false;
        CompileUnit cu = getCompileUnit();
        if (cu!=null) cu.addClass(this);
        isPrimaryNode=false;
    }    
    
    /**
     * The complete class structure will be initialized only when really
     * needed to avoid having too much objects during compilation
     */
    private void lazyClassInit() {       
        synchronized (lazyInitLock) {
            if (lazyInitDone) return;
            
            Field[] fields = clazz.getDeclaredFields();
            for (int i=0;i<fields.length;i++){
                addField(fields[i].getName(),fields[i].getModifiers(),this,null);
            }
            Method[] methods = clazz.getDeclaredMethods();
            for (int i=0;i<methods.length;i++){
                Method m = methods[i];
                MethodNode mn = new MethodNode(m.getName(), m.getModifiers(), ClassHelper.make(m.getReturnType()), createParameters(m.getParameterTypes()), ClassHelper.make(m.getExceptionTypes()), null);
                addMethod(mn);
            }
            Constructor[] constructors = clazz.getDeclaredConstructors();
            for (int i=0;i<constructors.length;i++){
                Constructor ctor = constructors[i];
                addConstructor(ctor.getModifiers(),createParameters(ctor.getParameterTypes()),ClassHelper.make(ctor.getExceptionTypes()),null);
            }
            Class sc = clazz.getSuperclass();
            if (sc!=null) superClass = ClassHelper.make(sc);
            buildInterfaceTypes(clazz);       
            lazyInitDone=true;
        }
    }
    
    private void buildInterfaceTypes(Class c) {
        Class[] interfaces = c.getInterfaces();
        ClassNode[] ret = new ClassNode[interfaces.length];
        for (int i=0;i<interfaces.length;i++){
            ret[i] = ClassHelper.make(interfaces[i]);
        }
        this.interfaces = ret;
    }
    
    
    // added to track the enclosing method for local inner classes
    private MethodNode enclosingMethod = null;

    public MethodNode getEnclosingMethod() {
        return redirect().enclosingMethod;
    }

    public void setEnclosingMethod(MethodNode enclosingMethod) {
        redirect().enclosingMethod = enclosingMethod;
    }


    /**
     * @param name       is the full name of the class
     * @param modifiers  the modifiers,
     * @param superClass the base class name - use "java.lang.Object" if no direct
     *                   base class
     * @see org.objectweb.asm.Opcodes
     */
    public ClassNode(String name, int modifiers, ClassNode superClass) {
        this(name, modifiers, superClass, ClassHelper.EMPTY_TYPE_ARRAY, MixinNode.EMPTY_ARRAY);
    }

    /**
     * @param name       is the full name of the class
     * @param modifiers  the modifiers,
     * @param superClass the base class name - use "java.lang.Object" if no direct
     *                   base class
     * @see org.objectweb.asm.Opcodes
     */
    public ClassNode(String name, int modifiers, ClassNode superClass, ClassNode[] interfaces, MixinNode[] mixins) {
        this.name = name;
        this.modifiers = modifiers;
        this.superClass = superClass;
        this.interfaces = interfaces;
        this.mixins = mixins;
        isPrimaryNode = true;
    }

    
    /**
     * Sets the superclass of this ClassNode
     */
    public void setSuperClass(ClassNode superClass) {
        redirect().superClass = superClass;
    }

    /**
     * Returns a list containing FieldNode objects for
     * each field in the class represented by this ClassNode
     */
    public List getFields() {
        if (!lazyInitDone) {
            lazyClassInit();
        }
        if (redirect!=null) return redirect().getFields();
        return fields;
    }

    /**
     * Returns an array of ClassNodes representing the
     * interfaces the class implements
     */
    public ClassNode[] getInterfaces() {
        if (!lazyInitDone) {
            lazyClassInit();
        }
        if (redirect!=null) return redirect().getInterfaces();
        return interfaces;
    }

    public MixinNode[] getMixins() {
        return redirect().mixins;
    }

    /**
     * Returns a list containing MethodNode objects for
     * each method in the class represented by this ClassNode
     */    
    public List getMethods() {
        if (!lazyInitDone) {
            lazyClassInit();
        }
        if (redirect!=null) return redirect().getMethods();
        return methods;
    }

    /**
     * Returns a list containing MethodNode objects for
     * each abstract method in the class represented by 

⌨️ 快捷键说明

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