/// <summary>Merge two verification types.</summary> /// <remarks> /// Merge two verification types. /// In most cases, the verification types must be the same. For example, /// INTEGER and DOUBLE cannot be merged and an exception will be thrown. /// The basic rules are: /// - If the types are equal, simply return one. /// - If either type is TOP, return TOP. /// - If either type is NULL, return the other type. /// - If both types are objects, find the lowest common ancestor in the /// class hierarchy. /// This method uses reflection to traverse the class hierarchy. Therefore, /// it is assumed that the current class being generated is never the target /// of a full object-object merge, which would need to load the current /// class reflectively. /// </remarks> internal static int Merge(int current, int incoming, ConstantPool pool) { int currentTag = GetTag(current); int incomingTag = GetTag(incoming); bool currentIsObject = currentTag == Org.Mozilla.Classfile.TypeInfo.OBJECT_TAG; bool incomingIsObject = incomingTag == Org.Mozilla.Classfile.TypeInfo.OBJECT_TAG; if (current == incoming || (currentIsObject && incoming == NULL)) { return current; } else { if (currentTag == Org.Mozilla.Classfile.TypeInfo.TOP || incomingTag == Org.Mozilla.Classfile.TypeInfo.TOP) { return Org.Mozilla.Classfile.TypeInfo.TOP; } else { if (current == NULL && incomingIsObject) { return incoming; } else { if (currentIsObject && incomingIsObject) { string currentName = GetPayloadAsType(current, pool); string incomingName = GetPayloadAsType(incoming, pool); // The class file always has the class and super names in the same // spot. The constant order is: class_data, class_name, super_data, // super_name. string currentlyGeneratedName = (string)pool.GetConstantData(2); string currentlyGeneratedSuperName = (string)pool.GetConstantData(4); // If any of the merged types are the class that's currently being // generated, automatically start at the super class instead. At // this point, we already know the classes are different, so we // don't need to handle that case. if (currentName.Equals(currentlyGeneratedName)) { currentName = currentlyGeneratedSuperName; } if (incomingName.Equals(currentlyGeneratedName)) { incomingName = currentlyGeneratedSuperName; } Type currentClass = GetClassFromInternalName(currentName); Type incomingClass = GetClassFromInternalName(incomingName); if (currentClass.IsAssignableFrom(incomingClass)) { return current; } else { if (incomingClass.IsAssignableFrom(currentClass)) { return incoming; } else { if (incomingClass.IsInterface || currentClass.IsInterface) { // For verification purposes, Sun specifies that interfaces are // subtypes of Object. Therefore, we know that the merge result // involving interfaces where one is not assignable to the // other results in Object. return OBJECT("java/lang/Object", pool); } else { Type commonClass = incomingClass.BaseType; while (commonClass != null) { if (commonClass.IsAssignableFrom(currentClass)) { string name = commonClass.FullName; name = ClassFileWriter.GetSlashedForm(name); return OBJECT(name, pool); } commonClass = commonClass.BaseType; } } } } } } } } throw new ArgumentException("bad merge attempt between " + ToString(current, pool) + " and " + ToString(incoming, pool)); }
/// <summary> /// Treat the result of getPayload as a constant pool index and fetch the /// corresponding String mapped to it. /// </summary> /// <remarks> /// Treat the result of getPayload as a constant pool index and fetch the /// corresponding String mapped to it. /// Only works on OBJECT types. /// </remarks> internal static string GetPayloadAsType(int typeInfo, ConstantPool pool) { if (GetTag(typeInfo) == OBJECT_TAG) { return (string)pool.GetConstantData(GetPayload(typeInfo)); } throw new ArgumentException("expecting object type"); }