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

com.greenpepper.shaded.org.codehaus.janino.ClassFileIClass Maven / Gradle / Ivy

The 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 com.greenpepper.shaded.org.codehaus.janino;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.greenpepper.shaded.org.codehaus.commons.compiler.CompileException;
import com.greenpepper.shaded.org.codehaus.janino.util.ClassFile;
import com.greenpepper.shaded.org.codehaus.janino.util.ClassFile.ConstantClassInfo;

/** A wrapper object that turns a {@link ClassFile} object into an {@link IClass}. */
@SuppressWarnings({ "rawtypes", "unchecked" }) public
class ClassFileIClass extends IClass {
    private static final boolean DEBUG = false;

    private final ClassFile    classFile;
    private final IClassLoader iClassLoader;
    private final short        accessFlags;

    private final Map resolvedFields = new HashMap();

    /**
     * @param classFile Source of data
     * @param iClassLoader {@link IClassLoader} through which to load other classes
     */
    public
    ClassFileIClass(ClassFile classFile, IClassLoader iClassLoader) {
        this.classFile    = classFile;
        this.iClassLoader = iClassLoader;

        // Determine class access flags.
        this.accessFlags = classFile.accessFlags;
    }

    // Implement IClass.

    @Override protected IConstructor[]
    getDeclaredIConstructors2() {
        List iConstructors = new ArrayList();

        for (ClassFile.MethodInfo mi : this.classFile.methodInfos) {
            IInvocable ii;
            try {
                ii = this.resolveMethod(mi);
            } catch (ClassNotFoundException ex) {
                throw new JaninoRuntimeException(ex.getMessage(), ex);
            }
            if (ii instanceof IConstructor) iConstructors.add(ii);
        }

        return (IConstructor[]) iConstructors.toArray(new IConstructor[iConstructors.size()]);
    }

    @Override protected IMethod[]
    getDeclaredIMethods2() {
        List iMethods = new ArrayList();

        for (ClassFile.MethodInfo mi : this.classFile.methodInfos) {

            // Skip JDK 1.5 synthetic methods (e.g. those generated for
            // covariant return values).
            if (Mod.isSynthetic(mi.getModifierFlags())) continue;

            IInvocable ii;
            try {
                ii = this.resolveMethod(mi);
            } catch (ClassNotFoundException ex) {
                throw new JaninoRuntimeException(ex.getMessage(), ex);
            }
            if (ii instanceof IMethod) iMethods.add((IMethod) ii);
        }

        return (IMethod[]) iMethods.toArray(new IMethod[iMethods.size()]);
    }

    @Override protected IField[]
    getDeclaredIFields2() {
        IField[] ifs = new IClass.IField[this.classFile.fieldInfos.size()];
        for (int i = 0; i < this.classFile.fieldInfos.size(); ++i) {
            try {
                ifs[i] = this.resolveField((ClassFile.FieldInfo) this.classFile.fieldInfos.get(i));
            } catch (ClassNotFoundException ex) {
                throw new JaninoRuntimeException(ex.getMessage(), ex);
            }
        }
        return ifs;
    }

    @Override protected IClass[]
    getDeclaredIClasses2() throws CompileException {
        ClassFile.InnerClassesAttribute ica = this.classFile.getInnerClassesAttribute();
        if (ica == null) return new IClass[0];

        List res = new ArrayList();
        for (ClassFile.InnerClassesAttribute.Entry e : ica.getEntries()) {
            if (e.outerClassInfoIndex == this.classFile.thisClass) {
                try {
                    res.add(this.resolveClass(e.innerClassInfoIndex));
                } catch (ClassNotFoundException ex) {
                    throw new CompileException(ex.getMessage(), null); // SUPPRESS CHECKSTYLE AvoidHidingCause
                }
            }
        }
        return (IClass[]) res.toArray(new IClass[res.size()]);
    }

