1:
38:
39:
40: package ;
41:
42: import ;
43: import ;
44: import ;
45: import ;
46:
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62:
63:
70: public class ObjectStreamClass implements Serializable
71: {
72: static final ObjectStreamField[] INVALID_FIELDS = new ObjectStreamField[0];
73:
74:
89: public static ObjectStreamClass lookup(Class<?> cl)
90: {
91: if (cl == null)
92: return null;
93: if (! (Serializable.class).isAssignableFrom(cl))
94: return null;
95:
96: return lookupForClassObject(cl);
97: }
98:
99:
104: static ObjectStreamClass lookupForClassObject(Class cl)
105: {
106: if (cl == null)
107: return null;
108:
109: ObjectStreamClass osc = classLookupTable.get(cl);
110:
111: if (osc != null)
112: return osc;
113: else
114: {
115: osc = new ObjectStreamClass(cl);
116: classLookupTable.put(cl, osc);
117: return osc;
118: }
119: }
120:
121:
127: public String getName()
128: {
129: return name;
130: }
131:
132:
141: public Class<?> forClass()
142: {
143: return clazz;
144: }
145:
146:
155: public long getSerialVersionUID()
156: {
157: return uid;
158: }
159:
160:
169: public ObjectStreamField[] getFields()
170: {
171: ObjectStreamField[] copy = new ObjectStreamField[ fields.length ];
172: System.arraycopy(fields, 0, copy, 0, fields.length);
173: return copy;
174: }
175:
176:
177:
178:
179: public ObjectStreamField getField (String name)
180: {
181: for (int i = 0; i < fields.length; i++)
182: if (fields[i].getName().equals(name))
183: return fields[i];
184: return null;
185: }
186:
187:
196: public String toString()
197: {
198: return "java.io.ObjectStreamClass< " + name + ", " + uid + " >";
199: }
200:
201:
202:
203:
204:
205:
206:
207:
208: boolean hasWriteMethod()
209: {
210: return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0;
211: }
212:
213:
214:
215: boolean isSerializable()
216: {
217: return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
218: }
219:
220:
221:
222:
223: boolean isExternalizable()
224: {
225: return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
226: }
227:
228:
229:
230: boolean isEnum()
231: {
232: return (flags & ObjectStreamConstants.SC_ENUM) != 0;
233: }
234:
235:
236:
237:
238:
239: ObjectStreamClass getSuper()
240: {
241: return superClass;
242: }
243:
244:
257: ObjectStreamClass[] hierarchy()
258: {
259: ObjectStreamClass[] result = hierarchy;
260: if (result == null)
261: {
262: int d = 0;
263:
264: for(ObjectStreamClass osc = this; osc != null; osc = osc.getSuper())
265: d++;
266:
267: result = new ObjectStreamClass[d];
268:
269: for (ObjectStreamClass osc = this; osc != null; osc = osc.getSuper())
270: {
271: result[--d] = osc;
272: }
273:
274: hierarchy = result;
275: }
276: return result;
277: }
278:
279:
282: private ObjectStreamClass[] hierarchy = null;
283:
284:
285:
286:
287:
288: int getFlags()
289: {
290: return flags;
291: }
292:
293:
294: ObjectStreamClass(String name, long uid, byte flags,
295: ObjectStreamField[] fields)
296: {
297: this.name = name;
298: this.uid = uid;
299: this.flags = flags;
300: this.fields = fields;
301: }
302:
303:
314: void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException
315: {hierarchy = null;
316: this.clazz = cl;
317:
318: cacheMethods();
319:
320: long class_uid = getClassUID(cl);
321: if (uid == 0)
322: uid = class_uid;
323: else
324: {
325:
326:
327: if (!cl.isArray() && uid != class_uid)
328: {
329: String msg = cl +
330: ": Local class not compatible: stream serialVersionUID="
331: + uid + ", local serialVersionUID=" + class_uid;
332: throw new InvalidClassException (msg);
333: }
334: }
335:
336: isProxyClass = clazz != null && Proxy.isProxyClass(clazz);
337: this.superClass = superClass;
338: calculateOffsets();
339:
340: try
341: {
342: ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz);
343:
344: if (exportedFields == null)
345: return;
346:
347: ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length];
348: int i, j, k;
349:
350:
354:
355: Arrays.sort(exportedFields);
356:
357: i = 0; j = 0; k = 0;
358: while (i < fields.length && j < exportedFields.length)
359: {
360: int comp = fields[i].compareTo(exportedFields[j]);
361:
362: if (comp < 0)
363: {
364: newFieldList[k] = fields[i];
365: fields[i].setPersistent(false);
366: fields[i].setToSet(false);
367: i++;
368: }
369: else if (comp > 0)
370: {
371:
374: newFieldList[k] = exportedFields[j];
375: newFieldList[k].setPersistent(true);
376: newFieldList[k].setToSet(false);
377: try
378: {
379: newFieldList[k].lookupField(clazz);
380: newFieldList[k].checkFieldType();
381: }
382: catch (NoSuchFieldException _)
383: {
384: }
385: j++;
386: }
387: else
388: {
389: try
390: {
391: exportedFields[j].lookupField(clazz);
392: exportedFields[j].checkFieldType();
393: }
394: catch (NoSuchFieldException _)
395: {
396: }
397:
398: if (!fields[i].getType().equals(exportedFields[j].getType()))
399: throw new InvalidClassException
400: ("serialPersistentFields must be compatible with" +
401: " imported fields (about " + fields[i].getName() + ")");
402: newFieldList[k] = fields[i];
403: fields[i].setPersistent(true);
404: i++;
405: j++;
406: }
407: k++;
408: }
409:
410: if (i < fields.length)
411: for (;i<fields.length;i++,k++)
412: {
413: fields[i].setPersistent(false);
414: fields[i].setToSet(false);
415: newFieldList[k] = fields[i];
416: }
417: else
418: if (j < exportedFields.length)
419: for (;j<exportedFields.length;j++,k++)
420: {
421: exportedFields[j].setPersistent(true);
422: exportedFields[j].setToSet(false);
423: newFieldList[k] = exportedFields[j];
424: }
425:
426: fields = new ObjectStreamField[k];
427: System.arraycopy(newFieldList, 0, fields, 0, k);
428: }
429: catch (NoSuchFieldException ignore)
430: {
431: return;
432: }
433: catch (IllegalAccessException ignore)
434: {
435: return;
436: }
437: }
438:
439: void setSuperclass (ObjectStreamClass osc)
440: {
441: superClass = osc;
442: hierarchy = null;
443: }
444:
445: void calculateOffsets()
446: {
447: int i;
448: ObjectStreamField field;
449: primFieldSize = 0;
450: int fcount = fields.length;
451: for (i = 0; i < fcount; ++ i)
452: {
453: field = fields[i];
454:
455: if (! field.isPrimitive())
456: break;
457:
458: field.setOffset(primFieldSize);
459: switch (field.getTypeCode())
460: {
461: case 'B':
462: case 'Z':
463: ++ primFieldSize;
464: break;
465: case 'C':
466: case 'S':
467: primFieldSize += 2;
468: break;
469: case 'I':
470: case 'F':
471: primFieldSize += 4;
472: break;
473: case 'D':
474: case 'J':
475: primFieldSize += 8;
476: break;
477: }
478: }
479:
480: for (objectFieldCount = 0; i < fcount; ++ i)
481: fields[i].setOffset(objectFieldCount++);
482: }
483:
484: private Method findMethod(Method[] methods, String name, Class[] params,
485: Class returnType, boolean mustBePrivate)
486: {
487: outer:
488: for (int i = 0; i < methods.length; i++)
489: {
490: final Method m = methods[i];
491: int mods = m.getModifiers();
492: if (Modifier.isStatic(mods)
493: || (mustBePrivate && !Modifier.isPrivate(mods)))
494: {
495: continue;
496: }
497:
498: if (m.getName().equals(name)
499: && m.getReturnType() == returnType)
500: {
501: Class[] mp = m.getParameterTypes();
502: if (mp.length == params.length)
503: {
504: for (int j = 0; j < mp.length; j++)
505: {
506: if (mp[j] != params[j])
507: {
508: continue outer;
509: }
510: }
511: AccessController.doPrivileged(new SetAccessibleAction(m));
512: return m;
513: }
514: }
515: }
516: return null;
517: }
518:
519: private static boolean inSamePackage(Class c1, Class c2)
520: {
521: String name1 = c1.getName();
522: String name2 = c2.getName();
523:
524: int id1 = name1.lastIndexOf('.');
525: int id2 = name2.lastIndexOf('.');
526:
527:
528: if (id1 == -1 || id2 == -1)
529: return id1 == id2;
530:
531: String package1 = name1.substring(0, id1);
532: String package2 = name2.substring(0, id2);
533:
534: return package1.equals(package2);
535: }
536:
537: final static Class[] noArgs = new Class[0];
538:
539: private static Method findAccessibleMethod(String name, Class from)
540: {
541: for (Class c = from; c != null; c = c.getSuperclass())
542: {
543: try
544: {
545: Method res = c.getDeclaredMethod(name, noArgs);
546: int mods = res.getModifiers();
547:
548: if (c == from
549: || Modifier.isProtected(mods)
550: || Modifier.isPublic(mods)
551: || (! Modifier.isPrivate(mods) && inSamePackage(c, from)))
552: {
553: AccessController.doPrivileged(new SetAccessibleAction(res));
554: return res;
555: }
556: }
557: catch (NoSuchMethodException e)
558: {
559: }
560: }
561:
562: return null;
563: }
564:
565:
576: private static boolean loadedByBootOrApplicationClassLoader(Class cl)
577: {
578: ClassLoader l = cl.getClassLoader();
579: return
580: ( l == null )
581: || (l == ClassLoader.getSystemClassLoader() );
582: }
583:
584: static Hashtable methodCache = new Hashtable();
585:
586: static final Class[] readObjectSignature = { ObjectInputStream.class };
587: static final Class[] writeObjectSignature = { ObjectOutputStream.class };
588:
589: private void cacheMethods()
590: {
591: Class cl = forClass();
592: Method[] cached = (Method[]) methodCache.get(cl);
593: if (cached == null)
594: {
595: cached = new Method[4];
596: Method[] methods = cl.getDeclaredMethods();
597:
598: cached[0] = findMethod(methods, "readObject",
599: readObjectSignature,
600: Void.TYPE, true);
601: cached[1] = findMethod(methods, "writeObject",
602: writeObjectSignature,
603: Void.TYPE, true);
604:
605:
606:
607: cached[2] = findAccessibleMethod("readResolve", cl);
608: cached[3] = findAccessibleMethod("writeReplace", cl);
609:
610:
614: if (loadedByBootOrApplicationClassLoader(cl))
615: methodCache.put(cl,cached);
616: }
617: readObjectMethod = cached[0];
618: writeObjectMethod = cached[1];
619: readResolveMethod = cached[2];
620: writeReplaceMethod = cached[3];
621: }
622:
623: private ObjectStreamClass(Class cl)
624: {
625: uid = 0;
626: flags = 0;
627: isProxyClass = Proxy.isProxyClass(cl);
628:
629: clazz = cl;
630: cacheMethods();
631: name = cl.getName();
632: setFlags(cl);
633: setFields(cl);
634:
635: if ( (Serializable.class).isAssignableFrom(cl) && !isProxyClass)
636: uid = getClassUID(cl);
637: superClass = lookup(cl.getSuperclass());
638: }
639:
640:
641:
642: private void setFlags(Class cl)
643: {
644: if ((java.io.Externalizable.class).isAssignableFrom(cl))
645: flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
646: else if ((java.io.Serializable.class).isAssignableFrom(cl))
647:
648: flags |= ObjectStreamConstants.SC_SERIALIZABLE;
649:
650: if (writeObjectMethod != null)
651: flags |= ObjectStreamConstants.SC_WRITE_METHOD;
652:
653: if (cl.isEnum() || cl == Enum.class)
654: flags |= ObjectStreamConstants.SC_ENUM;
655: }
656:
657:
658:
659:
660: private void setFields(Class cl)
661: {
662: SetAccessibleAction setAccessible = new SetAccessibleAction();
663:
664: if (!isSerializable() || isExternalizable() || isEnum())
665: {
666: fields = NO_FIELDS;
667: return;
668: }
669:
670: try
671: {
672: final Field f =
673: cl.getDeclaredField("serialPersistentFields");
674: setAccessible.setMember(f);
675: AccessController.doPrivileged(setAccessible);
676: int modifiers = f.getModifiers();
677:
678: if (Modifier.isStatic(modifiers)
679: && Modifier.isFinal(modifiers)
680: && Modifier.isPrivate(modifiers))
681: {
682: fields = getSerialPersistentFields(cl);
683: if (fields != null)
684: {
685: ObjectStreamField[] fieldsName = new ObjectStreamField[fields.length];
686: System.arraycopy(fields, 0, fieldsName, 0, fields.length);
687:
688: Arrays.sort (fieldsName, new Comparator() {
689: public int compare(Object o1, Object o2)
690: {
691: ObjectStreamField f1 = (ObjectStreamField)o1;
692: ObjectStreamField f2 = (ObjectStreamField)o2;
693:
694: return f1.getName().compareTo(f2.getName());
695: }
696: });
697:
698: for (int i=1; i < fields.length; i++)
699: {
700: if (fieldsName[i-1].getName().equals(fieldsName[i].getName()))
701: {
702: fields = INVALID_FIELDS;
703: return;
704: }
705: }
706:
707: Arrays.sort (fields);
708:
709: for (int i=0; i < fields.length; i++)
710: {
711: try
712: {
713: fields[i].lookupField(cl);
714: }
715: catch (NoSuchFieldException _)
716: {
717: fields[i].setToSet(false);
718: }
719: }
720:
721: calculateOffsets();
722: return;
723: }
724: }
725: }
726: catch (NoSuchFieldException ignore)
727: {
728: }
729: catch (IllegalAccessException ignore)
730: {
731: }
732:
733: int num_good_fields = 0;
734: Field[] all_fields = cl.getDeclaredFields();
735:
736: int modifiers;
737:
738: for (int i = 0; i < all_fields.length; i++)
739: {
740: modifiers = all_fields[i].getModifiers();
741: if (Modifier.isTransient(modifiers)
742: || Modifier.isStatic(modifiers))
743: all_fields[i] = null;
744: else
745: num_good_fields++;
746: }
747:
748:
749: fields = new ObjectStreamField[ num_good_fields ];
750: for (int from = 0, to = 0; from < all_fields.length; from++)
751: if (all_fields[from] != null)
752: {
753: final Field f = all_fields[from];
754: setAccessible.setMember(f);
755: AccessController.doPrivileged(setAccessible);
756: fields[to] = new ObjectStreamField(all_fields[from]);
757: to++;
758: }
759:
760: Arrays.sort(fields);
761:
762:
763: for (int i = 1; i < fields.length; i++)
764: {
765: if(fields[i - 1].getName().equals(fields[i].getName()))
766: throw new InternalError("Duplicate field " +
767: fields[i].getName() + " in class " + cl.getName());
768: }
769: calculateOffsets();
770: }
771:
772: static Hashtable uidCache = new Hashtable();
773:
774:
775:
776: private long getClassUID(Class cl)
777: {
778: long result = 0;
779: Long cache = (Long) uidCache.get(cl);
780: if (cache != null)
781: result = cache.longValue();
782: else
783: {
784:
785:
786: if (Enum.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl))
787: {
788:
789:
790: return 0L;
791: }
792: try
793: {
794: result = getClassUIDFromField(cl);
795: }
796: catch (NoSuchFieldException ignore)
797: {
798: try
799: {
800: result = calculateClassUID(cl);
801: }
802: catch (NoSuchAlgorithmException e)
803: {
804: throw new RuntimeException
805: ("The SHA algorithm was not found to use in computing the Serial Version UID for class "
806: + cl.getName(), e);
807: }
808: catch (IOException ioe)
809: {
810: throw new RuntimeException(ioe);
811: }
812: }
813:
814: if (loadedByBootOrApplicationClassLoader(cl))
815: uidCache.put(cl,Long.valueOf(result));
816: }
817: return result;
818: }
819:
820:
829: long getClassUIDFromField(Class cl)
830: throws NoSuchFieldException
831: {
832: long result;
833:
834: try
835: {
836:
837:
838:
839: final Field suid = cl.getDeclaredField("serialVersionUID");
840: SetAccessibleAction setAccessible = new SetAccessibleAction(suid);
841: AccessController.doPrivileged(setAccessible);
842: int modifiers = suid.getModifiers();
843:
844: if (Modifier.isStatic(modifiers)
845: && Modifier.isFinal(modifiers)
846: && suid.getType() == Long.TYPE)
847: result = suid.getLong(null);
848: else
849: throw new NoSuchFieldException();
850: }
851: catch (IllegalAccessException ignore)
852: {
853: throw new NoSuchFieldException();
854: }
855:
856: return result;
857: }
858:
859:
872: long calculateClassUID(Class cl)
873: throws NoSuchAlgorithmException, IOException
874: {
875: long result;
876: MessageDigest md;
877: try
878: {
879: md = MessageDigest.getInstance("SHA");
880: }
881: catch (NoSuchAlgorithmException e)
882: {
883:
884: Gnu gnuProvider = new Gnu();
885: Security.addProvider(gnuProvider);
886: md = MessageDigest.getInstance("SHA");
887: }
888:
889: DigestOutputStream digest_out =
890: new DigestOutputStream(nullOutputStream, md);
891: DataOutputStream data_out = new DataOutputStream(digest_out);
892:
893: data_out.writeUTF(cl.getName());
894:
895: int modifiers = cl.getModifiers();
896:
897: modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL
898: | Modifier.INTERFACE | Modifier.PUBLIC);
899: data_out.writeInt(modifiers);
900:
901:
902:
903: if (! cl.isArray())
904: {
905: Class[] interfaces = cl.getInterfaces();
906: Arrays.sort(interfaces, interfaceComparator);
907: for (int i = 0; i < interfaces.length; i++)
908: data_out.writeUTF(interfaces[i].getName());
909: }
910:
911: Field field;
912: Field[] fields = cl.getDeclaredFields();
913: Arrays.sort(fields, memberComparator);
914: for (int i = 0; i < fields.length; i++)
915: {
916: field = fields[i];
917: modifiers = field.getModifiers();
918: if (Modifier.isPrivate(modifiers)
919: && (Modifier.isStatic(modifiers)
920: || Modifier.isTransient(modifiers)))
921: continue;
922:
923: data_out.writeUTF(field.getName());
924: data_out.writeInt(modifiers);
925: data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType()));
926: }
927:
928:
929: if (VMObjectStreamClass.hasClassInitializer(cl))
930: {
931: data_out.writeUTF("<clinit>");
932: data_out.writeInt(Modifier.STATIC);
933: data_out.writeUTF("()V");
934: }
935:
936: Constructor constructor;
937: Constructor[] constructors = cl.getDeclaredConstructors();
938: Arrays.sort (constructors, memberComparator);
939: for (int i = 0; i < constructors.length; i++)
940: {
941: constructor = constructors[i];
942: modifiers = constructor.getModifiers();
943: if (Modifier.isPrivate(modifiers))
944: continue;
945:
946: data_out.writeUTF("<init>");
947: data_out.writeInt(modifiers);
948:
949:
950:
951: data_out.writeUTF
952: (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.'));
953: }
954:
955: Method method;
956: Method[] methods = cl.getDeclaredMethods();
957: Arrays.sort(methods, memberComparator);
958: for (int i = 0; i < methods.length; i++)
959: {
960: method = methods[i];
961: modifiers = method.getModifiers();
962: if (Modifier.isPrivate(modifiers))
963: continue;
964:
965: data_out.writeUTF(method.getName());
966: data_out.writeInt(modifiers);
967:
968:
969:
970: data_out.writeUTF
971: (TypeSignature.getEncodingOfMethod(method).replace('/', '.'));
972: }
973:
974: data_out.close();
975: byte[] sha = md.digest();
976: result = 0;
977: int len = sha.length < 8 ? sha.length : 8;
978: for (int i = 0; i < len; i++)
979: result += (long) (sha[i] & 0xFF) << (8 * i);
980:
981: return result;
982: }
983:
984:
993: private ObjectStreamField[] getSerialPersistentFields(Class clazz)
994: throws NoSuchFieldException, IllegalAccessException
995: {
996: ObjectStreamField[] fieldsArray = null;
997: ObjectStreamField[] o;
998:
999:
1000:
1001: Field f = clazz.getDeclaredField("serialPersistentFields");
1002: f.setAccessible(true);
1003:
1004: int modifiers = f.getModifiers();
1005: if (!(Modifier.isStatic(modifiers) &&
1006: Modifier.isFinal(modifiers) &&
1007: Modifier.isPrivate(modifiers)))
1008: return null;
1009:
1010: o = (ObjectStreamField[]) f.get(null);
1011:
1012: if (o == null)
1013: return null;
1014:
1015: fieldsArray = new ObjectStreamField[ o.length ];
1016: System.arraycopy(o, 0, fieldsArray, 0, o.length);
1017:
1018: return fieldsArray;
1019: }
1020:
1021:
1028: Externalizable newInstance() throws InvalidClassException
1029: {
1030: synchronized(this)
1031: {
1032: if (constructor == null)
1033: {
1034: try
1035: {
1036: final Constructor c = clazz.getConstructor(new Class[0]);
1037:
1038: AccessController.doPrivileged(new PrivilegedAction()
1039: {
1040: public Object run()
1041: {
1042: c.setAccessible(true);
1043: return null;
1044: }
1045: });
1046:
1047: constructor = c;
1048: }
1049: catch(NoSuchMethodException x)
1050: {
1051: throw new InvalidClassException(clazz.getName(),
1052: "No public zero-argument constructor");
1053: }
1054: }
1055: }
1056:
1057: try
1058: {
1059: return (Externalizable)constructor.newInstance();
1060: }
1061: catch(Exception x)
1062: {
1063: throw (InvalidClassException)
1064: new InvalidClassException(clazz.getName(),
1065: "Unable to instantiate").initCause(x);
1066: }
1067: }
1068:
1069: public static final ObjectStreamField[] NO_FIELDS = {};
1070:
1071: private static Hashtable<Class,ObjectStreamClass> classLookupTable
1072: = new Hashtable<Class,ObjectStreamClass>();
1073: private static final NullOutputStream nullOutputStream = new NullOutputStream();
1074: private static final Comparator interfaceComparator = new InterfaceComparator();
1075: private static final Comparator memberComparator = new MemberComparator();
1076: private static final
1077: Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class };
1078:
1079: private ObjectStreamClass superClass;
1080: private Class<?> clazz;
1081: private String name;
1082: private long uid;
1083: private byte flags;
1084:
1085:
1086:
1087: ObjectStreamField[] fields;
1088:
1089:
1090: int primFieldSize = -1;
1091: int objectFieldCount;
1092:
1093: Method readObjectMethod;
1094: Method readResolveMethod;
1095: Method writeReplaceMethod;
1096: Method writeObjectMethod;
1097: boolean realClassIsSerializable;
1098: boolean realClassIsExternalizable;
1099: ObjectStreamField[] fieldMapping;
1100: Constructor firstNonSerializableParentConstructor;
1101: private Constructor constructor;
1102:
1103: boolean isProxyClass = false;
1104:
1105:
1106:
1107: private static final long serialVersionUID = -6120832682080437368L;
1108:
1109:
1110:
1111: private static final class InterfaceComparator implements Comparator
1112: {
1113: public int compare(Object o1, Object o2)
1114: {
1115: return ((Class) o1).getName().compareTo(((Class) o2).getName());
1116: }
1117: }
1118:
1119:
1120:
1121:
1122: private static final class MemberComparator implements Comparator
1123: {
1124: public int compare(Object o1, Object o2)
1125: {
1126: Member m1 = (Member) o1;
1127: Member m2 = (Member) o2;
1128:
1129: int comp = m1.getName().compareTo(m2.getName());
1130:
1131: if (comp == 0)
1132: return TypeSignature.getEncodingOfMember(m1).
1133: compareTo(TypeSignature.getEncodingOfMember(m2));
1134: else
1135: return comp;
1136: }
1137: }
1138: }