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

org.aion.avm.core.ArraySuperResolver Maven / Gradle / Ivy

There is a newer version: 0.9.6
Show newest version
package org.aion.avm.core;

import org.aion.avm.ArrayRenamer;
import org.aion.avm.ArrayUtil;
import org.aion.avm.NameStyle;
import org.aion.avm.core.exceptionwrapping.ExceptionWrapperNameMapper;
import org.aion.avm.core.types.ClassHierarchy;
import org.aion.avm.core.types.CommonType;
import i.RuntimeAssertionError;

/**
 * A class that is used to determine a tightest common super class of two types, where at least one
 * of the two types must be an array type (either pre- or post- rename, it does not matter).
 *
 * If this resolver as well as {@link PlainTypeSuperResolver} and {@link PlainTypeSuperResolver} are
 * called on any two types then at least one of the resolvers will return a non-null and therefore
 * valid answer.
 */
public final class ArraySuperResolver {
    private final ClassHierarchy classHierarchy;
    private final ClassRenamer classRenamer;
    private final PlainTypeSuperResolver plainTypeSuperResolver;

    public ArraySuperResolver(ClassHierarchy classHierarchy, ClassRenamer classRenamer) {
        if (classHierarchy == null) {
            throw new NullPointerException("Cannot construct ArraySuperResolver with null class hierarchy.");
        }
        if (classRenamer == null) {
            throw new NullPointerException("Cannot construct ArraySuperResolver with null class renamer.");
        }
        this.classHierarchy = classHierarchy;
        this.classRenamer = classRenamer;
        this.plainTypeSuperResolver = new PlainTypeSuperResolver(this.classHierarchy, this.classRenamer);
    }

    /**
     * Returns a tightest common super class of the two given types if at least one of them is an
     * array type.
     *
     * Returns null if neither of the two given types are arrays.
     *
     * @param type1dotName The first type.
     * @param type2dotName The second type.
     * @return a tightest common super class or null.
     */
    public String getTightestSuperClassIfGivenArray(String type1dotName, String type2dotName) {
        RuntimeAssertionError.assertTrue(type1dotName != null);
        RuntimeAssertionError.assertTrue(type2dotName != null);

        boolean type1isArray = ArrayUtil.isArray(NameStyle.DOT_NAME, type1dotName);
        boolean type2isArray = ArrayUtil.isArray(NameStyle.DOT_NAME, type2dotName);
        boolean type1isSpecialArray = type1isArray && ArrayUtil.isSpecialPostRenameArray(NameStyle.DOT_NAME, type1dotName);
        boolean type2isSpecialArray = type2isArray && ArrayUtil.isSpecialPostRenameArray(NameStyle.DOT_NAME, type2dotName);
        boolean atLeastOneSpecialArray = type1isSpecialArray || type2isSpecialArray;

        if (type1isArray && type2isArray) {
            return atLeastOneSpecialArray ? findSuperOfAtLeastOneSpecialArray(type1dotName, type2dotName) : findSuperOfTwoArrays(type1dotName, type2dotName);
        } else if (type1isArray || type2isArray) {
            return atLeastOneSpecialArray ? findSuperOfAtLeastOneSpecialArray(type1dotName, type2dotName) : findSuperOfArrayAndNonArray(type1dotName, type2dotName);
        } else {
            return null;
        }
    }

    /**
     * A special array is one of our internal types: Array, IArray, ObjectArray, IObjectArray
     *
     * Assumption: at least one of the two arrays is a special array.
     */
    private String findSuperOfAtLeastOneSpecialArray(String array1, String array2) {
        boolean array1isSpecialArray = ArrayUtil.isSpecialPostRenameArray(NameStyle.DOT_NAME, array1);
        boolean array2isSpecialArray = ArrayUtil.isSpecialPostRenameArray(NameStyle.DOT_NAME, array2);

        if (array1isSpecialArray ^ array2isSpecialArray) {
            return findSuperOfSpecialArrayAndOther(array1, array2);
        } else {
            // Special arrays are in the hierarchy, we can query it directly.
            String commonSuper = this.classHierarchy.getTightestCommonSuperClass(array1, array2);

            // If the super class is ambiguous return IObject, otherwise return the super class.
            if (commonSuper == null) {
                return CommonType.I_OBJECT.dotName;
            } else {
                return commonSuper;
            }
        }
    }

