001: /*
002:
003: Derby - Class org.apache.derby.iapi.services.classfile.ClassInvestigator
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.iapi.services.classfile;
023:
024: import java.io.InputStream;
025: import java.util.Enumeration;
026:
027: import java.io.IOException;
028: import java.util.Vector;
029:
030: import org.apache.derby.iapi.services.classfile.VMDescriptor;
031: import org.apache.derby.iapi.services.classfile.VMDescriptor;
032: import java.util.HashSet;
033:
034: import java.util.Hashtable;
035: import java.util.Vector;
036: import java.util.Enumeration;
037: import java.util.Collections;
038:
039: /**
040: */
041:
042: public class ClassInvestigator extends ClassHolder {
043:
044: public static ClassInvestigator load(InputStream is)
045: throws IOException {
046:
047: ClassInput classInput = new ClassInput(is);
048:
049: // Check the header
050: checkHeader(classInput);
051:
052: // Read in the Constant Pool
053: int constantPoolCount = classInput.getU2();
054:
055: ClassInvestigator ci = new ClassInvestigator(constantPoolCount);
056: // Yes, index starts at 1, The '0'th constant pool entry
057: // is reserved for the JVM and is not present in the class file.
058: for (int i = 1; i < constantPoolCount;) {
059: ConstantPoolEntry item = ClassInvestigator
060: .getConstant(classInput);
061: i += ci.addEntry(item.getKey(), item);
062: }
063:
064: // Read in access_flags and class indexes
065: ci.access_flags = classInput.getU2();
066: ci.this _class = classInput.getU2();
067: ci.super _class = classInput.getU2();
068:
069: // interfaces is a simple int array
070: int interfaceCount = classInput.getU2();
071: if (interfaceCount != 0) {
072: ci.interfaces = new int[interfaceCount];
073: for (int i = 0; i < interfaceCount; i++)
074: ci.interfaces[i] = classInput.getU2();
075: }
076:
077: int fieldCount = classInput.getU2();
078: if (fieldCount != 0) {
079: ci.field_info = new MemberTable(fieldCount);
080: for (int i = 0; i < fieldCount; i++) {
081: ci.field_info.addEntry(readClassMember(ci, classInput));
082: }
083: }
084:
085: int methodCount = classInput.getU2();
086: if (methodCount != 0) {
087: ci.method_info = new MemberTable(methodCount);
088: for (int i = 0; i < methodCount; i++) {
089: ci.method_info
090: .addEntry(readClassMember(ci, classInput));
091: }
092: }
093:
094: int attributeCount = classInput.getU2();
095: if (attributeCount != 0) {
096: ci.attribute_info = new Attributes(attributeCount);
097:
098: for (int i = 0; i < attributeCount; i++)
099: ci.attribute_info.addEntry(new AttributeEntry(
100: classInput));
101: }
102: return ci;
103:
104: }
105:
106: private static ClassMember readClassMember(ClassInvestigator ci,
107: ClassInput in) throws IOException {
108:
109: ClassMember member = new ClassMember(ci, in.getU2(),
110: in.getU2(), in.getU2());
111:
112: int attributeCount = in.getU2();
113: if (attributeCount != 0) {
114: member.attribute_info = new Attributes(attributeCount);
115: for (int i = 0; i < attributeCount; i++)
116: member.attribute_info.addEntry(new AttributeEntry(in));
117: }
118:
119: return member;
120: }
121:
122: /*
123: ** Constructors.
124: */
125:
126: private ClassInvestigator(int constantPoolCount) {
127: super (constantPoolCount);
128: }
129:
130: /*
131: ** Methods to investigate this class
132: */
133:
134: public Enumeration implementedInterfaces() {
135: int interfaceCount = interfaces == null ? 0 : interfaces.length;
136: Vector implemented = new Vector(interfaceCount);
137:
138: for (int i = 0; i < interfaceCount; i++) {
139: implemented.addElement(className(interfaces[i]));
140: }
141: return implemented.elements();
142: }
143:
144: public Enumeration getFields() {
145: if (field_info == null)
146: return Collections.enumeration(Collections.EMPTY_LIST);
147:
148: return field_info.entries.elements();
149: }
150:
151: public Enumeration getMethods() {
152: if (method_info == null)
153: return Collections.enumeration(Collections.EMPTY_LIST);
154: return method_info.entries.elements();
155: }
156:
157: public Enumeration referencedClasses() {
158: return getClasses(getMethods(), getFields());
159: }
160:
161: /**
162: Return an Enumeration of all referenced classes
163: */
164:
165: private Enumeration getClasses(Enumeration methods,
166: Enumeration fields) {
167: return new ClassEnumeration(this , cptEntries.elements(),
168: methods, fields);
169: }
170:
171: public Enumeration getStrings() {
172: HashSet strings = new HashSet(30, 0.8f);
173:
174: int size = cptEntries.size();
175: for (int i = 1; i < size; i++) {
176: ConstantPoolEntry cpe = getEntry(i);
177:
178: if ((cpe == null)
179: || (cpe.getTag() != VMDescriptor.CONSTANT_String))
180: continue;
181:
182: CONSTANT_Index_info cii = (CONSTANT_Index_info) cpe;
183:
184: strings.add(nameIndexToString(cii.getI1()));
185: }
186:
187: return java.util.Collections.enumeration(strings);
188: }
189:
190: public ClassMember getMember(String simpleName, String descriptor) {
191:
192: if (descriptor.startsWith("(")) {
193: if (method_info == null)
194: return null;
195: return method_info.find(simpleName, descriptor);
196: } else {
197: if (field_info == null)
198: return null;
199: return field_info.find(simpleName, descriptor);
200: }
201: }
202:
203: /**
204: Return an Enumeration of all Member References
205: */
206: /*
207: Enumeration getMemberReferences() {
208: return new ReferenceEnumeration(this, elements());
209: }
210: */
211:
212: /*
213: ** Methods to modify the class.
214: */
215: // remove all atttributes that are not essential
216: public void removeAttributes() throws IOException {
217:
218: // Class level attributes
219: if (attribute_info != null) {
220: for (int i = attribute_info.size() - 1; i >= 0; i--) {
221:
222: AttributeEntry ae = (AttributeEntry) attribute_info
223: .elementAt(i);
224: String name = nameIndexToString(ae.getNameIndex());
225: if (name.equals("SourceFile"))
226: attribute_info.removeElementAt(i);
227: else if (name.equals("InnerClasses"))
228: ; // leave in
229: else
230: System.err
231: .println("WARNING - Unknown Class File attribute "
232: + name);
233: }
234:
235: if (attribute_info.size() == 0)
236: attribute_info = null;
237: }
238: attribute_info = null;
239:
240: // fields
241: for (Enumeration e = getFields(); e.hasMoreElements();) {
242: ClassMember member = (ClassMember) e.nextElement();
243:
244: Attributes attrs = member.attribute_info;
245:
246: if (attrs != null) {
247:
248: for (int i = attrs.size() - 1; i >= 0; i--) {
249:
250: AttributeEntry ae = (AttributeEntry) attrs
251: .elementAt(i);
252: String name = nameIndexToString(ae.getNameIndex());
253: if (name.equals("ConstantValue"))
254: ; // leave in
255: else if (name.equals("Synthetic"))
256: ; // leave in
257: else
258: System.err
259: .println("WARNING - Unknown Field attribute "
260: + name);
261: }
262:
263: if (attrs.size() == 0)
264: member.attribute_info = null;
265: }
266:
267: }
268:
269: // methods
270: for (Enumeration e = getMethods(); e.hasMoreElements();) {
271: ClassMember member = (ClassMember) e.nextElement();
272:
273: Attributes attrs = member.attribute_info;
274:
275: if (attrs != null) {
276:
277: for (int i = attrs.size() - 1; i >= 0; i--) {
278:
279: AttributeEntry ae = (AttributeEntry) attrs
280: .elementAt(i);
281: String name = nameIndexToString(ae.getNameIndex());
282: if (name.equals("Code"))
283: processCodeAttribute(member, ae);
284: else if (name.equals("Exceptions"))
285: ; // leave in
286: else if (name.equals("Deprecated"))
287: ; // leave in
288: else if (name.equals("Synthetic"))
289: ; // leave in
290: else
291: System.err
292: .println("WARNING - Unknown method attribute "
293: + name);
294: }
295:
296: if (attrs.size() == 0)
297: member.attribute_info = null;
298: }
299:
300: }
301: }
302:
303: private void processCodeAttribute(ClassMember member,
304: AttributeEntry ae) throws IOException {
305:
306: ClassInput ci = new ClassInput(
307: new java.io.ByteArrayInputStream(ae.infoIn));
308:
309: ci.skipBytes(4); // puts us at code_length
310: int len = ci.getU4();
311: ci.skipBytes(len); // puts us at exception_table_length
312: int count = ci.getU2();
313: if (count != 0)
314: ci.skipBytes(8 * count);
315:
316: int nonAttrLength = 4 + 4 + len + 2 + (8 * count);
317:
318: // now at attributes
319:
320: count = ci.getU2();
321: if (count == 0)
322: return;
323:
324: int newCount = count;
325: for (int i = 0; i < count; i++) {
326:
327: int nameIndex = ci.getU2();
328: String name = nameIndexToString(nameIndex);
329: if (name.equals("LineNumberTable")
330: || name.equals("LocalVariableTable"))
331: newCount--;
332: else
333: System.err.println("ERROR - Unknown code attribute "
334: + name);
335:
336: len = ci.getU4();
337: ci.skipBytes(len);
338: }
339:
340: if (newCount != 0) {
341: System.err
342: .println("ERROR - expecting all code attributes to be removed");
343: System.exit(1);
344: }
345:
346: // this is only coded for all attributes within a Code attribute being removed.
347:
348: byte[] newInfo = new byte[nonAttrLength + 2];
349: System.arraycopy(ae.infoIn, 0, newInfo, 0, nonAttrLength);
350: // last two bytes are left at 0 which means 0 attributes
351: ae.infoIn = newInfo;
352: }
353:
354: public void renameClassElements(Hashtable classNameMap,
355: Hashtable memberNameMap) {
356:
357: // this & super class
358: renameString(classNameMap,
359: (CONSTANT_Index_info) getEntry(this _class));
360: renameString(classNameMap,
361: (CONSTANT_Index_info) getEntry(super _class));
362:
363: // implemented interfaces
364: // handled by Class entries below
365:
366: // classes & Strings
367: // descriptors
368: int size = cptEntries.size();
369: for (int i = 1; i < size; i++) {
370: ConstantPoolEntry cpe = getEntry(i);
371:
372: if (cpe == null)
373: continue;
374:
375: switch (cpe.getTag()) {
376: case VMDescriptor.CONSTANT_String:
377: case VMDescriptor.CONSTANT_Class: {
378: CONSTANT_Index_info cii = (CONSTANT_Index_info) cpe;
379: renameString(classNameMap, cii);
380: break;
381: }
382: case VMDescriptor.CONSTANT_NameAndType: {
383: CONSTANT_Index_info cii = (CONSTANT_Index_info) cpe;
384: String newDescriptor = newDescriptor(classNameMap,
385: nameIndexToString(cii.getI2()));
386: if (newDescriptor != null) {
387: doRenameString(cii.getI2(), newDescriptor);
388: }
389: break;
390: }
391:
392: default:
393: continue;
394: }
395:
396: }
397:
398: //System.out.println("Starting Fields");
399:
400: // now the methods & fields, only descriptors at this time
401: renameMembers(getFields(), classNameMap, memberNameMap);
402:
403: renameMembers(getMethods(), classNameMap, memberNameMap);
404: }
405:
406: private void renameMembers(Enumeration e, Hashtable classNameMap,
407: Hashtable memberNameMap) {
408:
409: for (; e.hasMoreElements();) {
410: ClassMember member = (ClassMember) e.nextElement();
411:
412: String oldMemberName = nameIndexToString(member.name_index);
413: String newMemberName = (String) memberNameMap
414: .get(oldMemberName);
415: if (newMemberName != null)
416: doRenameString(member.name_index, newMemberName);
417:
418: String newDescriptor = newDescriptor(classNameMap,
419: nameIndexToString(member.descriptor_index));
420: if (newDescriptor != null) {
421: doRenameString(member.descriptor_index, newDescriptor);
422: }
423: }
424:
425: }
426:
427: private void renameString(Hashtable classNameMap,
428: CONSTANT_Index_info cii) {
429:
430: int index = cii.getI1();
431:
432: String name = nameIndexToString(index);
433: String newName = (String) classNameMap.get(name);
434: if (newName != null) {
435:
436: doRenameString(index, newName);
437:
438: return;
439: }
440:
441: // have to look for arrays
442: if (cii.getTag() == VMDescriptor.CONSTANT_Class) {
443:
444: if (name.charAt(0) == '[') {
445: int classOffset = name.indexOf('L') + 1;
446:
447: String baseClassName = name.substring(classOffset, name
448: .length() - 1);
449:
450: newName = (String) classNameMap.get(baseClassName);
451:
452: if (newName != null) {
453:
454: String newArrayClassName = name.substring(0,
455: classOffset)
456: + newName + ";";
457:
458: doRenameString(index, newArrayClassName);
459:
460: }
461:
462: }
463: }
464: }
465:
466: private void doRenameString(int index, String newName) {
467: ConstantPoolEntry cpe = getEntry(index);
468: if (cpe.getTag() != VMDescriptor.CONSTANT_Utf8)
469: throw new RuntimeException("unexpected type " + cpe);
470:
471: CONSTANT_Utf8_info newCpe = new CONSTANT_Utf8_info(newName);
472:
473: cptHashTable.remove(cpe.getKey());
474: cptHashTable.put(cpe.getKey(), cpe);
475:
476: newCpe.index = index;
477:
478: cptEntries.setElementAt(newCpe, index);
479: }
480:
481: /*
482: **
483: */
484: static private void checkHeader(ClassInput in) throws IOException {
485: int magic = in.getU4();
486: int minor_version = in.getU2();
487: int major_version = in.getU2();
488:
489: if (magic != VMDescriptor.JAVA_CLASS_FORMAT_MAGIC)
490: throw new ClassFormatError();
491: }
492:
493: private static ConstantPoolEntry getConstant(ClassInput in)
494: throws IOException {
495:
496: ConstantPoolEntry item;
497: int tag;
498: tag = in.readUnsignedByte();
499:
500: switch (tag) {
501: case VMDescriptor.CONSTANT_Class:
502: case VMDescriptor.CONSTANT_String:
503: item = new CONSTANT_Index_info(tag, in.getU2(), 0);
504: break;
505:
506: case VMDescriptor.CONSTANT_NameAndType:
507: case VMDescriptor.CONSTANT_Fieldref:
508: case VMDescriptor.CONSTANT_Methodref:
509: case VMDescriptor.CONSTANT_InterfaceMethodref:
510: item = new CONSTANT_Index_info(tag, in.getU2(), in.getU2());
511: break;
512:
513: case VMDescriptor.CONSTANT_Integer:
514: item = new CONSTANT_Integer_info(in.getU4());
515: break;
516:
517: case VMDescriptor.CONSTANT_Float:
518: item = new CONSTANT_Float_info(in.readFloat());
519: break;
520:
521: case VMDescriptor.CONSTANT_Long:
522: item = new CONSTANT_Long_info(in.readLong());
523: break;
524:
525: case VMDescriptor.CONSTANT_Double:
526: item = new CONSTANT_Double_info(in.readDouble());
527: break;
528:
529: case VMDescriptor.CONSTANT_Utf8:
530: item = new CONSTANT_Utf8_info(in.readUTF());
531: break;
532: default:
533: throw new ClassFormatError();
534: }
535:
536: return item;
537: }
538:
539: public static String newDescriptor(Hashtable classNameMap,
540: String descriptor) {
541:
542: String newDescriptor = null;
543:
544: int dlen = descriptor.length();
545: for (int offset = 0; offset < dlen;) {
546: char c = descriptor.charAt(offset);
547: switch (c) {
548: case VMDescriptor.C_VOID:
549: case VMDescriptor.C_BOOLEAN:
550: case VMDescriptor.C_BYTE:
551: case VMDescriptor.C_CHAR:
552: case VMDescriptor.C_SHORT:
553: case VMDescriptor.C_INT:
554: case VMDescriptor.C_LONG:
555: case VMDescriptor.C_FLOAT:
556: case VMDescriptor.C_DOUBLE:
557: case VMDescriptor.C_ARRAY:
558: case VMDescriptor.C_METHOD:
559: case VMDescriptor.C_ENDMETHOD:
560: default:
561: offset++;
562: continue;
563:
564: case VMDescriptor.C_CLASS: {
565: int startOffset = offset;
566: while (descriptor.charAt(offset++) != VMDescriptor.C_ENDCLASS)
567: ;
568: int endOffset = offset;
569:
570: // name includes L and ;
571: String name = descriptor.substring(startOffset,
572: endOffset);
573: String newName = (String) classNameMap.get(name);
574: if (newName != null) {
575: if (newDescriptor == null)
576: newDescriptor = descriptor;
577:
578: // we just replace the first occurance of it,
579: // the loop will hit any next occurance.
580: int startPos = newDescriptor.indexOf(name);
581:
582: String tmp;
583: if (startPos == 0)
584: tmp = newName;
585: else
586: tmp = newDescriptor.substring(0, startPos)
587: + newName;
588:
589: int endPos = startPos + name.length();
590:
591: if (endPos < newDescriptor.length()) {
592:
593: tmp += newDescriptor.substring(endPos,
594: newDescriptor.length());
595: }
596:
597: newDescriptor = tmp;
598: }
599: }
600: }
601:
602: }
603: //if (newDescriptor != null) {
604: // System.out.println("O - " + descriptor);
605: // System.out.println("N - " + newDescriptor);
606: //}
607: return newDescriptor;
608: }
609: }
|