22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27 package java.io;
28
29 import java.security.AccessController;
30 import java.security.PrivilegedAction;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.Objects;
35 import java.util.StringJoiner;
36
37 import jdk.internal.util.ByteArray;
38 import jdk.internal.access.JavaLangAccess;
39 import jdk.internal.access.SharedSecrets;
40 import sun.reflect.misc.ReflectUtil;
41
42 import static jdk.internal.util.ModifiedUtf.putChar;
43 import static jdk.internal.util.ModifiedUtf.utfLen;
44
45 /**
46 * An ObjectOutputStream writes primitive data types and graphs of Java objects
47 * to an OutputStream. The objects can be read (reconstituted) using an
48 * ObjectInputStream. Persistent storage of objects can be accomplished by
49 * using a file for the stream. If the stream is a network socket stream, the
50 * objects can be reconstituted on another host or in another process.
51 *
52 * <p>Only objects that support the java.io.Serializable interface can be
53 * written to streams. The class of each serializable object is encoded
54 * including the class name and signature of the class, the values of the
55 * object's fields and arrays, and the closure of any other objects referenced
56 * from the initial objects.
57 *
58 * <p>The method writeObject is used to write an object to the stream. Any
59 * object, including Strings and arrays, is written with writeObject. Multiple
60 * objects or primitives can be written to the stream. The objects must be
61 * read back from the corresponding ObjectInputstream with the same types and
116 * implement the java.io.Serializable interface. Subclasses of Objects that
117 * are not serializable can be serializable. In this case the non-serializable
118 * class must have a no-arg constructor to allow its fields to be initialized.
119 * In this case it is the responsibility of the subclass to save and restore
120 * the state of the non-serializable class. It is frequently the case that the
121 * fields of that class are accessible (public, package, or protected) or that
122 * there are get and set methods that can be used to restore the state.
123 *
124 * <p>Serialization of an object can be prevented by implementing writeObject
125 * and readObject methods that throw the NotSerializableException. The
126 * exception will be caught by the ObjectOutputStream and abort the
127 * serialization process.
128 *
129 * <p>Implementing the Externalizable interface allows the object to assume
130 * complete control over the contents and format of the object's serialized
131 * form. The methods of the Externalizable interface, writeExternal and
132 * readExternal, are called to save and restore the objects state. When
133 * implemented by a class they can write and read their own state using all of
134 * the methods of ObjectOutput and ObjectInput. It is the responsibility of
135 * the objects to handle any versioning that occurs.
136 *
137 * <p>Enum constants are serialized differently than ordinary serializable or
138 * externalizable objects. The serialized form of an enum constant consists
139 * solely of its name; field values of the constant are not transmitted. To
140 * serialize an enum constant, ObjectOutputStream writes the string returned by
141 * the constant's name method. Like other serializable or externalizable
142 * objects, enum constants can function as the targets of back references
143 * appearing subsequently in the serialization stream. The process by which
144 * enum constants are serialized cannot be customized; any class-specific
145 * writeObject and writeReplace methods defined by enum types are ignored
146 * during serialization. Similarly, any serialPersistentFields or
147 * serialVersionUID field declarations are also ignored--all enum types have a
148 * fixed serialVersionUID of 0L.
149 *
150 * <p>Primitive data, excluding serializable fields and externalizable data, is
151 * written to the ObjectOutputStream in block-data records. A block data record
152 * is composed of a header and data. The block data header consists of a marker
153 * and the number of bytes to follow the header. Consecutive primitive data
154 * writes are merged into one block-data record. The blocking factor used for
155 * a block-data record will be 1024 bytes. Each block-data record will be
156 * filled up to 1024 bytes, or be written whenever there is a termination of
157 * block-data mode. Calls to the ObjectOutputStream methods writeObject,
158 * defaultWriteObject and writeFields initially terminate any existing
159 * block-data record.
160 *
161 * <p>Records are serialized differently than ordinary serializable or externalizable
162 * objects, see <a href="ObjectInputStream.html#record-serialization">record serialization</a>.
163 *
164 * @spec serialization/index.html Java Object Serialization Specification
165 * @author Mike Warres
166 * @author Roger Riggs
167 * @see java.io.DataOutput
168 * @see java.io.ObjectInputStream
169 * @see java.io.Serializable
170 * @see java.io.Externalizable
171 * @see <a href="{@docRoot}/../specs/serialization/output.html">
172 * <cite>Java Object Serialization Specification,</cite> Section 2, "Object Output Classes"</a>
173 * @since 1.1
174 */
175 public class ObjectOutputStream
176 extends OutputStream implements ObjectOutput, ObjectStreamConstants
177 {
178 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
179
180 private static class Caches {
181 /** cache of subclass security audit results */
182 static final ClassValue<Boolean> subclassAudits =
183 new ClassValue<>() {
328 case PROTOCOL_VERSION_1:
329 case PROTOCOL_VERSION_2:
330 protocol = version;
331 break;
332
333 default:
334 throw new IllegalArgumentException(
335 "unknown version: " + version);
336 }
337 }
338
339 /**
340 * Write the specified object to the ObjectOutputStream. The class of the
341 * object, the signature of the class, and the values of the non-transient
342 * and non-static fields of the class and all of its supertypes are
343 * written. Default serialization for a class can be overridden using the
344 * writeObject and the readObject methods. Objects referenced by this
345 * object are written transitively so that a complete equivalent graph of
346 * objects can be reconstructed by an ObjectInputStream.
347 *
348 * <p>Exceptions are thrown for problems with the OutputStream and for
349 * classes that should not be serialized. All exceptions are fatal to the
350 * OutputStream, which is left in an indeterminate state, and it is up to
351 * the caller to ignore or recover the stream state.
352 *
353 * @throws InvalidClassException Something is wrong with a class used by
354 * serialization.
355 * @throws NotSerializableException Some object to be serialized does not
356 * implement the java.io.Serializable interface.
357 * @throws IOException Any exception thrown by the underlying
358 * OutputStream.
359 */
360 public final void writeObject(Object obj) throws IOException {
361 if (enableOverride) {
362 writeObjectOverride(obj);
363 return;
364 }
365 try {
366 writeObject0(obj, false);
367 } catch (IOException ex) {
397 * <ul>
398 * <li>An object written via writeUnshared is always serialized in the
399 * same manner as a newly appearing object (an object that has not
400 * been written to the stream yet), regardless of whether or not the
401 * object has been written previously.
402 *
403 * <li>If writeObject is used to write an object that has been previously
404 * written with writeUnshared, the previous writeUnshared operation
405 * is treated as if it were a write of a separate object. In other
406 * words, ObjectOutputStream will never generate back-references to
407 * object data written by calls to writeUnshared.
408 * </ul>
409 * While writing an object via writeUnshared does not in itself guarantee a
410 * unique reference to the object when it is deserialized, it allows a
411 * single object to be defined multiple times in a stream, so that multiple
412 * calls to readUnshared by the receiver will not conflict. Note that the
413 * rules described above only apply to the base-level object written with
414 * writeUnshared, and not to any transitively referenced sub-objects in the
415 * object graph to be serialized.
416 *
417 * <p>ObjectOutputStream subclasses which override this method can only be
418 * constructed in security contexts possessing the
419 * "enableSubclassImplementation" SerializablePermission; any attempt to
420 * instantiate such a subclass without this permission will cause a
421 * SecurityException to be thrown.
422 *
423 * @param obj object to write to stream
424 * @throws NotSerializableException if an object in the graph to be
425 * serialized does not implement the Serializable interface
426 * @throws InvalidClassException if a problem exists with the class of an
427 * object to be serialized
428 * @throws IOException if an I/O error occurs during serialization
429 * @since 1.4
430 */
431 public void writeUnshared(Object obj) throws IOException {
432 try {
433 writeObject0(obj, true);
434 } catch (IOException ex) {
435 if (depth == 0) {
436 writeFatalException(ex);
1430 private void writeOrdinaryObject(Object obj,
1431 ObjectStreamClass desc,
1432 boolean unshared)
1433 throws IOException
1434 {
1435 if (extendedDebugInfo) {
1436 debugInfoStack.push(
1437 (depth == 1 ? "root " : "") + "object (class \"" +
1438 obj.getClass().getName() + "\", " + obj.toString() + ")");
1439 }
1440 try {
1441 desc.checkSerialize();
1442
1443 bout.writeByte(TC_OBJECT);
1444 writeClassDesc(desc, false);
1445 handles.assign(unshared ? null : obj);
1446
1447 if (desc.isRecord()) {
1448 writeRecordData(obj, desc);
1449 } else if (desc.isExternalizable() && !desc.isProxy()) {
1450 writeExternalData((Externalizable) obj);
1451 } else {
1452 writeSerialData(obj, desc);
1453 }
1454 } finally {
1455 if (extendedDebugInfo) {
1456 debugInfoStack.pop();
1457 }
1458 }
1459 }
1460
1461 /**
1462 * Writes externalizable data of given object by invoking its
1463 * writeExternal() method.
1464 */
1465 private void writeExternalData(Externalizable obj) throws IOException {
1466 PutFieldImpl oldPut = curPut;
1467 curPut = null;
1468
1469 if (extendedDebugInfo) {
1478 bout.setBlockDataMode(true);
1479 obj.writeExternal(this);
1480 bout.setBlockDataMode(false);
1481 bout.writeByte(TC_ENDBLOCKDATA);
1482 }
1483 } finally {
1484 curContext = oldContext;
1485 if (extendedDebugInfo) {
1486 debugInfoStack.pop();
1487 }
1488 }
1489
1490 curPut = oldPut;
1491 }
1492
1493 /** Writes the record component values for the given record object. */
1494 private void writeRecordData(Object obj, ObjectStreamClass desc)
1495 throws IOException
1496 {
1497 assert obj.getClass().isRecord();
1498 ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
1499 if (slots.length != 1) {
1500 throw new InvalidClassException(
1501 "expected a single record slot length, but found: " + slots.length);
1502 }
1503
1504 defaultWriteFields(obj, desc); // #### seems unnecessary to use the accessors
1505 }
1506
1507 /**
1508 * Writes instance data for each serializable class of given object, from
1509 * superclass to subclass.
1510 */
1511 private void writeSerialData(Object obj, ObjectStreamClass desc)
1512 throws IOException
1513 {
1514 ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
1515 for (int i = 0; i < slots.length; i++) {
1516 ObjectStreamClass slotDesc = slots[i].desc;
1517 if (slotDesc.hasWriteObjectMethod()) {
1518 PutFieldImpl oldPut = curPut;
1519 curPut = null;
1520 SerialCallbackContext oldContext = curContext;
1521
1522 if (extendedDebugInfo) {
1523 debugInfoStack.push(
1524 "custom writeObject data (class \"" +
1525 slotDesc.getName() + "\")");
1526 }
1527 try {
1528 curContext = new SerialCallbackContext(obj, slotDesc);
1529 bout.setBlockDataMode(true);
1530 slotDesc.invokeWriteObject(obj, this);
1531 bout.setBlockDataMode(false);
1532 bout.writeByte(TC_ENDBLOCKDATA);
1533 } finally {
1534 curContext.setUsed();
1535 curContext = oldContext;
1536 if (extendedDebugInfo) {
|
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27 package java.io;
28
29 import java.security.AccessController;
30 import java.security.PrivilegedAction;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.Objects;
35 import java.util.StringJoiner;
36
37 import jdk.internal.util.ByteArray;
38 import jdk.internal.access.JavaLangAccess;
39 import jdk.internal.access.SharedSecrets;
40 import sun.reflect.misc.ReflectUtil;
41
42 import static java.io.ObjectInputStream.TRACE;
43
44 import static jdk.internal.util.ModifiedUtf.putChar;
45 import static jdk.internal.util.ModifiedUtf.utfLen;
46
47 /**
48 * An ObjectOutputStream writes primitive data types and graphs of Java objects
49 * to an OutputStream. The objects can be read (reconstituted) using an
50 * ObjectInputStream. Persistent storage of objects can be accomplished by
51 * using a file for the stream. If the stream is a network socket stream, the
52 * objects can be reconstituted on another host or in another process.
53 *
54 * <p>Only objects that support the java.io.Serializable interface can be
55 * written to streams. The class of each serializable object is encoded
56 * including the class name and signature of the class, the values of the
57 * object's fields and arrays, and the closure of any other objects referenced
58 * from the initial objects.
59 *
60 * <p>The method writeObject is used to write an object to the stream. Any
61 * object, including Strings and arrays, is written with writeObject. Multiple
62 * objects or primitives can be written to the stream. The objects must be
63 * read back from the corresponding ObjectInputstream with the same types and
118 * implement the java.io.Serializable interface. Subclasses of Objects that
119 * are not serializable can be serializable. In this case the non-serializable
120 * class must have a no-arg constructor to allow its fields to be initialized.
121 * In this case it is the responsibility of the subclass to save and restore
122 * the state of the non-serializable class. It is frequently the case that the
123 * fields of that class are accessible (public, package, or protected) or that
124 * there are get and set methods that can be used to restore the state.
125 *
126 * <p>Serialization of an object can be prevented by implementing writeObject
127 * and readObject methods that throw the NotSerializableException. The
128 * exception will be caught by the ObjectOutputStream and abort the
129 * serialization process.
130 *
131 * <p>Implementing the Externalizable interface allows the object to assume
132 * complete control over the contents and format of the object's serialized
133 * form. The methods of the Externalizable interface, writeExternal and
134 * readExternal, are called to save and restore the objects state. When
135 * implemented by a class they can write and read their own state using all of
136 * the methods of ObjectOutput and ObjectInput. It is the responsibility of
137 * the objects to handle any versioning that occurs.
138 * Value classes implementing {@link Externalizable} cannot be serialized
139 * or deserialized, the value object is immutable and the state cannot be restored.
140 * Use {@link Serializable} {@code writeReplace} to delegate to another serializable
141 * object such as a record.
142 *
143 * Value objects cannot be {@code java.io.Externalizable}.
144 *
145 * <p>Enum constants are serialized differently than ordinary serializable or
146 * externalizable objects. The serialized form of an enum constant consists
147 * solely of its name; field values of the constant are not transmitted. To
148 * serialize an enum constant, ObjectOutputStream writes the string returned by
149 * the constant's name method. Like other serializable or externalizable
150 * objects, enum constants can function as the targets of back references
151 * appearing subsequently in the serialization stream. The process by which
152 * enum constants are serialized cannot be customized; any class-specific
153 * writeObject and writeReplace methods defined by enum types are ignored
154 * during serialization. Similarly, any serialPersistentFields or
155 * serialVersionUID field declarations are also ignored--all enum types have a
156 * fixed serialVersionUID of 0L.
157 *
158 * <p>Primitive data, excluding serializable fields and externalizable data, is
159 * written to the ObjectOutputStream in block-data records. A block data record
160 * is composed of a header and data. The block data header consists of a marker
161 * and the number of bytes to follow the header. Consecutive primitive data
162 * writes are merged into one block-data record. The blocking factor used for
163 * a block-data record will be 1024 bytes. Each block-data record will be
164 * filled up to 1024 bytes, or be written whenever there is a termination of
165 * block-data mode. Calls to the ObjectOutputStream methods writeObject,
166 * defaultWriteObject and writeFields initially terminate any existing
167 * block-data record.
168 *
169 * <a id="record-serialization"></a>
170 * <p>Records are serialized differently than ordinary serializable or externalizable
171 * objects, see <a href="ObjectInputStream.html#record-serialization">record serialization</a>.
172 *
173 * <a id="valueclass-serialization"></a>
174 * <p>Value classes are {@linkplain Serializable} through the use of the serialization proxy pattern.
175 * The serialization protocol does not support a standard serialized form for value classes.
176 * The value class delegates to a serialization proxy by supplying an alternate
177 * record or object to be serialized instead of the value class.
178 * When the proxy is deserialized it re-constructs the value object and returns the value object.
179 * For example,
180 * {@snippet lang="java" :
181 * value class ZipCode implements Serializable { // @highlight substring="value class"
182 * private static final long serialVersionUID = 1L;
183 * private int zipCode;
184 * public ZipCode(int zip) { this.zipCode = zip; }
185 * public int zipCode() { return zipCode; }
186 *
187 * public Object writeReplace() { // @highlight substring="writeReplace"
188 * return new ZipCodeProxy(zipCode);
189 * }
190 *
191 * private record ZipCodeProxy(int zipCode) implements Serializable {
192 * public Object readResolve() { // @highlight substring="readResolve"
193 * return new ZipCode(zipCode);
194 * }
195 * }
196 * }
197 * }
198 *
199 * @spec serialization/index.html Java Object Serialization Specification
200 * @author Mike Warres
201 * @author Roger Riggs
202 * @see java.io.DataOutput
203 * @see java.io.ObjectInputStream
204 * @see java.io.Serializable
205 * @see java.io.Externalizable
206 * @see <a href="{@docRoot}/../specs/serialization/output.html">
207 * <cite>Java Object Serialization Specification,</cite> Section 2, "Object Output Classes"</a>
208 * @since 1.1
209 */
210 public class ObjectOutputStream
211 extends OutputStream implements ObjectOutput, ObjectStreamConstants
212 {
213 private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
214
215 private static class Caches {
216 /** cache of subclass security audit results */
217 static final ClassValue<Boolean> subclassAudits =
218 new ClassValue<>() {
363 case PROTOCOL_VERSION_1:
364 case PROTOCOL_VERSION_2:
365 protocol = version;
366 break;
367
368 default:
369 throw new IllegalArgumentException(
370 "unknown version: " + version);
371 }
372 }
373
374 /**
375 * Write the specified object to the ObjectOutputStream. The class of the
376 * object, the signature of the class, and the values of the non-transient
377 * and non-static fields of the class and all of its supertypes are
378 * written. Default serialization for a class can be overridden using the
379 * writeObject and the readObject methods. Objects referenced by this
380 * object are written transitively so that a complete equivalent graph of
381 * objects can be reconstructed by an ObjectInputStream.
382 *
383 * <p>Serialization and deserialization of value classes is described in
384 * {@linkplain ObjectOutputStream##valueclass-serialization value class serialization}.
385 *
386 * <p>Exceptions are thrown for problems with the OutputStream and for
387 * classes that should not be serialized. All exceptions are fatal to the
388 * OutputStream, which is left in an indeterminate state, and it is up to
389 * the caller to ignore or recover the stream state.
390 *
391 * @throws InvalidClassException Something is wrong with a class used by
392 * serialization.
393 * @throws NotSerializableException Some object to be serialized does not
394 * implement the java.io.Serializable interface.
395 * @throws IOException Any exception thrown by the underlying
396 * OutputStream.
397 */
398 public final void writeObject(Object obj) throws IOException {
399 if (enableOverride) {
400 writeObjectOverride(obj);
401 return;
402 }
403 try {
404 writeObject0(obj, false);
405 } catch (IOException ex) {
435 * <ul>
436 * <li>An object written via writeUnshared is always serialized in the
437 * same manner as a newly appearing object (an object that has not
438 * been written to the stream yet), regardless of whether or not the
439 * object has been written previously.
440 *
441 * <li>If writeObject is used to write an object that has been previously
442 * written with writeUnshared, the previous writeUnshared operation
443 * is treated as if it were a write of a separate object. In other
444 * words, ObjectOutputStream will never generate back-references to
445 * object data written by calls to writeUnshared.
446 * </ul>
447 * While writing an object via writeUnshared does not in itself guarantee a
448 * unique reference to the object when it is deserialized, it allows a
449 * single object to be defined multiple times in a stream, so that multiple
450 * calls to readUnshared by the receiver will not conflict. Note that the
451 * rules described above only apply to the base-level object written with
452 * writeUnshared, and not to any transitively referenced sub-objects in the
453 * object graph to be serialized.
454 *
455 * <p>Serialization and deserialization of value classes is described in
456 * {@linkplain ObjectOutputStream##valueclass-serialization value class serialization}.
457 *
458 * <p>ObjectOutputStream subclasses which override this method can only be
459 * constructed in security contexts possessing the
460 * "enableSubclassImplementation" SerializablePermission; any attempt to
461 * instantiate such a subclass without this permission will cause a
462 * SecurityException to be thrown.
463 *
464 * @param obj object to write to stream
465 * @throws NotSerializableException if an object in the graph to be
466 * serialized does not implement the Serializable interface
467 * @throws InvalidClassException if a problem exists with the class of an
468 * object to be serialized
469 * @throws IOException if an I/O error occurs during serialization
470 * @since 1.4
471 */
472 public void writeUnshared(Object obj) throws IOException {
473 try {
474 writeObject0(obj, true);
475 } catch (IOException ex) {
476 if (depth == 0) {
477 writeFatalException(ex);
1471 private void writeOrdinaryObject(Object obj,
1472 ObjectStreamClass desc,
1473 boolean unshared)
1474 throws IOException
1475 {
1476 if (extendedDebugInfo) {
1477 debugInfoStack.push(
1478 (depth == 1 ? "root " : "") + "object (class \"" +
1479 obj.getClass().getName() + "\", " + obj.toString() + ")");
1480 }
1481 try {
1482 desc.checkSerialize();
1483
1484 bout.writeByte(TC_OBJECT);
1485 writeClassDesc(desc, false);
1486 handles.assign(unshared ? null : obj);
1487
1488 if (desc.isRecord()) {
1489 writeRecordData(obj, desc);
1490 } else if (desc.isExternalizable() && !desc.isProxy()) {
1491 if (desc.isValue())
1492 throw new InvalidClassException("Externalizable not valid for value class "
1493 + desc.forClass().getName());
1494 writeExternalData((Externalizable) obj);
1495 } else {
1496 writeSerialData(obj, desc);
1497 }
1498 } finally {
1499 if (extendedDebugInfo) {
1500 debugInfoStack.pop();
1501 }
1502 }
1503 }
1504
1505 /**
1506 * Writes externalizable data of given object by invoking its
1507 * writeExternal() method.
1508 */
1509 private void writeExternalData(Externalizable obj) throws IOException {
1510 PutFieldImpl oldPut = curPut;
1511 curPut = null;
1512
1513 if (extendedDebugInfo) {
1522 bout.setBlockDataMode(true);
1523 obj.writeExternal(this);
1524 bout.setBlockDataMode(false);
1525 bout.writeByte(TC_ENDBLOCKDATA);
1526 }
1527 } finally {
1528 curContext = oldContext;
1529 if (extendedDebugInfo) {
1530 debugInfoStack.pop();
1531 }
1532 }
1533
1534 curPut = oldPut;
1535 }
1536
1537 /** Writes the record component values for the given record object. */
1538 private void writeRecordData(Object obj, ObjectStreamClass desc)
1539 throws IOException
1540 {
1541 assert obj.getClass().isRecord();
1542 List<ObjectStreamClass.ClassDataSlot> slots = desc.getClassDataLayout();
1543 if (slots.size() != 1) {
1544 throw new InvalidClassException(
1545 "expected a single record slot length, but found: " + slots.size());
1546 }
1547
1548 defaultWriteFields(obj, desc); // #### seems unnecessary to use the accessors
1549 }
1550
1551 /**
1552 * Writes instance data for each serializable class of given object, from
1553 * superclass to subclass.
1554 */
1555 private void writeSerialData(Object obj, ObjectStreamClass desc)
1556 throws IOException
1557 {
1558 List<ObjectStreamClass.ClassDataSlot> slots = desc.getClassDataLayout();
1559 for (int i = 0; i < slots.size(); i++) {
1560 ObjectStreamClass slotDesc = slots.get(i).desc;
1561 if (slotDesc.hasWriteObjectMethod()) {
1562 PutFieldImpl oldPut = curPut;
1563 curPut = null;
1564 SerialCallbackContext oldContext = curContext;
1565
1566 if (extendedDebugInfo) {
1567 debugInfoStack.push(
1568 "custom writeObject data (class \"" +
1569 slotDesc.getName() + "\")");
1570 }
1571 try {
1572 curContext = new SerialCallbackContext(obj, slotDesc);
1573 bout.setBlockDataMode(true);
1574 slotDesc.invokeWriteObject(obj, this);
1575 bout.setBlockDataMode(false);
1576 bout.writeByte(TC_ENDBLOCKDATA);
1577 } finally {
1578 curContext.setUsed();
1579 curContext = oldContext;
1580 if (extendedDebugInfo) {
|