    /**
     * Returns a tightest common super class of the two types, under the assumption that both of them
     * are arrays.
     */
    private String findSuperOfTwoArrays(String array1dotName, String array2dotName) {
        boolean array1isPreRename = this.classRenamer.isPreRename(array1dotName);
        boolean array2isPreRename = this.classRenamer.isPreRename(array2dotName);

        if (array1isPreRename && array2isPreRename) {
            return findSuperOfTwoPreRenameArrays(array1dotName, array2dotName);
        } else if (!array1isPreRename && !array2isPreRename) {
            return findSuperOfTwoPostRenameArrays(array1dotName, array2dotName);
        } else {
            // We have a pre- and post-rename array, they can only unify to java.lang.Object
            return CommonType.JAVA_LANG_OBJECT.dotName;
        }
    }

    /**
     * Returns a tightest common super class of the two types, under the assumption that one of them
     * is an array and the other is not.
     *
     * Returns java.lang.Object when:
     * 1. array is pre-rename.
     * 2. one type is pre-rename, other is post-rename.
     * 3. array is post-rename and the non-array is an exception wrapper.
     *
     * Returns IObject when:
     * 1. both types are post-rename and either the array is a unifying object array or the non-array
     *    is an interface.
     *
     * Returns shadow Object otherwise.
     */
    private String findSuperOfArrayAndNonArray(String type1dotName, String type2dotName) {
        String arrayDotName = ArrayUtil.isArray(NameStyle.DOT_NAME, type1dotName) ? type1dotName : type2dotName;
        String nonArrayDotName = arrayDotName.equals(type1dotName) ? type2dotName : type1dotName;

        if (ArrayUtil.isPreRenameArray(arrayDotName)) {
            // If the array is pre-rename then it can only unify to java.lang.Object
            return CommonType.JAVA_LANG_OBJECT.dotName;
        } else {
            if (this.classRenamer.isPreRename(nonArrayDotName)) {
                // Then we have a pre- and post-rename unification, these unify to java.lang.Object
                return CommonType.JAVA_LANG_OBJECT.dotName;
            } else {

                // Exception wrappers descend from java.lang.Throwable so we unify to java.lang.Object
                if (ExceptionWrapperNameMapper.isExceptionWrapperDotName(nonArrayDotName)) {
                    return CommonType.JAVA_LANG_OBJECT.dotName;
                }

                boolean arrayIsUnifyingType = ArrayUtil.isPostRenameUnifyingTypeObjectArray(NameStyle.DOT_NAME, arrayDotName);
                boolean nonArrayIsInterface = this.classHierarchy.postRenameTypeIsInterface(nonArrayDotName);

                if (arrayIsUnifyingType || nonArrayIsInterface) {
                    return CommonType.I_OBJECT.dotName;
                } else {
                    return CommonType.SHADOW_OBJECT.dotName;
                }
            }
        }
    }

    private String findSuperOfTwoPreRenameArrays(String array1preRename, String array2preRename) {
        boolean array1isPrimitiveArray = ArrayUtil.isPreRenamePrimitiveArray(array1preRename);
        boolean array2isPrimitiveArray = ArrayUtil.isPreRenamePrimitiveArray(array2preRename);

        if (array1isPrimitiveArray && array2isPrimitiveArray) {
            // Primitive arrays unify to java.lang.Object only, unless they are equal.
            return array1preRename.equals(array2preRename) ? array1preRename : CommonType.JAVA_LANG_OBJECT.dotName;
        } else if (!array1isPrimitiveArray && !array2isPrimitiveArray) {
            return findSuperOfTwoPreRenameObjectArrays(array1preRename, array2preRename);
        } else {
            // We have a primitive and an object array, they can only unify to java.lang.Object
            return ArrayRenamer.prependPreRenameObjectArrayPrefix(CommonType.JAVA_LANG_OBJECT.dotName, 1);
        }
    }

    private String findSuperOfTwoPostRenameArrays(String array1postRename, String array2postRename) {
        boolean array1isPrimitiveArray = ArrayUtil.isPostRenamePrimitiveArray(NameStyle.DOT_NAME, array1postRename);
        boolean array2isPrimitiveArray = ArrayUtil.isPostRenamePrimitiveArray(NameStyle.DOT_NAME, array2postRename);

        if (array1isPrimitiveArray && array2isPrimitiveArray) {
            // Primitive arrays unify to shadow Object only, unless they are equal.
            return (array1postRename.equals(array2postRename)) ? array1postRename : CommonType.SHADOW_OBJECT.dotName;
        } else if (!array1isPrimitiveArray && !array2isPrimitiveArray) {
            return findSuperOfTwoPostRenameObjectArrays(array1postRename, array2postRename);
        } else {
            // We have a primitive and object array. They unify to IObject if the object array is a unifying type.
            // Otherwise, they unify to shadow Object.
            boolean objectIsUnifyingArray = (array1isPrimitiveArray)
                ? ArrayUtil.isPostRenameUnifyingTypeObjectArray(NameStyle.DOT_NAME, array2postRename)
                : ArrayUtil.isPostRenameUnifyingTypeObjectArray(NameStyle.DOT_NAME, array1postRename);

            return objectIsUnifyingArray ? CommonType.I_OBJECT.dotName : CommonType.SHADOW_OBJECT.dotName;
        }
    }

    private String findSuperOfTwoPostRenameObjectArrays(String array1postRename, String array2postRename) {
        int dimension1 = ArrayUtil.dimensionOfPostRenameObjectArray(NameStyle.DOT_NAME, array1postRename);
        int dimension2 = ArrayUtil.dimensionOfPostRenameObjectArray(NameStyle.DOT_NAME, array2postRename);

        if (dimension1 == dimension2) {
            // They have the same dimension so we get an array of the super class of their base types.
            String baseType1 = ArrayRenamer.getPostRenameObjectArrayWrapperUnderlyingTypeName(NameStyle.DOT_NAME, array1postRename);
            String baseType2 = ArrayRenamer.getPostRenameObjectArrayWrapperUnderlyingTypeName(NameStyle.DOT_NAME, array2postRename);

            // We cannot have an array of array or array of exception wrapper so we have two plain types.
            String superOfBases = this.plainTypeSuperResolver.getTightestSuperClassIfGivenPlainType(baseType1, baseType2);
            RuntimeAssertionError.assertTrue(superOfBases != null);

            // IObject is given when super is ambiguous, in this case we have to wrap as a unifying type.
            boolean superIsIObject = superOfBases.equals(CommonType.I_OBJECT.dotName);
            boolean array1isConcreteType = ArrayUtil.isPostRenameConcreteTypeObjectArray(NameStyle.DOT_NAME, array1postRename);
            boolean array2isConcreteType = ArrayUtil.isPostRenameConcreteTypeObjectArray(NameStyle.DOT_NAME, array2postRename);

            if (!superIsIObject && array1isConcreteType && array2isConcreteType) {
                // We have two concrete types array, they must unify to a concrete type array.
                return superOfBases.equals(CommonType.SHADOW_OBJECT.dotName)
                    ? CommonType.SHADOW_OBJECT.dotName
                    : ArrayRenamer.wrapAsConcreteObjectArray(NameStyle.DOT_NAME, superOfBases, dimension1);

            } else {
                // A unifying array is present so the two must unify to a unifying type.
                return ArrayRenamer.wrapAsUnifyingObjectArray(NameStyle.DOT_NAME, superOfBases, dimension1);
            }
        } else {
            // They differ in dimension, so they can only unify to IObject or shadow Object.
            boolean array1isUnifyingType = ArrayUtil.isPostRenameUnifyingTypeObjectArray(NameStyle.DOT_NAME, array1postRename);
            boolean array2isUnifyingType = ArrayUtil.isPostRenameUnifyingTypeObjectArray(NameStyle.DOT_NAME, array2postRename);

            if (array1isUnifyingType || array2isUnifyingType) {
                return CommonType.I_OBJECT.dotName;
            } else {
                return CommonType.SHADOW_OBJECT.dotName;
            }
        }
    }

    private String findSuperOfTwoPreRenameObjectArrays(String array1, String array2) {
        int dimension1 = ArrayUtil.dimensionOfPreRenameObjectArray(array1);
        int dimension2 = ArrayUtil.dimensionOfPreRenameObjectArray(array2);

        if (dimension1 == dimension2) {
            // They are the same dimension so these will have a complex unification.
            // We need the base types of each of the two arrays so that we can unify them.

            String baseType1 = ArrayRenamer.getPreRenameObjectArrayWrapperUnderlyingTypeName(array1);
            String baseType2 = ArrayRenamer.getPreRenameObjectArrayWrapperUnderlyingTypeName(array2);

            // We are left with two plain types as our base types.
            String superOfBases = this.plainTypeSuperResolver.getTightestSuperClassIfGivenPlainType(baseType1, baseType2);
            RuntimeAssertionError.assertTrue(superOfBases != null);

            return ArrayRenamer.prependPreRenameObjectArrayPrefix(superOfBases, dimension1);
        } else {
            // They differ in dimensions and can only unify to java.lang.Object
            return ArrayRenamer.prependPreRenameObjectArrayPrefix(CommonType.JAVA_LANG_OBJECT.dotName, 1);
        }
    }

    private String findSuperOfSpecialArrayAndOther(String type1dotName, String type2dotName) {
        String specialArray = (ArrayUtil.isSpecialPostRenameArray(NameStyle.DOT_NAME, type1dotName)) ? type1dotName : type2dotName;
        String otherType = specialArray.equals(type1dotName) ? type2dotName : type1dotName;

        if (this.classRenamer.isPreRename(otherType)) {
            return CommonType.JAVA_LANG_OBJECT.dotName;
        } else {

            // Both types are post-rename. Other may still be an array or an exception wrapper.
            if (ExceptionWrapperNameMapper.isExceptionWrapperDotName(otherType)) {
                // Exception wrappers descend from java.lang.Throwable so they can only unify to java.lang.Object here
                return CommonType.JAVA_LANG_OBJECT.dotName;
            } else if (ArrayUtil.isPostRenameArray(NameStyle.DOT_NAME, otherType)) {

                if (ArrayUtil.isPostRenameSingleDimensionPrimitiveArray(NameStyle.DOT_NAME, otherType)) {

                    // The primitive array unifies to the special array if it is its parent, otherwise it
                    // unifies to shadow Object or IObject depending on which special array we have.
                    if (specialArray.equals(CommonType.ARRAY.dotName) || specialArray.equals(CommonType.I_ARRAY.dotName)) {
                        return specialArray;
                    } else if (specialArray.equals(CommonType.OBJECT_ARRAY.dotName)) {
                        return CommonType.SHADOW_OBJECT.dotName;
                    } else if (specialArray.equals(CommonType.I_OBJECT_ARRAY.dotName)) {
                        return CommonType.I_OBJECT.dotName;
                    } else {
                        throw RuntimeAssertionError.unreachable("Expected a special array type: " + specialArray);
                    }

                } else if (ArrayUtil.isPostRenameMultiDimensionPrimitiveArray(NameStyle.DOT_NAME, otherType)) {

                    // Multi-dimensional primitive arrays unify to IObject if the special array is an interface, otherwise shadow Object
                    if (specialArray.equals(CommonType.I_OBJECT_ARRAY.dotName) || specialArray.equals(CommonType.I_ARRAY.dotName)) {
                        return CommonType.I_OBJECT.dotName;
                    } else if (specialArray.equals(CommonType.ARRAY.dotName) || specialArray.equals(CommonType.OBJECT_ARRAY.dotName)) {
                        return CommonType.SHADOW_OBJECT.dotName;
                    } else {
                        throw RuntimeAssertionError.unreachable("Expected a special array type: " + specialArray);
                    }

                } else if (ArrayUtil.isPostRenameConcreteTypeObjectArray(NameStyle.DOT_NAME, otherType)) {

                    // The concrete array unifies to the special array if it is its parent, otherwise it
                    // unifies to shadow Object or IObject depending on which special array we have.
                    if (specialArray.equals(CommonType.OBJECT_ARRAY.dotName) || specialArray.equals(CommonType.I_OBJECT_ARRAY.dotName)) {
                        return specialArray;
                    } else if (specialArray.equals(CommonType.ARRAY.dotName)) {
                        return CommonType.SHADOW_OBJECT.dotName;
                    } else if (specialArray.equals(CommonType.I_ARRAY.dotName)) {
                        return CommonType.I_OBJECT.dotName;
                    } else {
                        throw RuntimeAssertionError.unreachable("Expected a special array type: " + specialArray);
                    }

                } else if (ArrayUtil.isPostRenameUnifyingTypeObjectArray(NameStyle.DOT_NAME, otherType)) {

                    // A unifying array type unifies to its parent IObjectArray or else IObject
                    if (specialArray.equals(CommonType.I_OBJECT_ARRAY.dotName)) {
                        return specialArray;
                    } else if (specialArray.equals(CommonType.OBJECT_ARRAY.dotName) || specialArray.equals(CommonType.I_ARRAY.dotName) || specialArray.equals(CommonType.ARRAY.dotName)) {
                        return CommonType.I_OBJECT.dotName;
                    } else {
                        throw RuntimeAssertionError.unreachable("Expected a special array type: " + specialArray);
                    }

                } else {
                    throw RuntimeAssertionError.unreachable("Expected a post-rename array: " + otherType);
                }
            } else {

                // Then other is not an exception wrapper or an array, just a plain type. The two can
                // only unify to IObject and shadow Object
                boolean specialTypeIsInterface = specialArray.equals(CommonType.I_OBJECT_ARRAY.dotName) || specialArray.equals(CommonType.I_ARRAY.dotName);
                boolean otherTypeIsInterface = this.classHierarchy.postRenameTypeIsInterface(otherType);

                if (specialTypeIsInterface || otherTypeIsInterface) {
                    return CommonType.I_OBJECT.dotName;
                } else {
                    return CommonType.SHADOW_OBJECT.dotName;
                }
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy