--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.iapi.services.classfile.ClassInvestigator\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.iapi.services.classfile;\r
+\r
+\r
+import java.io.InputStream;\r
+import java.util.Enumeration;\r
+\r
+import java.io.IOException;\r
+import java.util.Vector;\r
+\r
+import org.apache.derby.iapi.services.classfile.VMDescriptor;\r
+import org.apache.derby.iapi.services.classfile.VMDescriptor;\r
+import java.util.HashSet;\r
+\r
+import java.util.Hashtable;\r
+import java.util.Vector;\r
+import java.util.Enumeration;\r
+import java.util.Collections;\r
+\r
+\r
+/** \r
+*/\r
+\r
+public class ClassInvestigator extends ClassHolder {\r
+\r
+ public static ClassInvestigator load(InputStream is)\r
+ throws IOException {\r
+\r
+ ClassInput classInput = new ClassInput(is);\r
+\r
+ // Check the header\r
+ int magic = classInput.getU4();\r
+ int minor_version = classInput.getU2();\r
+ int major_version = classInput.getU2();\r
+\r
+ if (magic != VMDescriptor.JAVA_CLASS_FORMAT_MAGIC)\r
+ throw new ClassFormatError();\r
+\r
+ // Read in the Constant Pool\r
+ int constantPoolCount = classInput.getU2();\r
+\r
+ ClassInvestigator ci = new ClassInvestigator(constantPoolCount);\r
+ \r
+ ci.minor_version = minor_version;\r
+ ci.major_version = major_version; \r
+ \r
+ // Yes, index starts at 1, The '0'th constant pool entry\r
+ // is reserved for the JVM and is not present in the class file.\r
+ for (int i = 1; i < constantPoolCount; ) {\r
+ ConstantPoolEntry item = ClassInvestigator.getConstant(classInput);\r
+ i += ci.addEntry(item.getKey(), item);\r
+ }\r
+\r
+ // Read in access_flags and class indexes\r
+ ci.access_flags = classInput.getU2();\r
+ ci.this_class = classInput.getU2();\r
+ ci.super_class = classInput.getU2();\r
+\r
+ // interfaces is a simple int array\r
+ int interfaceCount = classInput.getU2();\r
+ if (interfaceCount != 0) {\r
+ ci.interfaces = new int[interfaceCount];\r
+ for (int i = 0; i < interfaceCount; i++)\r
+ ci.interfaces[i] = classInput.getU2();\r
+ }\r
+\r
+ int fieldCount = classInput.getU2();\r
+ if (fieldCount != 0) {\r
+ ci.field_info = new MemberTable(fieldCount);\r
+ for (int i = 0; i < fieldCount; i++)\r
+ {\r
+ ci.field_info.addEntry(readClassMember(ci, classInput));\r
+ }\r
+ }\r
+\r
+ int methodCount = classInput.getU2();\r
+ if (methodCount != 0) {\r
+ ci.method_info = new MemberTable(methodCount);\r
+ for (int i = 0; i < methodCount; i++)\r
+ {\r
+ ci.method_info.addEntry(readClassMember(ci, classInput));\r
+ }\r
+ }\r
+\r
+ int attributeCount = classInput.getU2();\r
+ if (attributeCount != 0) {\r
+ ci.attribute_info = new Attributes(attributeCount);\r
+\r
+ for (int i = 0; i < attributeCount; i++)\r
+ ci.attribute_info.addEntry(new AttributeEntry(classInput));\r
+ }\r
+ return ci;\r
+\r
+ }\r
+\r
+ private static ClassMember readClassMember(ClassInvestigator ci, ClassInput in)\r
+ throws IOException {\r
+\r
+ ClassMember member = new ClassMember(ci, in.getU2(), in.getU2(), in.getU2());\r
+\r
+ int attributeCount = in.getU2();\r
+ if (attributeCount != 0) {\r
+ member.attribute_info = new Attributes(attributeCount);\r
+ for (int i = 0; i < attributeCount; i++)\r
+ member.attribute_info.addEntry(new AttributeEntry(in));\r
+ }\r
+ \r
+ return member;\r
+ }\r
+\r
+ /*\r
+ ** Constructors.\r
+ */\r
+\r
+ private ClassInvestigator(int constantPoolCount) {\r
+ super(constantPoolCount);\r
+ }\r
+\r
+ /*\r
+ ** Methods to investigate this class\r
+ */\r
+\r
+\r
+ public Enumeration implementedInterfaces()\r
+ {\r
+ int interfaceCount = interfaces == null ? 0 : interfaces.length;\r
+ Vector implemented = new Vector(interfaceCount);\r
+\r
+ for (int i = 0; i < interfaceCount; i++)\r
+ {\r
+ implemented.addElement(className(interfaces[i]));\r
+ }\r
+ return implemented.elements();\r
+ }\r
+ public Enumeration getFields() {\r
+ if (field_info == null)\r
+ return Collections.enumeration(Collections.EMPTY_LIST);\r
+\r
+ return field_info.entries.elements();\r
+ }\r
+\r
+ public Enumeration getMethods() {\r
+ if (method_info == null)\r
+ return Collections.enumeration(Collections.EMPTY_LIST);\r
+ return method_info.entries.elements();\r
+ }\r
+\r
+ public Enumeration referencedClasses() {\r
+ return getClasses(getMethods(), getFields() );\r
+ }\r
+\r
+ /**\r
+ Return an Enumeration of all referenced classes\r
+ */\r
+\r
+ private Enumeration getClasses(Enumeration methods, Enumeration fields)\r
+ {\r
+ return new ClassEnumeration(this, cptEntries.elements(), methods, fields);\r
+ }\r
+\r
+ public Enumeration getStrings() {\r
+ HashSet strings = new HashSet(30, 0.8f);\r
+ \r
+ int size = cptEntries.size();\r
+ for (int i = 1; i < size; i++) {\r
+ ConstantPoolEntry cpe = getEntry(i);\r
+\r
+ if ((cpe == null) || (cpe.getTag() != VMDescriptor.CONSTANT_String))\r
+ continue;\r
+\r
+ CONSTANT_Index_info cii = (CONSTANT_Index_info) cpe;\r
+\r
+ strings.add(nameIndexToString(cii.getI1()));\r
+ }\r
+\r
+ return java.util.Collections.enumeration(strings);\r
+ }\r
+\r
+ public ClassMember getMember(String simpleName, String descriptor) {\r
+\r
+ if (descriptor.startsWith("(")) {\r
+ if (method_info == null)\r
+ return null;\r
+ return method_info.find(simpleName, descriptor);\r
+ }\r
+ else {\r
+ if (field_info == null)\r
+ return null;\r
+ return field_info.find(simpleName, descriptor);\r
+ }\r
+ }\r
+\r
+ /**\r
+ Return an Enumeration of all Member References\r
+ */\r
+/*\r
+ Enumeration getMemberReferences() {\r
+ return new ReferenceEnumeration(this, elements());\r
+ }\r
+*/\r
+\r
+ /*\r
+ ** Methods to modify the class.\r
+ */\r
+ // remove all atttributes that are not essential\r
+ public void removeAttributes() throws IOException {\r
+\r
+ // Class level attributes\r
+ if (attribute_info != null) {\r
+ for (int i = attribute_info.size() - 1; i >= 0 ; i--) {\r
+\r
+ AttributeEntry ae = (AttributeEntry) attribute_info.elementAt(i);\r
+ String name = nameIndexToString(ae.getNameIndex());\r
+ if (name.equals("SourceFile"))\r
+ attribute_info.removeElementAt(i);\r
+ else if (name.equals("InnerClasses"))\r
+ ; // leave in\r
+ else\r
+ System.err.println("WARNING - Unknown Class File attribute " + name);\r
+ }\r
+\r
+ if (attribute_info.size() == 0)\r
+ attribute_info = null;\r
+ }\r
+ attribute_info = null;\r
+\r
+ // fields\r
+ for (Enumeration e = getFields(); e.hasMoreElements(); ) {\r
+ ClassMember member = (ClassMember) e.nextElement();\r
+\r
+ Attributes attrs = member.attribute_info;\r
+\r
+ if (attrs != null) {\r
+\r
+ for (int i = attrs.size() - 1; i >= 0 ; i--) {\r
+\r
+ AttributeEntry ae = (AttributeEntry) attrs.elementAt(i);\r
+ String name = nameIndexToString(ae.getNameIndex());\r
+ if (name.equals("ConstantValue"))\r
+ ; // leave in\r
+ else if (name.equals("Synthetic"))\r
+ ; // leave in\r
+ else\r
+ System.err.println("WARNING - Unknown Field attribute " + name);\r
+ }\r
+\r
+ if (attrs.size() == 0)\r
+ member.attribute_info = null;\r
+ }\r
+\r
+ }\r
+\r
+ // methods\r
+ for (Enumeration e = getMethods(); e.hasMoreElements(); ) {\r
+ ClassMember member = (ClassMember) e.nextElement();\r
+\r
+ Attributes attrs = member.attribute_info;\r
+\r
+ if (attrs != null) {\r
+\r
+ for (int i = attrs.size() - 1; i >= 0 ; i--) {\r
+\r
+ AttributeEntry ae = (AttributeEntry) attrs.elementAt(i);\r
+ String name = nameIndexToString(ae.getNameIndex());\r
+ if (name.equals("Code"))\r
+ processCodeAttribute(member, ae);\r
+ else if (name.equals("Exceptions"))\r
+ ; // leave in\r
+ else if (name.equals("Deprecated"))\r
+ ; // leave in\r
+ else if (name.equals("Synthetic"))\r
+ ; // leave in\r
+ else\r
+ System.err.println("WARNING - Unknown method attribute " + name);\r
+ }\r
+\r
+ if (attrs.size() == 0)\r
+ member.attribute_info = null;\r
+ }\r
+\r
+ }\r
+ }\r
+\r
+ private void processCodeAttribute(ClassMember member, AttributeEntry ae) throws IOException {\r
+\r
+ ClassInput ci = new ClassInput(new java.io.ByteArrayInputStream(ae.infoIn));\r
+\r
+\r
+ ci.skipBytes(4); // puts us at code_length\r
+ int len = ci.getU4();\r
+ ci.skipBytes(len); // puts us at exception_table_length\r
+ int count = ci.getU2();\r
+ if (count != 0)\r
+ ci.skipBytes(8 * count);\r
+\r
+ int nonAttrLength = 4 + 4 + len + 2 + (8 * count);\r
+\r
+ // now at attributes\r
+\r
+ count = ci.getU2();\r
+ if (count == 0)\r
+ return;\r
+\r
+ int newCount = count;\r
+ for (int i = 0; i < count; i++) {\r
+\r
+ int nameIndex = ci.getU2();\r
+ String name = nameIndexToString(nameIndex);\r
+ if (name.equals("LineNumberTable") || name.equals("LocalVariableTable"))\r
+ newCount--;\r
+ else\r
+ System.err.println("ERROR - Unknown code attribute " + name);\r
+\r
+ len = ci.getU4();\r
+ ci.skipBytes(len);\r
+ }\r
+\r
+ if (newCount != 0) {\r
+ System.err.println("ERROR - expecting all code attributes to be removed");\r
+ System.exit(1);\r
+ }\r
+\r
+ // this is only coded for all attributes within a Code attribute being removed.\r
+\r
+ byte[] newInfo = new byte[nonAttrLength + 2];\r
+ System.arraycopy(ae.infoIn, 0, newInfo, 0, nonAttrLength);\r
+ // last two bytes are left at 0 which means 0 attributes\r
+ ae.infoIn = newInfo;\r
+ }\r
+\r
+ public void renameClassElements(Hashtable classNameMap, Hashtable memberNameMap) {\r
+\r
+ // this & super class\r
+ renameString(classNameMap, (CONSTANT_Index_info) getEntry(this_class));\r
+ renameString(classNameMap, (CONSTANT_Index_info) getEntry(super_class));\r
+\r
+ // implemented interfaces\r
+ // handled by Class entries below\r
+\r
+ // classes & Strings\r
+ // descriptors\r
+ int size = cptEntries.size();\r
+ for (int i = 1; i < size; i++) {\r
+ ConstantPoolEntry cpe = getEntry(i);\r
+\r
+ if (cpe == null)\r
+ continue;\r
+\r
+ switch (cpe.getTag()) {\r
+ case VMDescriptor.CONSTANT_String:\r
+ case VMDescriptor.CONSTANT_Class:\r
+ {\r
+ CONSTANT_Index_info cii = (CONSTANT_Index_info) cpe;\r
+ renameString(classNameMap, cii);\r
+ break;\r
+ }\r
+ case VMDescriptor.CONSTANT_NameAndType:\r
+ {\r
+ CONSTANT_Index_info cii = (CONSTANT_Index_info) cpe;\r
+ String newDescriptor = newDescriptor(classNameMap, nameIndexToString(cii.getI2()));\r
+ if (newDescriptor != null) {\r
+ doRenameString(cii.getI2(), newDescriptor);\r
+ }\r
+ break;\r
+ }\r
+\r
+ default:\r
+ continue;\r
+ }\r
+\r
+ }\r
+\r
+ //System.out.println("Starting Fields");\r
+\r
+ // now the methods & fields, only descriptors at this time\r
+ renameMembers(getFields(), classNameMap, memberNameMap);\r
+\r
+ renameMembers(getMethods(), classNameMap, memberNameMap);\r
+ }\r
+\r
+ private void renameMembers(Enumeration e, Hashtable classNameMap, Hashtable memberNameMap) {\r
+\r
+ for (; e.hasMoreElements(); ) {\r
+ ClassMember member = (ClassMember) e.nextElement();\r
+\r
+ String oldMemberName = nameIndexToString(member.name_index);\r
+ String newMemberName = (String) memberNameMap.get(oldMemberName);\r
+ if (newMemberName != null)\r
+ doRenameString(member.name_index, newMemberName);\r
+\r
+ String newDescriptor = newDescriptor(classNameMap, nameIndexToString(member.descriptor_index));\r
+ if (newDescriptor != null) {\r
+ doRenameString(member.descriptor_index, newDescriptor);\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ private void renameString(Hashtable classNameMap, CONSTANT_Index_info cii) {\r
+\r
+ int index = cii.getI1();\r
+\r
+ String name = nameIndexToString(index);\r
+ String newName = (String) classNameMap.get(name);\r
+ if (newName != null) {\r
+\r
+ doRenameString(index, newName);\r
+\r
+ return;\r
+ }\r
+\r
+ // have to look for arrays\r
+ if (cii.getTag() == VMDescriptor.CONSTANT_Class) {\r
+\r
+ if (name.charAt(0) == '[') {\r
+ int classOffset = name.indexOf('L') + 1;\r
+\r
+ String baseClassName = name.substring(classOffset, name.length() - 1);\r
+\r
+\r
+ newName = (String) classNameMap.get(baseClassName);\r
+\r
+ if (newName != null) {\r
+\r
+ String newArrayClassName = name.substring(0, classOffset) + newName + ";";\r
+\r
+ doRenameString(index, newArrayClassName);\r
+\r
+ }\r
+\r
+ }\r
+ }\r
+ }\r
+\r
+ private void doRenameString(int index, String newName) {\r
+ ConstantPoolEntry cpe = getEntry(index);\r
+ if (cpe.getTag() != VMDescriptor.CONSTANT_Utf8)\r
+ throw new RuntimeException("unexpected type " + cpe);\r
+\r
+ CONSTANT_Utf8_info newCpe = new CONSTANT_Utf8_info(newName);\r
+\r
+ cptHashTable.remove(cpe.getKey());\r
+ cptHashTable.put(newCpe.getKey(), newCpe);\r
+\r
+ newCpe.index = index;\r
+\r
+ cptEntries.setElementAt(newCpe, index);\r
+ }\r
+\r
+ private static ConstantPoolEntry getConstant(ClassInput in)\r
+ throws IOException {\r
+\r
+ ConstantPoolEntry item;\r
+ int tag; \r
+ tag = in.readUnsignedByte();\r
+\r
+ switch (tag) {\r
+ case VMDescriptor.CONSTANT_Class:\r
+ case VMDescriptor.CONSTANT_String:\r
+ item = new CONSTANT_Index_info(tag, in.getU2(), 0);\r
+ break;\r
+\r
+ case VMDescriptor.CONSTANT_NameAndType:\r
+ case VMDescriptor.CONSTANT_Fieldref:\r
+ case VMDescriptor.CONSTANT_Methodref:\r
+ case VMDescriptor.CONSTANT_InterfaceMethodref:\r
+ item = new CONSTANT_Index_info(tag, in.getU2(), in.getU2());\r
+ break;\r
+\r
+ case VMDescriptor.CONSTANT_Integer:\r
+ item = new CONSTANT_Integer_info(in.getU4());\r
+ break;\r
+\r
+ case VMDescriptor.CONSTANT_Float:\r
+ item = new CONSTANT_Float_info(in.readFloat());\r
+ break;\r
+\r
+ case VMDescriptor.CONSTANT_Long:\r
+ item = new CONSTANT_Long_info(in.readLong());\r
+ break;\r
+\r
+ case VMDescriptor.CONSTANT_Double:\r
+ item = new CONSTANT_Double_info(in.readDouble());\r
+ break;\r
+\r
+ case VMDescriptor.CONSTANT_Utf8:\r
+ item = new CONSTANT_Utf8_info(in.readUTF());\r
+ break;\r
+ default:\r
+ throw new ClassFormatError();\r
+ }\r
+\r
+ return item;\r
+ }\r
+ public static String newDescriptor(Hashtable classNameMap, String descriptor) {\r
+\r
+ String newDescriptor = null;\r
+\r
+ int dlen = descriptor.length();\r
+ for (int offset = 0; offset < dlen; ) {\r
+ char c = descriptor.charAt(offset);\r
+ switch (c) {\r
+ case VMDescriptor.C_VOID :\r
+ case VMDescriptor.C_BOOLEAN:\r
+ case VMDescriptor.C_BYTE:\r
+ case VMDescriptor.C_CHAR:\r
+ case VMDescriptor.C_SHORT:\r
+ case VMDescriptor.C_INT:\r
+ case VMDescriptor.C_LONG:\r
+ case VMDescriptor.C_FLOAT:\r
+ case VMDescriptor.C_DOUBLE:\r
+ case VMDescriptor.C_ARRAY:\r
+ case VMDescriptor.C_METHOD:\r
+ case VMDescriptor.C_ENDMETHOD:\r
+ default:\r
+ offset++;\r
+ continue;\r
+\r
+ case VMDescriptor.C_CLASS:\r
+ {\r
+ int startOffset = offset;\r
+ while (descriptor.charAt(offset++) != VMDescriptor.C_ENDCLASS)\r
+ ;\r
+ int endOffset = offset;\r
+\r
+ // name includes L and ;\r
+ String name = descriptor.substring(startOffset, endOffset);\r
+ String newName = (String) classNameMap.get(name);\r
+ if (newName != null) {\r
+ if (newDescriptor == null)\r
+ newDescriptor = descriptor;\r
+\r
+ // we just replace the first occurance of it,\r
+ // the loop will hit any next occurance.\r
+ int startPos = newDescriptor.indexOf(name);\r
+\r
+ String tmp;\r
+ if (startPos == 0)\r
+ tmp = newName;\r
+ else\r
+ tmp = newDescriptor.substring(0, startPos) +\r
+ newName;\r
+\r
+ int endPos = startPos + name.length();\r
+\r
+ if (endPos < newDescriptor.length()) {\r
+\r
+ tmp += newDescriptor.substring(endPos , newDescriptor.length());\r
+ }\r
+\r
+\r
+ newDescriptor = tmp;\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ }\r
+ //if (newDescriptor != null) {\r
+ // System.out.println("O - " + descriptor);\r
+ // System.out.println("N - " + newDescriptor);\r
+ //}\r
+ return newDescriptor;\r
+ }\r
+}\r