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

org.codehaus.janino.IClass Maven / Gradle / Ivy

Go to download

The "JANINO" implementation of the "commons-compiler" API: Super-small, super-fast, independent from the JDK's "tools.jar".

There is a newer version: 3.1.12
Show newest version

/*
 * Janino - An embedded Java[TM] compiler
 *
 * Copyright (c) 2001-2010, Arno Unkrig
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
 *       following disclaimer.
 *    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 of the author may not be used to endorse or promote products derived from this software without
 *       specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 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 AUTHOR 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.janino;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.codehaus.commons.compiler.CompileException;
import org.codehaus.janino.Java.Annotation;

/**
 * A simplified equivalent to "java.lang.reflect".
 * 

* 'JLS7' means a reference to the Java Language Specification, Java SE * 7 Edition */ @SuppressWarnings({ "rawtypes", "unchecked" }) public abstract class IClass { private static final boolean DEBUG = false; /** * Special return value for {@link IField#getConstantValue()} indicating that the field does not have a * constant value. */ public static final Object NOT_CONSTANT = new Object() { @Override public String toString() { return "NOT_CONSTANT"; } }; /** The {@link IClass} object for the type VOID. */ public static final IClass VOID = new PrimitiveIClass(Descriptor.VOID); /** The {@link IClass} object for the primitive type BYTE. */ public static final IClass BYTE = new PrimitiveIClass(Descriptor.BYTE); /** The {@link IClass} object for the primitive type CHAR. */ public static final IClass CHAR = new PrimitiveIClass(Descriptor.CHAR); /** The {@link IClass} object for the primitive type DOUBLE. */ public static final IClass DOUBLE = new PrimitiveIClass(Descriptor.DOUBLE); /** The {@link IClass} object for the primitive type FLOAT. */ public static final IClass FLOAT = new PrimitiveIClass(Descriptor.FLOAT); /** The {@link IClass} object for the primitive type INT. */ public static final IClass INT = new PrimitiveIClass(Descriptor.INT); /** The {@link IClass} object for the primitive type LONG. */ public static final IClass LONG = new PrimitiveIClass(Descriptor.LONG); /** The {@link IClass} object for the primitive type SHORT. */ public static final IClass SHORT = new PrimitiveIClass(Descriptor.SHORT); /** The {@link IClass} object for the primitive type BOOLEAN. */ public static final IClass BOOLEAN = new PrimitiveIClass(Descriptor.BOOLEAN); private static class PrimitiveIClass extends IClass { private final String fieldDescriptor; public PrimitiveIClass(String fieldDescriptor) { this.fieldDescriptor = fieldDescriptor; } @Override protected IClass getComponentType2() { return null; } @Override protected IClass[] getDeclaredIClasses2() { return new IClass[0]; } @Override protected IConstructor[] getDeclaredIConstructors2() { return new IConstructor[0]; } @Override protected IField[] getDeclaredIFields2() { return new IField[0]; } @Override protected IMethod[] getDeclaredIMethods2() { return new IMethod[0]; } @Override protected IClass getDeclaringIClass2() { return null; } @Override protected String getDescriptor2() { return this.fieldDescriptor; } @Override protected IClass[] getInterfaces2() { return new IClass[0]; } @Override protected IClass getOuterIClass2() { return null; } @Override protected IClass getSuperclass2() { return null; } @Override public boolean isAbstract() { return false; } @Override public boolean isArray() { return false; } @Override public boolean isFinal() { return true; } @Override public boolean isInterface() { return false; } @Override public boolean isPrimitive() { return true; } @Override public boolean isPrimitiveNumeric() { return Descriptor.isPrimitiveNumeric(this.fieldDescriptor); } // SUPPRESS CHECKSTYLE LineLength @Override public Access getAccess() { return Access.PUBLIC; } } /** * Returns all the constructors declared by the class represented by the * type. If the class has a default constructor, it is included. *

* Returns an array with zero elements for an interface, array, primitive type or * "void". */ public final IConstructor[] getDeclaredIConstructors() { if (this.declaredIConstructorsCache == null) { this.declaredIConstructorsCache = this.getDeclaredIConstructors2(); } return this.declaredIConstructorsCache; } private IConstructor[] declaredIConstructorsCache; /** The uncached version of {@link #getDeclaredIConstructors()} which must be implemented by derived classes. */ protected abstract IConstructor[] getDeclaredIConstructors2(); /** * Returns the methods of the class or interface (but not inherited methods). For covariant methods, only the * method with the most derived return type is included. *
* Returns an empty array for an array, primitive type or "void". */ public final IMethod[] getDeclaredIMethods() { if (this.declaredIMethodsCache == null) { this.declaredIMethodsCache = this.getDeclaredIMethods2(); } return this.declaredIMethodsCache; } private IMethod[] declaredIMethodsCache; /** The uncached version of {@link #getDeclaredIMethods()} which must be implemented by derived classes. */ protected abstract IMethod[] getDeclaredIMethods2(); /** * Returns all methods with the given name declared in the class or interface (but not inherited methods). *
* Returns an empty array if no methods with that name are declared. * * @return an array of {@link IMethod}s that must not be modified */ public final IMethod[] getDeclaredIMethods(String methodName) { if (this.declaredIMethodCache == null) { IMethod[] dims = this.getDeclaredIMethods(); // Fill the map with "IMethod"s and "List"s. Map*/> m = new HashMap(); for (IMethod dim : dims) { String mn = dim.getName(); Object o = m.get(mn); if (o == null) { m.put(mn, dim); } else if (o instanceof IMethod) { List l = new ArrayList(); l.add(o); l.add(dim); m.put(mn, l); } else { ((List) o).add(dim); } } // Convert "IMethod"s and "List"s to "IMethod[]"s. for (Map.Entry*/> me : m.entrySet()) { Object v = me.getValue(); if (v instanceof IMethod) { me.setValue(new IMethod[] { (IMethod) v }); } else { List l = (List) v; me.setValue(l.toArray(new IMethod[l.size()])); } } this.declaredIMethodCache = m; } IMethod[] methods = (IMethod[]) this.declaredIMethodCache.get(methodName); return methods == null ? IClass.NO_IMETHODS : methods; } private Map*/> declaredIMethodCache; /** * Returns all methods declared in the class or interface, its superclasses and its * superinterfaces.
* * @return an array of {@link IMethod}s that must not be modified */ public final IMethod[] getIMethods() throws CompileException { if (this.iMethodCache == null) { List iMethods = new ArrayList(); this.getIMethods(iMethods); this.iMethodCache = (IMethod[]) iMethods.toArray(new IMethod[iMethods.size()]); } return this.iMethodCache; } private IMethod[] iMethodCache; private void getIMethods(List result) throws CompileException { IMethod[] ms = this.getDeclaredIMethods(); SCAN_DECLARED_METHODS: for (IMethod candidate : ms) { String candidateDescriptor = candidate.getDescriptor(); String candidateName = candidate.getName(); // Check if a method with the same name and descriptor has been added before. for (IMethod oldMethod : result) { if ( candidateName.equals(oldMethod.getName()) && candidateDescriptor.equals(oldMethod.getDescriptor()) ) continue SCAN_DECLARED_METHODS; } result.add(candidate); } IClass sc = this.getSuperclass(); if (sc != null) sc.getIMethods(result); for (IClass ii : this.getInterfaces()) ii.getIMethods(result); } private static final IMethod[] NO_IMETHODS = new IMethod[0]; /** * @return Whether this {@link IClass} (or its superclass or the interfaces it implements) has an {@link IMethod} * with the given name and parameter types */ public final boolean hasIMethod(String methodName, IClass[] parameterTypes) throws CompileException { return this.findIMethod(methodName, parameterTypes) != null; } /** * @return The {@link IMethod} declared in this {@link IClass} (or its superclass or the interfaces it implements) * with the given name and parameter types, or {@code null} if an applicable method could not be found */ public final IMethod findIMethod(String methodName, IClass[] parameterTypes) throws CompileException { { IMethod result = null; for (IMethod im : this.getDeclaredIMethods(methodName)) { if ( Arrays.equals(im.getParameterTypes(), parameterTypes) && (result == null || result.getReturnType().isAssignableFrom(im.getReturnType())) ) result = im; } if (result != null) return result; } { IClass superclass = this.getSuperclass(); if (superclass != null) { IMethod result = superclass.findIMethod(methodName, parameterTypes); if (result != null) return result; } } { IClass[] interfaces = this.getInterfaces(); for (IClass interfacE : interfaces) { IMethod result = interfacE.findIMethod(methodName, parameterTypes); if (result != null) return result; } } return null; } /** * @return The {@link IConstructor} declared in this {@link IClass} with the given parameter types, or {@code null} * if an applicable constrcutor could not be found */ public final IConstructor findIConstructor(IClass[] parameterTypes) throws CompileException { IConstructor[] ics = this.getDeclaredIConstructors(); for (IConstructor ic : ics) { if (Arrays.equals(ic.getParameterTypes(), parameterTypes)) return ic; } return null; } /** * Returns the {@link IField}s declared in this {@link IClass} (but not inherited fields). * * @return An empty array for an array, primitive type or "void" */ public final IField[] getDeclaredIFields() { Collection allFields = this.getDeclaredIFieldsCache().values(); return (IField[]) allFields.toArray(new IField[allFields.size()]); } /** @return String fieldName => IField */ private Map getDeclaredIFieldsCache() { if (this.declaredIFieldsCache == null) { IField[] fields = this.getDeclaredIFields2(); Map m = new HashMap(); for (IField f : fields) m.put(f.getName(), f); this.declaredIFieldsCache = m; } return this.declaredIFieldsCache; } /** * Returns the named {@link IField} declared in this {@link IClass} (does not work for inherited fields). * * @return null iff this {@link IClass} does not declare an {@link IField} with that name */ public final IField getDeclaredIField(String name) { return (IField) this.getDeclaredIFieldsCache().get(name); } /** * Clears the cache of declared fields which this class maintains in order to minimize the invocations of {@link * #getDeclaredIFields2()}. */ protected void clearIFieldCaches() { this.declaredIFieldsCache = null; } private Map declaredIFieldsCache; /** Uncached version of {@link #getDeclaredIFields()}. */ protected abstract IField[] getDeclaredIFields2(); /** * Returns the synthetic fields of an anonymous or local class, in * the order in which they are passed to all constructors. */ public IField[] getSyntheticIFields() { return new IField[0]; } /** * Returns the classes and interfaces declared as members of the class * (but not inherited classes and interfaces).
* Returns an empty array for an array, primitive type or "void". */ public final IClass[] getDeclaredIClasses() throws CompileException { if (this.declaredIClassesCache == null) { this.declaredIClassesCache = this.getDeclaredIClasses2(); } return this.declaredIClassesCache; } private IClass[] declaredIClassesCache; /** @return The member types of this type */ protected abstract IClass[] getDeclaredIClasses2() throws CompileException; /** @return If this class is a member class, the declaring class, otherwise {@code null} */ public final IClass getDeclaringIClass() throws CompileException { if (!this.declaringIClassIsCached) { this.declaringIClassCache = this.getDeclaringIClass2(); this.declaringIClassIsCached = true; } return this.declaringIClassCache; } private boolean declaringIClassIsCached; private IClass declaringIClassCache; /** @return If this class is a member class, the declaring class, otherwise {@code null} */ protected abstract IClass getDeclaringIClass2() throws CompileException; /** * The following types have an "outer class": *

    *
  • Anonymous classes declared in a non-static method of a class *
  • Local classes declared in a non-static method of a class *
  • Non-static member classes *
* * @return The outer class of this type, or {@code null} */ public final IClass getOuterIClass() throws CompileException { if (!this.outerIClassIsCached) { this.outerIClassCache = this.getOuterIClass2(); this.outerIClassIsCached = true; } return this.outerIClassCache; } private boolean outerIClassIsCached; private IClass outerIClassCache; /** @see #getOuterIClass() */ protected abstract IClass getOuterIClass2() throws CompileException; /** * Returns the superclass of the class.
* Returns "null" for class "Object", interfaces, arrays, primitive types * and "void". */ public final IClass getSuperclass() throws CompileException { if (!this.superclassIsCached) { this.superclassCache = this.getSuperclass2(); this.superclassIsCached = true; if (this.superclassCache != null && this.superclassCache.isSubclassOf(this)) { throw new CompileException( "Class circularity detected for \"" + Descriptor.toClassName(this.getDescriptor()) + "\"", null ); } } return this.superclassCache; } private boolean superclassIsCached; private IClass superclassCache; /** @see #getSuperclass() */ protected abstract IClass getSuperclass2() throws CompileException; /** @return The accessibility of this type */ public abstract Access getAccess(); /** * Whether subclassing is allowed (JVMS 4.1 access_flags) * @return true if subclassing is prohibited */ public abstract boolean isFinal(); /** * Returns the interfaces implemented by the class.
* Returns the superinterfaces of the interface.
* Returns "Cloneable" and "Serializable" for arrays.
* Returns an empty array for primitive types and "void". */ public final IClass[] getInterfaces() throws CompileException { if (this.interfacesCache == null) { this.interfacesCache = this.getInterfaces2(); for (IClass ii : this.interfacesCache) { if (ii.implementsInterface(this)) { throw new CompileException( "Interface circularity detected for \"" + Descriptor.toClassName(this.getDescriptor()) + "\"", null ); } } } return this.interfacesCache; } private IClass[] interfacesCache; /** @see #getInterfaces() */ protected abstract IClass[] getInterfaces2() throws CompileException; /** * Whether the class may be instantiated (JVMS 4.1 access_flags) * @return true if instantiation is prohibited */ public abstract boolean isAbstract(); /** Returns the field descriptor for the type as defined by JVMS 4.3.2. This method is fast. */ public final String getDescriptor() { if (this.descriptorCache == null) { this.descriptorCache = this.getDescriptor2(); } return this.descriptorCache; } private String descriptorCache; /** @return The field descriptor for the type as defined by JVMS 4.3.2. */ protected abstract String getDescriptor2(); /** * Convenience method that determines the field descriptors of an array of {@link IClass}es. * @see #getDescriptor() */ public static String[] getDescriptors(IClass[] iClasses) { String[] descriptors = new String[iClasses.length]; for (int i = 0; i < iClasses.length; ++i) descriptors[i] = iClasses[i].getDescriptor(); return descriptors; } /**@return Whether this type represents an interface */ public abstract boolean isInterface(); /** @return Whether this type represents an array */ public abstract boolean isArray(); /** @return Whether this type represents a primitive type or "void" */ public abstract boolean isPrimitive(); /** @return Whether this type represents "byte", "short", "int", "long", "char", "float" or "double" */ public abstract boolean isPrimitiveNumeric(); /** * @return The component type of the array, or {@code null} for classes, interfaces, primitive types and {@code * void} */ public final IClass getComponentType() { if (!this.componentTypeIsCached) { this.componentTypeCache = this.getComponentType2(); this.componentTypeIsCached = true; } return this.componentTypeCache; } private boolean componentTypeIsCached; private IClass componentTypeCache; /** @see #getComponentType() */ protected abstract IClass getComponentType2(); @Override public String toString() { return Descriptor.toClassName(this.getDescriptor()); } /** * Determine if "this" is assignable from "that". This is true if "this" is identical with "that" (JLS7 5.1.1), or * if "that" is widening-primitive-convertible to "this" (JLS7 5.1.2), or if "that" is * widening-reference-convertible to "this" (JLS7 5.1.5). */ public boolean isAssignableFrom(IClass that) throws CompileException { // Identity conversion, JLS7 5.1.1 if (this == that) return true; // Widening primitive conversion, JLS7 5.1.2 { String ds = that.getDescriptor() + this.getDescriptor(); if (ds.length() == 2 && IClass.PRIMITIVE_WIDENING_CONVERSIONS.contains(ds)) return true; } // Widening reference conversion, JLS7 5.1.5 { // JLS7 5.1.4.1: Target type is superclass of source class type. if (that.isSubclassOf(this)) return true; // JLS7 5.1.4.2: Source class type implements target interface type. // JLS7 5.1.4.4: Source interface type implements target interface type. if (that.implementsInterface(this)) return true; // JLS7 5.1.4.3 Convert "null" literal to any reference type. if (that == IClass.VOID && !this.isPrimitive()) return true; // JLS7 5.1.4.5: From any interface to type "Object". if (that.isInterface() && this.getDescriptor().equals(Descriptor.JAVA_LANG_OBJECT)) return true; if (that.isArray()) { // JLS7 5.1.4.6: From any array type to type "Object". if (this.getDescriptor().equals(Descriptor.JAVA_LANG_OBJECT)) return true; // JLS7 5.1.4.7: From any array type to type "Cloneable". if (this.getDescriptor().equals(Descriptor.JAVA_LANG_CLONEABLE)) return true; // JLS7 5.1.4.8: From any array type to type "java.io.Serializable". if (this.getDescriptor().equals(Descriptor.JAVA_IO_SERIALIZABLE)) return true; // JLS7 5.1.4.9: From SC[] to TC[] while SC if widening reference convertible to TC. if (this.isArray()) { IClass thisCt = this.getComponentType(); IClass thatCt = that.getComponentType(); if (!thisCt.isPrimitive() && thisCt.isAssignableFrom(thatCt)) return true; } } } return false; } private static final Set PRIMITIVE_WIDENING_CONVERSIONS = new HashSet(); static { String[] pwcs = new String[] { Descriptor.BYTE + Descriptor.SHORT, Descriptor.BYTE + Descriptor.INT, Descriptor.SHORT + Descriptor.INT, Descriptor.CHAR + Descriptor.INT, Descriptor.BYTE + Descriptor.LONG, Descriptor.SHORT + Descriptor.LONG, Descriptor.CHAR + Descriptor.LONG, Descriptor.INT + Descriptor.LONG, Descriptor.BYTE + Descriptor.FLOAT, Descriptor.SHORT + Descriptor.FLOAT, Descriptor.CHAR + Descriptor.FLOAT, Descriptor.INT + Descriptor.FLOAT, Descriptor.LONG + Descriptor.FLOAT, Descriptor.BYTE + Descriptor.DOUBLE, Descriptor.SHORT + Descriptor.DOUBLE, Descriptor.CHAR + Descriptor.DOUBLE, Descriptor.INT + Descriptor.DOUBLE, Descriptor.LONG + Descriptor.DOUBLE, Descriptor.FLOAT + Descriptor.DOUBLE, }; for (String pwc : pwcs) IClass.PRIMITIVE_WIDENING_CONVERSIONS.add(pwc); } /** * Returns true if this class is an immediate or non-immediate * subclass of that class. */ public boolean isSubclassOf(IClass that) throws CompileException { for (IClass sc = this.getSuperclass(); sc != null; sc = sc.getSuperclass()) { if (sc == that) return true; } return false; } /** * If this represents a class: Return true if this class * directly or indirectly implements that interface. *

* If this represents an interface: Return true if this * interface directly or indirectly extends that interface. */ public boolean implementsInterface(IClass that) throws CompileException { for (IClass c = this; c != null; c = c.getSuperclass()) { IClass[] tis = c.getInterfaces(); for (IClass ti : tis) { if (ti == that || ti.implementsInterface(that)) return true; } } return false; } /** * Get an {@link IClass} that represents an n-dimensional array of this type. * * @param n dimension count * @param objectType Required because the superclass of an array class is {@link Object} by definition */ public IClass getArrayIClass(int n, IClass objectType) { IClass result = this; for (int i = 0; i < n; ++i) result = result.getArrayIClass(objectType); return result; } /** * Get an {@link IClass} that represents an array of this type. * * @param objectType Required because the superclass of an array class is {@link Object} by definition */ public synchronized IClass getArrayIClass(IClass objectType) { if (this.arrayIClass == null) { this.arrayIClass = this.getArrayIClass2(objectType); } return this.arrayIClass; } private IClass arrayIClass; private IClass getArrayIClass2(final IClass objectType) { final IClass componentType = this; return new IClass() { @Override public IClass.IConstructor[] getDeclaredIConstructors2() { return new IClass.IConstructor[0]; } // Special trickery #17: Arrays override "Object.clone()", but without "throws // CloneNotSupportedException"! @Override public IClass.IMethod[] getDeclaredIMethods2() { return new IClass.IMethod[] { new IMethod() { @Override public String getName() { return "clone"; } @Override public IClass getReturnType() { return objectType; } @Override public boolean isAbstract() { return false; } @Override public boolean isStatic() { return false; } @Override public Access getAccess() { return Access.PUBLIC; } @Override public boolean isVarargs() { return false; } @Override public IClass[] getParameterTypes2() { return new IClass[0]; } @Override public IClass[] getThrownExceptions2() { return new IClass[0]; } @Override public Annotation[] getAnnotations() { return new Annotation[0]; } } }; } // CHECKSTYLE LineLength:OFF @Override public IClass.IField[] getDeclaredIFields2() { return new IClass.IField[0]; } @Override public IClass[] getDeclaredIClasses2() { return new IClass[0]; } @Override public IClass getDeclaringIClass2() { return null; } @Override public IClass getOuterIClass2() { return null; } @Override public IClass getSuperclass2() { return objectType; } @Override public IClass[] getInterfaces2() { return new IClass[0]; } @Override public String getDescriptor2() { return '[' + componentType.getDescriptor(); } @Override public Access getAccess() { return componentType.getAccess(); } @Override public boolean isFinal() { return true; } @Override public boolean isInterface() { return false; } @Override public boolean isAbstract() { return false; } @Override public boolean isArray() { return true; } @Override public boolean isPrimitive() { return false; } @Override public boolean isPrimitiveNumeric() { return false; } @Override public IClass getComponentType2() { return componentType; } // CHECKSTYLE LineLength:ON @Override public String toString() { return componentType.toString() + "[]"; } }; } /** * If optionalName is null, find all {@link IClass}es visible in the * scope of the current class. *

* If optionalName is not null, find the member {@link IClass}es * that has the given name. If the name is ambiguous (i.e. if more than one superclass, * interface of enclosing type declares a type with that name), then the size of the * returned array is greater than one. *

* Examines superclasses, interfaces and enclosing type declarations. * @return an array of {@link IClass}es in unspecified order, possibly of length zero */ IClass[] findMemberType(String optionalName) throws CompileException { IClass[] res = (IClass[]) this.memberTypeCache.get(optionalName); if (res == null) { // Notice: A type may be added multiply to the result set because we are in its scope // multiply. E.g. the type is a member of a superclass AND a member of an enclosing type. Set s = new HashSet(); this.findMemberType(optionalName, s); res = s.isEmpty() ? IClass.ZERO_ICLASSES : (IClass[]) s.toArray(new IClass[s.size()]); this.memberTypeCache.put(optionalName, res); } return res; } private final Map memberTypeCache = new HashMap(); private static final IClass[] ZERO_ICLASSES = new IClass[0]; private void findMemberType(String optionalName, Collection result) throws CompileException { // Search for a type with the given name in the current class. IClass[] memberTypes = this.getDeclaredIClasses(); if (optionalName == null) { result.addAll(Arrays.asList(memberTypes)); } else { String memberDescriptor = Descriptor.fromClassName( Descriptor.toClassName(this.getDescriptor()) + '$' + optionalName ); for (final IClass mt : memberTypes) { if (mt.getDescriptor().equals(memberDescriptor)) { result.add(mt); return; } } } // Examine superclass. { IClass superclass = this.getSuperclass(); if (superclass != null) superclass.findMemberType(optionalName, result); } // Examine interfaces. for (IClass i : this.getInterfaces()) i.findMemberType(optionalName, result); // Examine enclosing type declarations. { IClass declaringIClass = this.getDeclaringIClass(); IClass outerIClass = this.getOuterIClass(); if (declaringIClass != null) { declaringIClass.findMemberType(optionalName, result); } if (outerIClass != null && outerIClass != declaringIClass) { outerIClass.findMemberType(optionalName, result); } } } /** * Base for the members of an {@link IClass}. {@link IMember} are expected to be immutable, i.e. all getter methods * return constant values. */ public interface IMember { /** * @return One of {@link Access#PRIVATE}, {@link Access#PROTECTED}, * {@link Access#DEFAULT} and {@link Access#PUBLIC}. */ Access getAccess(); /** @return Modifiers and/or annotations of this member */ Annotation[] getAnnotations(); /** @return The {@link IClass} that declares this {@link IClass.IMember} */ IClass getDeclaringIClass(); } /** Base class for {@link IConstructor} and {@link IMethod}. */ public abstract class IInvocable implements IMember { private boolean argsNeedAdjust; /** TODO */ public void setArgsNeedAdjust(boolean newVal) { this.argsNeedAdjust = newVal; } /** TODO */ public boolean argsNeedAdjust() { return this.argsNeedAdjust; } /** * @return Whether this invocable is 'variable arity', i.e. its last parameter has an ellipsis ('...') after * the type */ public abstract boolean isVarargs(); // Implement IMember. @Override public abstract Access getAccess(); @Override public IClass getDeclaringIClass() { return IClass.this; } /** Returns the types of the parameters of this constructor or method. This method is fast. */ public final IClass[] getParameterTypes() throws CompileException { if (this.parameterTypesCache == null) { this.parameterTypesCache = this.getParameterTypes2(); } return this.parameterTypesCache; } private IClass[] parameterTypesCache; /** @return The types of the parameters of this constructor or method */ public abstract IClass[] getParameterTypes2() throws CompileException; /** Returns the method descriptor of this constructor or method. This method is fast. */ public final String getDescriptor() throws CompileException { if (this.descriptorCache == null) { this.descriptorCache = this.getDescriptor2(); } return this.descriptorCache; } private String descriptorCache; /** Uncached implementation of {@link #getDescriptor()}. */ public abstract String getDescriptor2() throws CompileException; /** Returns the types thrown by this constructor or method. This method is fast. */ public final IClass[] getThrownExceptions() throws CompileException { if (this.thrownExceptionsCache == null) { this.thrownExceptionsCache = this.getThrownExceptions2(); } return this.thrownExceptionsCache; } private IClass[] thrownExceptionsCache; /** @return The types thrown by this constructor or method */ public abstract IClass[] getThrownExceptions2() throws CompileException; /** * @return Whether this {@link IInvocable} is more specific then {@code that} (in the sense of JLS7 15.12.2.5) */ public boolean isMoreSpecificThan(IInvocable that) throws CompileException { if (IClass.DEBUG) System.out.print("\"" + this + "\".isMoreSpecificThan(\"" + that + "\") => "); // a variable-length argument is always less specific than a fixed arity. final boolean thatIsVararg; if ((thatIsVararg = that.isVarargs()) != this.isVarargs()) { // Only one of the two is varargs. return thatIsVararg; } else if (thatIsVararg) { // Both are varargs. final IClass[] thisParameterTypes = this.getParameterTypes(); final IClass[] thatParameterTypes = that.getParameterTypes(); IClass[] t, u; int n, k; if (thisParameterTypes.length >= thatParameterTypes.length) { t = thisParameterTypes; u = thatParameterTypes; n = t.length; k = u.length; IClass[] s = u; // this = T | T_n // that = U | U_k // n >= k // ignore generics, for now // T0, T1, ..., Tn-1, Tn[] // U0, U1, .., Uk[] final int kMinus1 = k - 1; for (int j = 0; j < kMinus1; ++j) { // expect T[j] <: S[j] if (!s[j].isAssignableFrom(t[j])) { return false; } } final IClass sk1 = s[kMinus1].getComponentType(); final int nMinus1 = n - 1; for (int j = kMinus1; j < nMinus1; ++j) { // expect T[j] <: S[k -1] if (!sk1.isAssignableFrom(t[j])) { return false; } } if (!sk1.isAssignableFrom(t[nMinus1])) { return false; } } else { u = thisParameterTypes; t = thatParameterTypes; n = t.length; k = u.length; IClass[] s = t; // n >= k final int kMinus1 = k - 1; for (int j = 0; j < kMinus1; ++j) { // expect U[j] <: S[j] if (!s[j].isAssignableFrom(u[j])) { return false; } } final IClass uk1 = u[kMinus1].getComponentType(); final int nMinus1 = n - 1; for (int j = kMinus1; j < nMinus1; ++j) { // expect U[k -1] <: S[j] if (!s[j].isAssignableFrom(uk1)) { return false; } } if (!s[nMinus1].getComponentType().isAssignableFrom(uk1)) { return false; } } return true; } // both are fixed arity // The following case is tricky: JLS7 says that the invocation is AMBIGUOUS, but only JAVAC 1.2 issues an // error; JAVAC 1.4.1, 1.5.0 and 1.6.0 obviously ignore the declaring type and invoke "A.meth(String)". // JLS7 is not clear about this. For compatibility with JAVA 1.4.1, 1.5.0 and 1.6.0, JANINO also ignores // the declaring type. // // See also JANINO-79 and JlsTests / 15.12.2.2 // if (false) { // if (!that.getDeclaringIClass().isAssignableFrom(this.getDeclaringIClass())) { // if (IClass.DEBUG) System.out.println("falsE"); // return false; // } // } IClass[] thisParameterTypes = this.getParameterTypes(); IClass[] thatParameterTypes = that.getParameterTypes(); for (int i = 0; i < thisParameterTypes.length; ++i) { if (!thatParameterTypes[i].isAssignableFrom(thisParameterTypes[i])) { if (IClass.DEBUG) System.out.println("false"); return false; } } if (IClass.DEBUG) System.out.println("true"); return !Arrays.equals(thisParameterTypes, thatParameterTypes); } /** * @return Whether this {@link IInvocable} is less specific then {@code that} (in the sense of JLS7 15.12.2.5) */ public boolean isLessSpecificThan(IInvocable that) throws CompileException { return that.isMoreSpecificThan(this); } @Override public abstract String toString(); } /** Representation of a constructor of an {@link IClass}. */ public abstract class IConstructor extends IInvocable { /** * Opposed to {@link java.lang.reflect.Constructor#getParameterTypes()}, the * return value of this method does not include the optionally leading "synthetic * parameters". */ @Override public abstract IClass[] getParameterTypes2() throws CompileException; /** * Opposed to {@link #getParameterTypes()}, the method descriptor returned by this method does include the * optionally leading synthetic parameters. */ @Override public String getDescriptor2() throws CompileException { IClass[] parameterTypes = this.getParameterTypes(); IClass outerIClass = IClass.this.getOuterIClass(); if (outerIClass != null) { IClass[] tmp = new IClass[parameterTypes.length + 1]; tmp[0] = outerIClass; System.arraycopy(parameterTypes, 0, tmp, 1, parameterTypes.length); parameterTypes = tmp; } return new MethodDescriptor(IClass.getDescriptors(parameterTypes), Descriptor.VOID).toString(); } @Override public String toString() { StringBuilder sb = new StringBuilder(this.getDeclaringIClass().toString()); sb.append('('); try { IClass[] parameterTypes = this.getParameterTypes(); for (int i = 0; i < parameterTypes.length; ++i) { if (i > 0) sb.append(", "); sb.append(parameterTypes[i].toString()); } } catch (CompileException ex) { sb.append(""); } sb.append(')'); return sb.toString(); } } /** Representation of a method in an {@link IClass}. */ public abstract class IMethod extends IInvocable { /** @return Whether this method is STATIC */ public abstract boolean isStatic(); /** @return Whether this method is ABSTRACT */ public abstract boolean isAbstract(); /** @return The return type of this method */ public abstract IClass getReturnType() throws CompileException; /** @return The name of this method */ public abstract String getName(); @Override public String getDescriptor2() throws CompileException { return new MethodDescriptor( IClass.getDescriptors(this.getParameterTypes()), this.getReturnType().getDescriptor() ).toString(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(this.getAccess().toString()).append(' '); if (this.isStatic()) sb.append("static "); if (this.isAbstract()) sb.append("abstract "); try { sb.append(this.getReturnType().toString()); } catch (CompileException ex) { sb.append(""); } sb.append(' '); sb.append(this.getDeclaringIClass().toString()); sb.append('.'); sb.append(this.getName()); sb.append('('); try { IClass[] parameterTypes = this.getParameterTypes(); for (int i = 0; i < parameterTypes.length; ++i) { if (i > 0) sb.append(", "); sb.append(parameterTypes[i].toString()); } } catch (CompileException ex) { sb.append(""); } sb.append(')'); try { IClass[] tes = this.getThrownExceptions(); if (tes.length > 0) { sb.append(" throws ").append(tes[0]); for (int i = 1; i < tes.length; ++i) sb.append(", ").append(tes[i]); } } catch (CompileException ex) { sb.append(""); } return sb.toString(); } } /** Representation of a field of this {@link IClass}. */ public abstract class IField implements IMember { // Implement IMember. @Override public abstract Access getAccess(); @Override public IClass getDeclaringIClass() { return IClass.this; } /** @return Whether this field is STATIC */ public abstract boolean isStatic(); /** @return The type of this field */ public abstract IClass getType() throws CompileException; /** @return The name this field */ public abstract String getName(); /** @return The descriptor of this field */ public String getDescriptor() throws CompileException { return this.getType().getDescriptor(); } /** * Returns the value of the field if it is a compile-time constant value, i.e. the field is FINAL and its * initializer is a constant expression (JLS7 15.28, bullet 12). */ public abstract Object getConstantValue() throws CompileException; @Override public String toString() { return this.getDeclaringIClass().toString() + "." + this.getName(); } } /** * This class caches the declared methods in order to minimize the invocations of {@link #getDeclaredIMethods2()}. */ public void invalidateMethodCaches() { this.declaredIMethodsCache = null; this.declaredIMethodCache = null; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy