/// <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));
}