    @Override protected IClass
    getDeclaringIClass2() throws CompileException {
        ClassFile.InnerClassesAttribute ica = this.classFile.getInnerClassesAttribute();
        if (ica == null) return null;

        for (ClassFile.InnerClassesAttribute.Entry e : ica.getEntries()) {
            if (e.innerClassInfoIndex == this.classFile.thisClass) {
                // Is this an anonymous class?
                if (e.outerClassInfoIndex == 0) return null;
                try {
                    return this.resolveClass(e.outerClassInfoIndex);
                } catch (ClassNotFoundException ex) {
                    throw new CompileException(ex.getMessage(), null); // SUPPRESS CHECKSTYLE AvoidHidingCause
                }
            }
        }
        return null;
    }

    @Override protected IClass
    getOuterIClass2() throws CompileException {
        ClassFile.InnerClassesAttribute ica = this.classFile.getInnerClassesAttribute();
        if (ica == null) return null;

        for (ClassFile.InnerClassesAttribute.Entry e : ica.getEntries()) {
            if (e.innerClassInfoIndex == this.classFile.thisClass) {
                if (e.outerClassInfoIndex == 0) {

                    // Anonymous class or local class.
                    // TODO: Determine enclosing instance of anonymous class or local class
                    return null;
                } else  {

                    // Member type.
                    if (Mod.isStatic(e.innerClassAccessFlags)) return null;
                    try {
                        return this.resolveClass(e.outerClassInfoIndex);
                    } catch (ClassNotFoundException ex) {
                        throw new CompileException(ex.getMessage(), null); // SUPPRESS CHECKSTYLE AvoidHidingCause
                    }
                }
            }
        }
        return null;
    }

    @Override protected IClass
    getSuperclass2() throws CompileException {
        if (this.classFile.superclass == 0) return null;
        try {
            return this.resolveClass(this.classFile.superclass);
        } catch (ClassNotFoundException e) {
            throw new CompileException(e.getMessage(), null); // SUPPRESS CHECKSTYLE AvoidHidingCause
        }
    }

    @Override public Access
    getAccess() { return ClassFileIClass.accessFlags2Access(this.accessFlags); }

    @Override public boolean
    isFinal() { return Mod.isFinal(this.accessFlags); }

    @Override protected IClass[]
    getInterfaces2() throws CompileException { return this.resolveClasses(this.classFile.interfaces); }

    @Override public boolean
    isAbstract() { return Mod.isAbstract(this.accessFlags); }

    @Override protected String
    getDescriptor2() { return Descriptor.fromClassName(this.classFile.getThisClassName()); }

    @Override public boolean
    isInterface() { return Mod.isInterface(this.accessFlags); }

    @Override public boolean
    isArray() { return false; }

    @Override public boolean
    isPrimitive() { return false; }

    @Override public boolean
    isPrimitiveNumeric() { return false; }

    @Override protected IClass
    getComponentType2()  { return null; }

    /** Resolves all classes referenced by this class file. */
    public void
    resolveAllClasses() throws ClassNotFoundException {
        for (short i = 0; i < this.classFile.getConstantPoolSize(); ++i) {
            ClassFile.ConstantPoolInfo cpi = this.classFile.getConstantPoolInfo(i);
            if (cpi instanceof ClassFile.ConstantClassInfo) {
                this.resolveClass(i);
            } else
            if (cpi instanceof ClassFile.ConstantNameAndTypeInfo) {
                String descriptor = ((ClassFile.ConstantNameAndTypeInfo) cpi).getDescriptor(this.classFile);
                if (descriptor.charAt(0) == '(') {
                    MethodDescriptor md = new MethodDescriptor(descriptor);
                    this.resolveClass(md.returnFd);
                    for (String parameterFd : md.parameterFds) this.resolveClass(parameterFd);
                } else {
                    this.resolveClass(descriptor);
                }
            }
        }
    }

    /** @param index Index of the CONSTANT_Class_info to resolve (JVMS 4.4.1) */
    private IClass
    resolveClass(short index) throws ClassNotFoundException {
        if (ClassFileIClass.DEBUG) System.out.println("index=" + index);
        ConstantClassInfo cci = (ConstantClassInfo) this.classFile.getConstantPoolInfo(index);
        return this.resolveClass(Descriptor.fromInternalForm(cci.getName(this.classFile)));
    }

    private IClass
    resolveClass(String descriptor) throws ClassNotFoundException {
        if (ClassFileIClass.DEBUG) System.out.println("descriptor=" + descriptor);

        IClass result = (IClass) this.resolvedClasses.get(descriptor);
        if (result != null) return result;

        result = this.iClassLoader.loadIClass(descriptor);
        if (result == null) throw new ClassNotFoundException(descriptor);

        this.resolvedClasses.put(descriptor, result);
        return result;
    }
    private final Map resolvedClasses = new HashMap();

    private IClass[]
    resolveClasses(short[] ifs) throws CompileException {
        IClass[] result = new IClass[ifs.length];
        for (int i = 0; i < result.length; ++i) {
            try {
                result[i] = this.resolveClass(ifs[i]);
            } catch (ClassNotFoundException e) {
                throw new CompileException(e.getMessage(), null); // SUPPRESS CHECKSTYLE AvoidHidingCause
            }
        }
        return result;
    }

    /**
     * Turn a {@link ClassFile.MethodInfo} into an {@link IInvocable}. This includes the checking and the
     * removal of the magic first parameter of an inner class constructor.
     *
     * @param methodInfo
     * @throws ClassNotFoundException
     */
    private IInvocable
    resolveMethod(final ClassFile.MethodInfo methodInfo) throws ClassNotFoundException {
        IInvocable result = (IInvocable) this.resolvedMethods.get(methodInfo);
        if (result != null) return result;

        // Determine method name.
        final String name = methodInfo.getName();

        // Determine return type.
        MethodDescriptor md = new MethodDescriptor(methodInfo.getDescriptor());

        final IClass returnType = this.resolveClass(md.returnFd);

        // Determine parameter types.
        final IClass[] parameterTypes = new IClass[md.parameterFds.length];
        for (int i = 0; i < parameterTypes.length; ++i) parameterTypes[i] = this.resolveClass(md.parameterFds[i]);

        // Determine thrown exceptions.
        IClass[]                  tes = null;
        ClassFile.AttributeInfo[] ais = methodInfo.getAttributes();
        for (ClassFile.AttributeInfo ai : ais) {
            if (ai instanceof ClassFile.ExceptionsAttribute) {
                ConstantClassInfo[] ccis = ((ClassFile.ExceptionsAttribute) ai).getExceptions(this.classFile);
                tes = new IClass[ccis.length];
                for (int i = 0; i < tes.length; ++i) {
                    tes[i] = this.resolveClass(Descriptor.fromInternalForm(ccis[i].getName(this.classFile)));
                }
            }
        }
        final IClass[] thrownExceptions = tes == null ? new IClass[0] : tes;

        // Determine access.
        final Access access = ClassFileIClass.accessFlags2Access(methodInfo.getModifierFlags());

        if ("".equals(name)) {
            result = new IClass.IConstructor() {

                @Override public boolean
                isVarargs() { return Mod.isVarargs(methodInfo.getModifierFlags()); }

                @Override public IClass[]
                getParameterTypes2() throws CompileException {

                    // Process magic first parameter of inner class constructor.
                    IClass outerIClass = ClassFileIClass.this.getOuterIClass();
                    if (outerIClass != null) {
                        if (parameterTypes.length < 1) {
                            throw new JaninoRuntimeException("Inner class constructor lacks magic first parameter");
                        }
                        if (parameterTypes[0] != outerIClass) {
                            throw new JaninoRuntimeException(
                                "Magic first parameter of inner class constructor has type \""
                                + parameterTypes[0].toString()
                                + "\" instead of that of its enclosing instance (\""
                                + outerIClass.toString()
                                + "\")"
                            );
                        }
                        IClass[] tmp = new IClass[parameterTypes.length - 1];
                        System.arraycopy(parameterTypes, 1, tmp, 0, tmp.length);
                        return tmp;
                    }

                    return parameterTypes;
                }

                @Override public IClass[]          getThrownExceptions2() { return thrownExceptions; }
                @Override public Access            getAccess()            { return access; }
                @Override public Java.Annotation[] getAnnotations()       { return methodInfo.getAnnotations(); }
            };
        } else {
            result = new IClass.IMethod() {

                @Override public String
                getName() { return name; }

                @Override public IClass
                getReturnType() { return returnType; }

                @Override public boolean
                isStatic() { return Mod.isStatic(methodInfo.getModifierFlags()); }

                @Override public boolean
                isAbstract() { return Mod.isAbstract(methodInfo.getModifierFlags()); }

                @Override public boolean
                isVarargs() { return Mod.isVarargs(methodInfo.getModifierFlags()); }

                @Override public IClass[]
                getParameterTypes2() { return parameterTypes; }

                @Override public IClass[]
                getThrownExceptions2() { return thrownExceptions; }

                @Override public Access
                getAccess() { return access; }

                @Override public Java.Annotation[]
                getAnnotations() { return methodInfo.getAnnotations(); }
            };
        }
        this.resolvedMethods.put(methodInfo, result);
        return result;
    }
    private final Map resolvedMethods = new HashMap();

    private IField
    resolveField(final ClassFile.FieldInfo fieldInfo) throws ClassNotFoundException {
        IField result = (IField) this.resolvedFields.get(fieldInfo);
        if (result != null) return result;

        // Determine field name.
        final String name = fieldInfo.getName(this.classFile);

        // Determine field type.
        final String descriptor = fieldInfo.getDescriptor(this.classFile);
        final IClass type       = this.resolveClass(descriptor);

        // Determine optional "constant value" of the field (JLS7 15.28, bullet 14). If a field has a "ConstantValue"
        // attribute, we assume that it has a constant value. Notice that this assumption is not always correct,
        // because typical Java™ compilers do not generate a "ConstantValue" attribute for fields like
        // "int RED = 0", because "0" is the default value for an integer field.
        ClassFile.ConstantValueAttribute cva = null;
        for (ClassFile.AttributeInfo ai : fieldInfo.getAttributes()) {
            if (ai instanceof ClassFile.ConstantValueAttribute) {
                cva = (ClassFile.ConstantValueAttribute) ai;
                break;
            }
        }

        final Object optionalConstantValue = cva == null ? IClass.NOT_CONSTANT : cva.getConstantValue(this.classFile);
        final Access access                = ClassFileIClass.accessFlags2Access(fieldInfo.getModifierFlags());

        result = new IField() {
            @Override public Object            getConstantValue() { return optionalConstantValue; }
            @Override public String            getName()          { return name; }
            @Override public IClass            getType()          { return type; }
            @Override public boolean           isStatic()         { return Mod.isStatic(fieldInfo.getModifierFlags()); }
            @Override public Access            getAccess()        { return access; }
            @Override public Java.Annotation[] getAnnotations()   { return fieldInfo.getAnnotations(); }
        };
        this.resolvedFields.put(fieldInfo, result);
        return result;
    }

    private static Access
    accessFlags2Access(short accessFlags) {
        return (
            Mod.isPublicAccess(accessFlags)      ? Access.PUBLIC
            : Mod.isProtectedAccess(accessFlags) ? Access.PROTECTED
            : Mod.isPrivateAccess(accessFlags)   ? Access.PRIVATE
            : Access.DEFAULT
        );
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy