src/share/classes/java/io/ObjectOutputStream.java

Print this page




1017      * ObjectStreamClass to write class descriptor type strings.
1018      */
1019     void writeTypeString(String str) throws IOException {
1020         int handle;
1021         if (str == null) {
1022             writeNull();
1023         } else if ((handle = handles.lookup(str)) != -1) {
1024             writeHandle(handle);
1025         } else {
1026             writeString(str, false);
1027         }
1028     }
1029 
1030     /**
1031      * Verifies that this (possibly subclass) instance can be constructed
1032      * without violating security constraints: the subclass must not override
1033      * security-sensitive non-final methods, or else the
1034      * "enableSubclassImplementation" SerializablePermission is checked.
1035      */
1036     private void verifySubclass() {
1037         Class cl = getClass();
1038         if (cl == ObjectOutputStream.class) {
1039             return;
1040         }
1041         SecurityManager sm = System.getSecurityManager();
1042         if (sm == null) {
1043             return;
1044         }
1045         processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
1046         WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
1047         Boolean result = Caches.subclassAudits.get(key);
1048         if (result == null) {
1049             result = Boolean.valueOf(auditSubclass(cl));
1050             Caches.subclassAudits.putIfAbsent(key, result);
1051         }
1052         if (result.booleanValue()) {
1053             return;
1054         }
1055         sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
1056     }
1057 
1058     /**
1059      * Performs reflective checks on given subclass to verify that it doesn't
1060      * override security-sensitive non-final methods.  Returns true if subclass
1061      * is "safe", false otherwise.
1062      */
1063     private static boolean auditSubclass(final Class subcl) {
1064         Boolean result = AccessController.doPrivileged(
1065             new PrivilegedAction<Boolean>() {
1066                 public Boolean run() {
1067                     for (Class cl = subcl;
1068                          cl != ObjectOutputStream.class;
1069                          cl = cl.getSuperclass())
1070                     {
1071                         try {
1072                             cl.getDeclaredMethod(
1073                                 "writeUnshared", new Class[] { Object.class });
1074                             return Boolean.FALSE;
1075                         } catch (NoSuchMethodException ex) {
1076                         }
1077                         try {
1078                             cl.getDeclaredMethod("putFields", (Class[]) null);
1079                             return Boolean.FALSE;
1080                         } catch (NoSuchMethodException ex) {
1081                         }
1082                     }
1083                     return Boolean.TRUE;
1084                 }
1085             }
1086         );
1087         return result.booleanValue();
1088     }
1089 
1090     /**
1091      * Clears internal data structures.
1092      */
1093     private void clear() {


1105         depth++;
1106         try {
1107             // handle previously written and non-replaceable objects
1108             int h;
1109             if ((obj = subs.lookup(obj)) == null) {
1110                 writeNull();
1111                 return;
1112             } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1113                 writeHandle(h);
1114                 return;
1115             } else if (obj instanceof Class) {
1116                 writeClass((Class) obj, unshared);
1117                 return;
1118             } else if (obj instanceof ObjectStreamClass) {
1119                 writeClassDesc((ObjectStreamClass) obj, unshared);
1120                 return;
1121             }
1122 
1123             // check for replacement object
1124             Object orig = obj;
1125             Class cl = obj.getClass();
1126             ObjectStreamClass desc;
1127             for (;;) {
1128                 // REMIND: skip this check for strings/arrays?
1129                 Class repCl;
1130                 desc = ObjectStreamClass.lookup(cl, true);
1131                 if (!desc.hasWriteReplaceMethod() ||
1132                     (obj = desc.invokeWriteReplace(obj)) == null ||
1133                     (repCl = obj.getClass()) == cl)
1134                 {
1135                     break;
1136                 }
1137                 cl = repCl;
1138             }
1139             if (enableReplace) {
1140                 Object rep = replaceObject(obj);
1141                 if (rep != obj && rep != null) {
1142                     cl = rep.getClass();
1143                     desc = ObjectStreamClass.lookup(cl, true);
1144                 }
1145                 obj = rep;
1146             }
1147 
1148             // if object replaced, run through original checks a second time
1149             if (obj != orig) {


1152                     writeNull();
1153                     return;
1154                 } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1155                     writeHandle(h);
1156                     return;
1157                 } else if (obj instanceof Class) {
1158                     writeClass((Class) obj, unshared);
1159                     return;
1160                 } else if (obj instanceof ObjectStreamClass) {
1161                     writeClassDesc((ObjectStreamClass) obj, unshared);
1162                     return;
1163                 }
1164             }
1165 
1166             // remaining cases
1167             if (obj instanceof String) {
1168                 writeString((String) obj, unshared);
1169             } else if (cl.isArray()) {
1170                 writeArray(obj, desc, unshared);
1171             } else if (obj instanceof Enum) {
1172                 writeEnum((Enum) obj, desc, unshared);
1173             } else if (obj instanceof Serializable) {
1174                 writeOrdinaryObject(obj, desc, unshared);
1175             } else {
1176                 if (extendedDebugInfo) {
1177                     throw new NotSerializableException(
1178                         cl.getName() + "\n" + debugInfoStack.toString());
1179                 } else {
1180                     throw new NotSerializableException(cl.getName());
1181                 }
1182             }
1183         } finally {
1184             depth--;
1185             bout.setBlockDataMode(oldMode);
1186         }
1187     }
1188 
1189     /**
1190      * Writes null code to stream.
1191      */
1192     private void writeNull() throws IOException {
1193         bout.writeByte(TC_NULL);
1194     }
1195 
1196     /**
1197      * Writes given object handle to stream.
1198      */
1199     private void writeHandle(int handle) throws IOException {
1200         bout.writeByte(TC_REFERENCE);
1201         bout.writeInt(baseWireHandle + handle);
1202     }
1203 
1204     /**
1205      * Writes representation of given class to stream.
1206      */
1207     private void writeClass(Class cl, boolean unshared) throws IOException {
1208         bout.writeByte(TC_CLASS);
1209         writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
1210         handles.assign(unshared ? null : cl);
1211     }
1212 
1213     /**
1214      * Writes representation of given class descriptor to stream.
1215      */
1216     private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
1217         throws IOException
1218     {
1219         int handle;
1220         if (desc == null) {
1221             writeNull();
1222         } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
1223             writeHandle(handle);
1224         } else if (desc.isProxy()) {
1225             writeProxyDesc(desc, unshared);
1226         } else {
1227             writeNonProxyDesc(desc, unshared);
1228         }
1229     }
1230 
1231     /**
1232      * Writes class descriptor representing a dynamic proxy class to stream.
1233      */
1234     private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
1235         throws IOException
1236     {
1237         bout.writeByte(TC_PROXYCLASSDESC);
1238         handles.assign(unshared ? null : desc);
1239 
1240         Class cl = desc.forClass();
1241         Class[] ifaces = cl.getInterfaces();
1242         bout.writeInt(ifaces.length);
1243         for (int i = 0; i < ifaces.length; i++) {
1244             bout.writeUTF(ifaces[i].getName());
1245         }
1246 
1247         bout.setBlockDataMode(true);
1248         annotateProxyClass(cl);
1249         bout.setBlockDataMode(false);
1250         bout.writeByte(TC_ENDBLOCKDATA);
1251 
1252         writeClassDesc(desc.getSuperDesc(), false);
1253     }
1254 
1255     /**
1256      * Writes class descriptor representing a standard (i.e., not a dynamic
1257      * proxy) class to stream.
1258      */
1259     private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
1260         throws IOException
1261     {
1262         bout.writeByte(TC_CLASSDESC);
1263         handles.assign(unshared ? null : desc);
1264 
1265         if (protocol == PROTOCOL_VERSION_1) {
1266             // do not invoke class descriptor write hook with old protocol
1267             desc.writeNonProxy(this);
1268         } else {
1269             writeClassDescriptor(desc);
1270         }
1271 
1272         Class cl = desc.forClass();
1273         bout.setBlockDataMode(true);
1274         annotateClass(cl);
1275         bout.setBlockDataMode(false);
1276         bout.writeByte(TC_ENDBLOCKDATA);
1277 
1278         writeClassDesc(desc.getSuperDesc(), false);
1279     }
1280 
1281     /**
1282      * Writes given string to stream, using standard or long UTF format
1283      * depending on string length.
1284      */
1285     private void writeString(String str, boolean unshared) throws IOException {
1286         handles.assign(unshared ? null : str);
1287         long utflen = bout.getUTFLength(str);
1288         if (utflen <= 0xFFFF) {
1289             bout.writeByte(TC_STRING);
1290             bout.writeUTF(str, utflen);
1291         } else {
1292             bout.writeByte(TC_LONGSTRING);
1293             bout.writeLongUTF(str, utflen);
1294         }
1295     }
1296 
1297     /**
1298      * Writes given array object to stream.
1299      */
1300     private void writeArray(Object array,
1301                             ObjectStreamClass desc,
1302                             boolean unshared)
1303         throws IOException
1304     {
1305         bout.writeByte(TC_ARRAY);
1306         writeClassDesc(desc, false);
1307         handles.assign(unshared ? null : array);
1308 
1309         Class ccl = desc.forClass().getComponentType();
1310         if (ccl.isPrimitive()) {
1311             if (ccl == Integer.TYPE) {
1312                 int[] ia = (int[]) array;
1313                 bout.writeInt(ia.length);
1314                 bout.writeInts(ia, 0, ia.length);
1315             } else if (ccl == Byte.TYPE) {
1316                 byte[] ba = (byte[]) array;
1317                 bout.writeInt(ba.length);
1318                 bout.write(ba, 0, ba.length, true);
1319             } else if (ccl == Long.TYPE) {
1320                 long[] ja = (long[]) array;
1321                 bout.writeInt(ja.length);
1322                 bout.writeLongs(ja, 0, ja.length);
1323             } else if (ccl == Float.TYPE) {
1324                 float[] fa = (float[]) array;
1325                 bout.writeInt(fa.length);
1326                 bout.writeFloats(fa, 0, fa.length);
1327             } else if (ccl == Double.TYPE) {
1328                 double[] da = (double[]) array;
1329                 bout.writeInt(da.length);


1360                     }
1361                     try {
1362                         writeObject0(objs[i], false);
1363                     } finally {
1364                         if (extendedDebugInfo) {
1365                             debugInfoStack.pop();
1366                         }
1367                     }
1368                 }
1369             } finally {
1370                 if (extendedDebugInfo) {
1371                     debugInfoStack.pop();
1372                 }
1373             }
1374         }
1375     }
1376 
1377     /**
1378      * Writes given enum constant to stream.
1379      */
1380     private void writeEnum(Enum en,
1381                            ObjectStreamClass desc,
1382                            boolean unshared)
1383         throws IOException
1384     {
1385         bout.writeByte(TC_ENUM);
1386         ObjectStreamClass sdesc = desc.getSuperDesc();
1387         writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
1388         handles.assign(unshared ? null : en);
1389         writeString(en.name(), false);
1390     }
1391 
1392     /**
1393      * Writes representation of a "ordinary" (i.e., not a String, Class,
1394      * ObjectStreamClass, array, or enum constant) serializable object to the
1395      * stream.
1396      */
1397     private void writeOrdinaryObject(Object obj,
1398                                      ObjectStreamClass desc,
1399                                      boolean unshared)
1400         throws IOException


1683                         fields[numPrimFields + i].getName() + "\", type: \"" +
1684                         fields[numPrimFields + i].getType() + "\")");
1685                 }
1686                 try {
1687                     writeObject0(objVals[i],
1688                                  fields[numPrimFields + i].isUnshared());
1689                 } finally {
1690                     if (extendedDebugInfo) {
1691                         debugInfoStack.pop();
1692                     }
1693                 }
1694             }
1695         }
1696 
1697         /**
1698          * Returns offset of field with given name and type.  A specified type
1699          * of null matches all types, Object.class matches all non-primitive
1700          * types, and any other non-null type matches assignable types only.
1701          * Throws IllegalArgumentException if no matching field found.
1702          */
1703         private int getFieldOffset(String name, Class type) {
1704             ObjectStreamField field = desc.getField(name, type);
1705             if (field == null) {
1706                 throw new IllegalArgumentException("no such field " + name +
1707                                                    " with type " + type);
1708             }
1709             return field.getOffset();
1710         }
1711     }
1712 
1713     /**
1714      * Buffered output stream with two modes: in default mode, outputs data in
1715      * same format as DataOutputStream; in "block data" mode, outputs data
1716      * bracketed by block data markers (see object serialization specification
1717      * for details).
1718      */
1719     private static class BlockDataOutputStream
1720         extends OutputStream implements DataOutput
1721     {
1722         /** maximum data block length */
1723         private static final int MAX_BLOCK_SIZE = 1024;




1017      * ObjectStreamClass to write class descriptor type strings.
1018      */
1019     void writeTypeString(String str) throws IOException {
1020         int handle;
1021         if (str == null) {
1022             writeNull();
1023         } else if ((handle = handles.lookup(str)) != -1) {
1024             writeHandle(handle);
1025         } else {
1026             writeString(str, false);
1027         }
1028     }
1029 
1030     /**
1031      * Verifies that this (possibly subclass) instance can be constructed
1032      * without violating security constraints: the subclass must not override
1033      * security-sensitive non-final methods, or else the
1034      * "enableSubclassImplementation" SerializablePermission is checked.
1035      */
1036     private void verifySubclass() {
1037         Class<?> cl = getClass();
1038         if (cl == ObjectOutputStream.class) {
1039             return;
1040         }
1041         SecurityManager sm = System.getSecurityManager();
1042         if (sm == null) {
1043             return;
1044         }
1045         processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
1046         WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
1047         Boolean result = Caches.subclassAudits.get(key);
1048         if (result == null) {
1049             result = Boolean.valueOf(auditSubclass(cl));
1050             Caches.subclassAudits.putIfAbsent(key, result);
1051         }
1052         if (result.booleanValue()) {
1053             return;
1054         }
1055         sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
1056     }
1057 
1058     /**
1059      * Performs reflective checks on given subclass to verify that it doesn't
1060      * override security-sensitive non-final methods.  Returns true if subclass
1061      * is "safe", false otherwise.
1062      */
1063     private static boolean auditSubclass(final Class<?> subcl) {
1064         Boolean result = AccessController.doPrivileged(
1065             new PrivilegedAction<Boolean>() {
1066                 public Boolean run() {
1067                     for (Class<?> cl = subcl;
1068                          cl != ObjectOutputStream.class;
1069                          cl = cl.getSuperclass())
1070                     {
1071                         try {
1072                             cl.getDeclaredMethod(
1073                                 "writeUnshared", new Class<?>[] { Object.class });
1074                             return Boolean.FALSE;
1075                         } catch (NoSuchMethodException ex) {
1076                         }
1077                         try {
1078                             cl.getDeclaredMethod("putFields", (Class[]) null);
1079                             return Boolean.FALSE;
1080                         } catch (NoSuchMethodException ex) {
1081                         }
1082                     }
1083                     return Boolean.TRUE;
1084                 }
1085             }
1086         );
1087         return result.booleanValue();
1088     }
1089 
1090     /**
1091      * Clears internal data structures.
1092      */
1093     private void clear() {


1105         depth++;
1106         try {
1107             // handle previously written and non-replaceable objects
1108             int h;
1109             if ((obj = subs.lookup(obj)) == null) {
1110                 writeNull();
1111                 return;
1112             } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1113                 writeHandle(h);
1114                 return;
1115             } else if (obj instanceof Class) {
1116                 writeClass((Class) obj, unshared);
1117                 return;
1118             } else if (obj instanceof ObjectStreamClass) {
1119                 writeClassDesc((ObjectStreamClass) obj, unshared);
1120                 return;
1121             }
1122 
1123             // check for replacement object
1124             Object orig = obj;
1125             Class<?> cl = obj.getClass();
1126             ObjectStreamClass desc;
1127             for (;;) {
1128                 // REMIND: skip this check for strings/arrays?
1129                 Class<?> repCl;
1130                 desc = ObjectStreamClass.lookup(cl, true);
1131                 if (!desc.hasWriteReplaceMethod() ||
1132                     (obj = desc.invokeWriteReplace(obj)) == null ||
1133                     (repCl = obj.getClass()) == cl)
1134                 {
1135                     break;
1136                 }
1137                 cl = repCl;
1138             }
1139             if (enableReplace) {
1140                 Object rep = replaceObject(obj);
1141                 if (rep != obj && rep != null) {
1142                     cl = rep.getClass();
1143                     desc = ObjectStreamClass.lookup(cl, true);
1144                 }
1145                 obj = rep;
1146             }
1147 
1148             // if object replaced, run through original checks a second time
1149             if (obj != orig) {


1152                     writeNull();
1153                     return;
1154                 } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1155                     writeHandle(h);
1156                     return;
1157                 } else if (obj instanceof Class) {
1158                     writeClass((Class) obj, unshared);
1159                     return;
1160                 } else if (obj instanceof ObjectStreamClass) {
1161                     writeClassDesc((ObjectStreamClass) obj, unshared);
1162                     return;
1163                 }
1164             }
1165 
1166             // remaining cases
1167             if (obj instanceof String) {
1168                 writeString((String) obj, unshared);
1169             } else if (cl.isArray()) {
1170                 writeArray(obj, desc, unshared);
1171             } else if (obj instanceof Enum) {
1172                 writeEnum((Enum<?>) obj, desc, unshared);
1173             } else if (obj instanceof Serializable) {
1174                 writeOrdinaryObject(obj, desc, unshared);
1175             } else {
1176                 if (extendedDebugInfo) {
1177                     throw new NotSerializableException(
1178                         cl.getName() + "\n" + debugInfoStack.toString());
1179                 } else {
1180                     throw new NotSerializableException(cl.getName());
1181                 }
1182             }
1183         } finally {
1184             depth--;
1185             bout.setBlockDataMode(oldMode);
1186         }
1187     }
1188 
1189     /**
1190      * Writes null code to stream.
1191      */
1192     private void writeNull() throws IOException {
1193         bout.writeByte(TC_NULL);
1194     }
1195 
1196     /**
1197      * Writes given object handle to stream.
1198      */
1199     private void writeHandle(int handle) throws IOException {
1200         bout.writeByte(TC_REFERENCE);
1201         bout.writeInt(baseWireHandle + handle);
1202     }
1203 
1204     /**
1205      * Writes representation of given class to stream.
1206      */
1207     private void writeClass(Class<?> cl, boolean unshared) throws IOException {
1208         bout.writeByte(TC_CLASS);
1209         writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
1210         handles.assign(unshared ? null : cl);
1211     }
1212 
1213     /**
1214      * Writes representation of given class descriptor to stream.
1215      */
1216     private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
1217         throws IOException
1218     {
1219         int handle;
1220         if (desc == null) {
1221             writeNull();
1222         } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
1223             writeHandle(handle);
1224         } else if (desc.isProxy()) {
1225             writeProxyDesc(desc, unshared);
1226         } else {
1227             writeNonProxyDesc(desc, unshared);
1228         }
1229     }
1230 
1231     /**
1232      * Writes class descriptor representing a dynamic proxy class to stream.
1233      */
1234     private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
1235         throws IOException
1236     {
1237         bout.writeByte(TC_PROXYCLASSDESC);
1238         handles.assign(unshared ? null : desc);
1239 
1240         Class<?> cl = desc.forClass();
1241         Class[] ifaces = cl.getInterfaces();
1242         bout.writeInt(ifaces.length);
1243         for (int i = 0; i < ifaces.length; i++) {
1244             bout.writeUTF(ifaces[i].getName());
1245         }
1246 
1247         bout.setBlockDataMode(true);
1248         annotateProxyClass(cl);
1249         bout.setBlockDataMode(false);
1250         bout.writeByte(TC_ENDBLOCKDATA);
1251 
1252         writeClassDesc(desc.getSuperDesc(), false);
1253     }
1254 
1255     /**
1256      * Writes class descriptor representing a standard (i.e., not a dynamic
1257      * proxy) class to stream.
1258      */
1259     private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
1260         throws IOException
1261     {
1262         bout.writeByte(TC_CLASSDESC);
1263         handles.assign(unshared ? null : desc);
1264 
1265         if (protocol == PROTOCOL_VERSION_1) {
1266             // do not invoke class descriptor write hook with old protocol
1267             desc.writeNonProxy(this);
1268         } else {
1269             writeClassDescriptor(desc);
1270         }
1271 
1272         Class<?> cl = desc.forClass();
1273         bout.setBlockDataMode(true);
1274         annotateClass(cl);
1275         bout.setBlockDataMode(false);
1276         bout.writeByte(TC_ENDBLOCKDATA);
1277 
1278         writeClassDesc(desc.getSuperDesc(), false);
1279     }
1280 
1281     /**
1282      * Writes given string to stream, using standard or long UTF format
1283      * depending on string length.
1284      */
1285     private void writeString(String str, boolean unshared) throws IOException {
1286         handles.assign(unshared ? null : str);
1287         long utflen = bout.getUTFLength(str);
1288         if (utflen <= 0xFFFF) {
1289             bout.writeByte(TC_STRING);
1290             bout.writeUTF(str, utflen);
1291         } else {
1292             bout.writeByte(TC_LONGSTRING);
1293             bout.writeLongUTF(str, utflen);
1294         }
1295     }
1296 
1297     /**
1298      * Writes given array object to stream.
1299      */
1300     private void writeArray(Object array,
1301                             ObjectStreamClass desc,
1302                             boolean unshared)
1303         throws IOException
1304     {
1305         bout.writeByte(TC_ARRAY);
1306         writeClassDesc(desc, false);
1307         handles.assign(unshared ? null : array);
1308 
1309         Class<?> ccl = desc.forClass().getComponentType();
1310         if (ccl.isPrimitive()) {
1311             if (ccl == Integer.TYPE) {
1312                 int[] ia = (int[]) array;
1313                 bout.writeInt(ia.length);
1314                 bout.writeInts(ia, 0, ia.length);
1315             } else if (ccl == Byte.TYPE) {
1316                 byte[] ba = (byte[]) array;
1317                 bout.writeInt(ba.length);
1318                 bout.write(ba, 0, ba.length, true);
1319             } else if (ccl == Long.TYPE) {
1320                 long[] ja = (long[]) array;
1321                 bout.writeInt(ja.length);
1322                 bout.writeLongs(ja, 0, ja.length);
1323             } else if (ccl == Float.TYPE) {
1324                 float[] fa = (float[]) array;
1325                 bout.writeInt(fa.length);
1326                 bout.writeFloats(fa, 0, fa.length);
1327             } else if (ccl == Double.TYPE) {
1328                 double[] da = (double[]) array;
1329                 bout.writeInt(da.length);


1360                     }
1361                     try {
1362                         writeObject0(objs[i], false);
1363                     } finally {
1364                         if (extendedDebugInfo) {
1365                             debugInfoStack.pop();
1366                         }
1367                     }
1368                 }
1369             } finally {
1370                 if (extendedDebugInfo) {
1371                     debugInfoStack.pop();
1372                 }
1373             }
1374         }
1375     }
1376 
1377     /**
1378      * Writes given enum constant to stream.
1379      */
1380     private void writeEnum(Enum<?> en,
1381                            ObjectStreamClass desc,
1382                            boolean unshared)
1383         throws IOException
1384     {
1385         bout.writeByte(TC_ENUM);
1386         ObjectStreamClass sdesc = desc.getSuperDesc();
1387         writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
1388         handles.assign(unshared ? null : en);
1389         writeString(en.name(), false);
1390     }
1391 
1392     /**
1393      * Writes representation of a "ordinary" (i.e., not a String, Class,
1394      * ObjectStreamClass, array, or enum constant) serializable object to the
1395      * stream.
1396      */
1397     private void writeOrdinaryObject(Object obj,
1398                                      ObjectStreamClass desc,
1399                                      boolean unshared)
1400         throws IOException


1683                         fields[numPrimFields + i].getName() + "\", type: \"" +
1684                         fields[numPrimFields + i].getType() + "\")");
1685                 }
1686                 try {
1687                     writeObject0(objVals[i],
1688                                  fields[numPrimFields + i].isUnshared());
1689                 } finally {
1690                     if (extendedDebugInfo) {
1691                         debugInfoStack.pop();
1692                     }
1693                 }
1694             }
1695         }
1696 
1697         /**
1698          * Returns offset of field with given name and type.  A specified type
1699          * of null matches all types, Object.class matches all non-primitive
1700          * types, and any other non-null type matches assignable types only.
1701          * Throws IllegalArgumentException if no matching field found.
1702          */
1703         private int getFieldOffset(String name, Class<?> type) {
1704             ObjectStreamField field = desc.getField(name, type);
1705             if (field == null) {
1706                 throw new IllegalArgumentException("no such field " + name +
1707                                                    " with type " + type);
1708             }
1709             return field.getOffset();
1710         }
1711     }
1712 
1713     /**
1714      * Buffered output stream with two modes: in default mode, outputs data in
1715      * same format as DataOutputStream; in "block data" mode, outputs data
1716      * bracketed by block data markers (see object serialization specification
1717      * for details).
1718      */
1719     private static class BlockDataOutputStream
1720         extends OutputStream implements DataOutput
1721     {
1722         /** maximum data block length */
1723         private static final int MAX_BLOCK_SIZE = 1024;