/// <summary>Construct a ClassFileWriter for a class.</summary> /// <remarks>Construct a ClassFileWriter for a class.</remarks> /// <param name="className"> /// the name of the class to write, including /// full package qualification. /// </param> /// <param name="superClassName"> /// the name of the superclass of the class /// to write, including full package qualification. /// </param> /// <param name="sourceFileName"> /// the name of the source file to use for /// producing debug information, or null if debug information /// is not desired /// </param> public ClassFileWriter(string className, string superClassName, string sourceFileName) { generatedClassName = className; itsConstantPool = new ConstantPool(this); itsThisClassIndex = itsConstantPool.AddClass(className); itsSuperClassIndex = itsConstantPool.AddClass(superClassName); if (sourceFileName != null) { itsSourceFileNameIndex = itsConstantPool.AddUtf8(sourceFileName); } // All "new" implementations are supposed to output ACC_SUPER as a // class flag. This is specified in the first JVM spec, so it should // be old enough that it's okay to always set it. itsFlags = ACC_PUBLIC | ACC_SUPER; }
internal static void Print(int[] locals, int localsTop, int[] stack, int stackTop, ConstantPool pool) { System.Console.Out.Write("locals: "); System.Console.Out.WriteLine(ToString(locals, localsTop, pool)); System.Console.Out.Write("stack: "); System.Console.Out.WriteLine(ToString(stack, stackTop, pool)); System.Console.Out.WriteLine(); }
internal static void Print(int[] locals, int[] stack, ConstantPool pool) { Print(locals, locals.Length, stack, stack.Length, pool); }
internal static string ToString(int[] types, int typesTop, ConstantPool pool) { StringBuilder sb = new StringBuilder(); sb.Append("["); for (int i = 0; i < typesTop; i++) { if (i > 0) { sb.Append(", "); } sb.Append(ToString(types[i], pool)); } sb.Append("]"); return sb.ToString(); }
internal static string ToString(int[] types, ConstantPool pool) { return ToString(types, types.Length, pool); }
/// <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)); }
internal static string ToString(int type, ConstantPool pool) { int tag = GetTag(type); switch (tag) { case Org.Mozilla.Classfile.TypeInfo.TOP: { return "top"; } case Org.Mozilla.Classfile.TypeInfo.INTEGER: { return "int"; } case Org.Mozilla.Classfile.TypeInfo.FLOAT: { return "float"; } case Org.Mozilla.Classfile.TypeInfo.DOUBLE: { return "double"; } case Org.Mozilla.Classfile.TypeInfo.LONG: { return "long"; } case Org.Mozilla.Classfile.TypeInfo.NULL: { return "null"; } case Org.Mozilla.Classfile.TypeInfo.UNINITIALIZED_THIS: { return "uninitialized_this"; } default: { if (tag == Org.Mozilla.Classfile.TypeInfo.OBJECT_TAG) { return GetPayloadAsType(type, pool); } else { if (tag == Org.Mozilla.Classfile.TypeInfo.UNINITIALIZED_VAR_TAG) { return "uninitialized"; } else { throw new ArgumentException("bad type"); } } break; } } }
/// <summary>Create type information from an internal type.</summary> /// <remarks>Create type information from an internal type.</remarks> internal static int FromType(string type, ConstantPool pool) { if (type.Length == 1) { switch (type[0]) { case 'B': case 'C': case 'S': case 'Z': case 'I': { // sbyte // unicode char // short // boolean // all of the above are verified as integers return INTEGER; } case 'D': { return DOUBLE; } case 'F': { return FLOAT; } case 'J': { return LONG; } default: { throw new ArgumentException("bad type"); } } } return Org.Mozilla.Classfile.TypeInfo.OBJECT(type, 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"); }
internal static int OBJECT(string type, ConstantPool pool) { return OBJECT(pool.AddClass(type)); }
/// <summary>Merge an operand stack or local variable array with incoming state.</summary> /// <remarks> /// Merge an operand stack or local variable array with incoming state. /// They are treated the same way; by this point, it should already be /// ensured that the array sizes are the same, which is the only additional /// constraint that is imposed on merging operand stacks (the local variable /// array is always the same size). /// </remarks> private bool MergeState(int[] current, int[] incoming, int size, ConstantPool pool) { bool changed = false; for (int i = 0; i < size; i++) { int currentType = current[i]; current[i] = TypeInfo.Merge(current[i], incoming[i], pool); if (currentType != current[i]) { changed = true; } } return changed; }
internal bool Merge(int[] locals, int localsTop, int[] stack, int stackTop, ConstantPool pool) { if (!isInitialized) { System.Array.Copy(locals, 0, this.locals, 0, localsTop); this.stack = new int[stackTop]; System.Array.Copy(stack, 0, this.stack, 0, stackTop); isInitialized = true; return true; } else { if (this.locals.Length == localsTop && this.stack.Length == stackTop) { bool localsChanged = MergeState(this.locals, locals, localsTop, pool); bool stackChanged = MergeState(this.stack, stack, stackTop, pool); return localsChanged || stackChanged; } else { throw new ArgumentException("bad merge attempt"); } } }