123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808 |
- /*
- * Javassist, a Java-bytecode translator toolkit.
- * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. Alternatively, the contents of this file may be used under
- * the terms of the GNU Lesser General Public License Version 2.1 or later,
- * or the Apache License Version 2.0.
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- */
-
- package javassist.bytecode;
-
- import java.io.DataOutputStream;
- import java.io.IOException;
- import java.io.OutputStream;
-
- /**
- * A quick class-file writer. This is useful when a generated
- * class file is simple and the code generation should be fast.
- *
- * <p>Example:
- *
- * <blockquote><pre>
- * ClassFileWriter cfw = new ClassFileWriter(ClassFile.JAVA_4, 0);
- * ConstPoolWriter cpw = cfw.getConstPool();
- *
- * FieldWriter fw = cfw.getFieldWriter();
- * fw.add(AccessFlag.PUBLIC, "value", "I", null);
- * fw.add(AccessFlag.PUBLIC, "value2", "J", null);
- *
- * int thisClass = cpw.addClassInfo("sample/Test");
- * int superClass = cpw.addClassInfo("java/lang/Object");
- *
- * MethodWriter mw = cfw.getMethodWriter();
- *
- * mw.begin(AccessFlag.PUBLIC, MethodInfo.nameInit, "()V", null, null);
- * mw.add(Opcode.ALOAD_0);
- * mw.add(Opcode.INVOKESPECIAL);
- * int signature = cpw.addNameAndTypeInfo(MethodInfo.nameInit, "()V");
- * mw.add16(cpw.addMethodrefInfo(superClass, signature));
- * mw.add(Opcode.RETURN);
- * mw.codeEnd(1, 1);
- * mw.end(null, null);
- *
- * mw.begin(AccessFlag.PUBLIC, "one", "()I", null, null);
- * mw.add(Opcode.ICONST_1);
- * mw.add(Opcode.IRETURN);
- * mw.codeEnd(1, 1);
- * mw.end(null, null);
- *
- * byte[] classfile = cfw.end(AccessFlag.PUBLIC, thisClass, superClass,
- * null, null);
- * </pre></blockquote>
- *
- * <p>The code above generates the following class:
- *
- * <blockquote><pre>
- * package sample;
- * public class Test {
- * public int value;
- * public long value2;
- * public Test() { super(); }
- * public one() { return 1; }
- * }
- * </pre></blockquote>
- *
- * @since 3.13
- */
- public class ClassFileWriter {
- private ByteStream output;
- private ConstPoolWriter constPool;
- private FieldWriter fields;
- private MethodWriter methods;
- int thisClass, superClass;
-
- /**
- * Constructs a class file writer.
- *
- * @param major the major version ({@link ClassFile#JAVA_4}, {@link ClassFile#JAVA_5}, ...).
- * @param minor the minor version (0 for JDK 1.3 and later).
- */
- public ClassFileWriter(int major, int minor) {
- output = new ByteStream(512);
- output.writeInt(0xCAFEBABE); // magic
- output.writeShort(minor);
- output.writeShort(major);
- constPool = new ConstPoolWriter(output);
- fields = new FieldWriter(constPool);
- methods = new MethodWriter(constPool);
-
- }
-
- /**
- * Returns a constant pool.
- */
- public ConstPoolWriter getConstPool() { return constPool; }
-
- /**
- * Returns a filed writer.
- */
- public FieldWriter getFieldWriter() { return fields; }
-
- /**
- * Returns a method writer.
- */
- public MethodWriter getMethodWriter() { return methods; }
-
- /**
- * Ends writing and returns the contents of the class file.
- *
- * @param accessFlags access flags.
- * @param thisClass this class. an index indicating its <code>CONSTANT_Class_info</code>.
- * @param superClass super class. an index indicating its <code>CONSTANT_Class_info</code>.
- * @param interfaces implemented interfaces.
- * index numbers indicating their <code>ClassInfo</code>.
- * It may be null.
- * @param aw attributes of the class file. May be null.
- *
- * @see AccessFlag
- */
- public byte[] end(int accessFlags, int thisClass, int superClass,
- int[] interfaces, AttributeWriter aw) {
- constPool.end();
- output.writeShort(accessFlags);
- output.writeShort(thisClass);
- output.writeShort(superClass);
- if (interfaces == null)
- output.writeShort(0);
- else {
- int n = interfaces.length;
- output.writeShort(n);
- for (int i = 0; i < n; i++)
- output.writeShort(interfaces[i]);
- }
-
- output.enlarge(fields.dataSize() + methods.dataSize() + 6);
- try {
- output.writeShort(fields.size());
- fields.write(output);
-
- output.writeShort(methods.numOfMethods());
- methods.write(output);
- }
- catch (IOException e) {}
-
- writeAttribute(output, aw, 0);
- return output.toByteArray();
- }
-
- /**
- * Ends writing and writes the contents of the class file into the
- * given output stream.
- *
- * @param accessFlags access flags.
- * @param thisClass this class. an index indicating its <code>CONSTANT_Class_info</code>.
- * @param superClass super class. an index indicating its <code>CONSTANT_Class_info</code>.
- * @param interfaces implemented interfaces.
- * index numbers indicating their <code>CONSTATNT_Class_info</code>.
- * It may be null.
- * @param aw attributes of the class file. May be null.
- *
- * @see AccessFlag
- */
- public void end(DataOutputStream out,
- int accessFlags, int thisClass, int superClass,
- int[] interfaces, AttributeWriter aw)
- throws IOException
- {
- constPool.end();
- output.writeTo(out);
- out.writeShort(accessFlags);
- out.writeShort(thisClass);
- out.writeShort(superClass);
- if (interfaces == null)
- out.writeShort(0);
- else {
- int n = interfaces.length;
- out.writeShort(n);
- for (int i = 0; i < n; i++)
- out.writeShort(interfaces[i]);
- }
-
- out.writeShort(fields.size());
- fields.write(out);
-
- out.writeShort(methods.numOfMethods());
- methods.write(out);
- if (aw == null)
- out.writeShort(0);
- else {
- out.writeShort(aw.size());
- aw.write(out);
- }
- }
-
- /**
- * This writes attributes.
- *
- * <p>For example, the following object writes a synthetic attribute:
- *
- * <pre>
- * ConstPoolWriter cpw = ...;
- * final int tag = cpw.addUtf8Info("Synthetic");
- * AttributeWriter aw = new AttributeWriter() {
- * public int size() {
- * return 1;
- * }
- * public void write(DataOutputStream out) throws java.io.IOException {
- * out.writeShort(tag);
- * out.writeInt(0);
- * }
- * };
- * </pre>
- */
- public static interface AttributeWriter {
- /**
- * Returns the number of attributes that this writer will
- * write.
- */
- public int size();
-
- /**
- * Writes all the contents of the attributes. The binary representation
- * of the contents is an array of <code>attribute_info</code>.
- */
- public void write(DataOutputStream out) throws IOException;
- }
-
- static void writeAttribute(ByteStream bs, AttributeWriter aw, int attrCount) {
- if (aw == null) {
- bs.writeShort(attrCount);
- return;
- }
-
- bs.writeShort(aw.size() + attrCount);
- DataOutputStream dos = new DataOutputStream(bs);
- try {
- aw.write(dos);
- dos.flush();
- }
- catch (IOException e) {}
- }
-
- /**
- * Field.
- */
- public static final class FieldWriter {
- protected ByteStream output;
- protected ConstPoolWriter constPool;
- private int fieldCount;
-
- FieldWriter(ConstPoolWriter cp) {
- output = new ByteStream(128);
- constPool = cp;
- fieldCount = 0;
- }
-
- /**
- * Adds a new field.
- *
- * @param accessFlags access flags.
- * @param name the field name.
- * @param descriptor the field type.
- * @param aw the attributes of the field. may be null.
- * @see AccessFlag
- */
- public void add(int accessFlags, String name, String descriptor, AttributeWriter aw) {
- int nameIndex = constPool.addUtf8Info(name);
- int descIndex = constPool.addUtf8Info(descriptor);
- add(accessFlags, nameIndex, descIndex, aw);
- }
-
- /**
- * Adds a new field.
- *
- * @param accessFlags access flags.
- * @param name the field name. an index indicating its <code>CONSTANT_Utf8_info</code>.
- * @param descriptor the field type. an index indicating its <code>CONSTANT_Utf8_info</code>.
- * @param aw the attributes of the field. may be null.
- * @see AccessFlag
- */
- public void add(int accessFlags, int name, int descriptor, AttributeWriter aw) {
- ++fieldCount;
- output.writeShort(accessFlags);
- output.writeShort(name);
- output.writeShort(descriptor);
- writeAttribute(output, aw, 0);
- }
-
- int size() { return fieldCount; }
-
- int dataSize() { return output.size(); }
-
- /**
- * Writes the added fields.
- */
- void write(OutputStream out) throws IOException {
- output.writeTo(out);
- }
- }
-
- /**
- * Method.
- */
- public static final class MethodWriter {
- protected ByteStream output;
- protected ConstPoolWriter constPool;
- private int methodCount;
- protected int codeIndex;
- protected int throwsIndex;
- protected int stackIndex;
-
- private int startPos;
- private boolean isAbstract;
- private int catchPos;
- private int catchCount;
-
- MethodWriter(ConstPoolWriter cp) {
- output = new ByteStream(256);
- constPool = cp;
- methodCount = 0;
- codeIndex = 0;
- throwsIndex = 0;
- stackIndex = 0;
- }
-
- /**
- * Starts Adding a new method.
- *
- * @param accessFlags access flags.
- * @param name the method name.
- * @param descriptor the method signature.
- * @param exceptions throws clause. It may be null.
- * The class names must be the JVM-internal
- * representations like <code>java/lang/Exception</code>.
- * @param aw attributes to the <code>Method_info</code>.
- */
- public void begin(int accessFlags, String name, String descriptor,
- String[] exceptions, AttributeWriter aw) {
- int nameIndex = constPool.addUtf8Info(name);
- int descIndex = constPool.addUtf8Info(descriptor);
- int[] intfs;
- if (exceptions == null)
- intfs = null;
- else
- intfs = constPool.addClassInfo(exceptions);
-
- begin(accessFlags, nameIndex, descIndex, intfs, aw);
- }
-
- /**
- * Starts adding a new method.
- *
- * @param accessFlags access flags.
- * @param name the method name. an index indicating its <code>CONSTANT_Utf8_info</code>.
- * @param descriptor the field type. an index indicating its <code>CONSTANT_Utf8_info</code>.
- * @param exceptions throws clause. indexes indicating <code>CONSTANT_Class_info</code>s.
- * It may be null.
- * @param aw attributes to the <code>Method_info</code>.
- */
- public void begin(int accessFlags, int name, int descriptor, int[] exceptions, AttributeWriter aw) {
- ++methodCount;
- output.writeShort(accessFlags);
- output.writeShort(name);
- output.writeShort(descriptor);
- isAbstract = (accessFlags & AccessFlag.ABSTRACT) != 0;
-
- int attrCount = isAbstract ? 0 : 1;
- if (exceptions != null)
- ++attrCount;
-
- writeAttribute(output, aw, attrCount);
-
- if (exceptions != null)
- writeThrows(exceptions);
-
- if (!isAbstract) {
- if (codeIndex == 0)
- codeIndex = constPool.addUtf8Info(CodeAttribute.tag);
-
- startPos = output.getPos();
- output.writeShort(codeIndex);
- output.writeBlank(12); // attribute_length, maxStack, maxLocals, code_lenth
- }
-
- catchPos = -1;
- catchCount = 0;
- }
-
- private void writeThrows(int[] exceptions) {
- if (throwsIndex == 0)
- throwsIndex = constPool.addUtf8Info(ExceptionsAttribute.tag);
-
- output.writeShort(throwsIndex);
- output.writeInt(exceptions.length * 2 + 2);
- output.writeShort(exceptions.length);
- for (int i = 0; i < exceptions.length; i++)
- output.writeShort(exceptions[i]);
- }
-
- /**
- * Appends an 8bit value of bytecode.
- *
- * @see Opcode
- */
- public void add(int b) {
- output.write(b);
- }
-
- /**
- * Appends a 16bit value of bytecode.
- */
- public void add16(int b) {
- output.writeShort(b);
- }
-
- /**
- * Appends a 32bit value of bytecode.
- */
- public void add32(int b) {
- output.writeInt(b);
- }
-
- /**
- * Appends a invokevirtual, inovkespecial, or invokestatic bytecode.
- *
- * @see Opcode
- */
- public void addInvoke(int opcode, String targetClass, String methodName,
- String descriptor) {
- int target = constPool.addClassInfo(targetClass);
- int nt = constPool.addNameAndTypeInfo(methodName, descriptor);
- int method = constPool.addMethodrefInfo(target, nt);
- add(opcode);
- add16(method);
- }
-
- /**
- * Ends appending bytecode.
- */
- public void codeEnd(int maxStack, int maxLocals) {
- if (!isAbstract) {
- output.writeShort(startPos + 6, maxStack);
- output.writeShort(startPos + 8, maxLocals);
- output.writeInt(startPos + 10, output.getPos() - startPos - 14); // code_length
- catchPos = output.getPos();
- catchCount = 0;
- output.writeShort(0); // number of catch clauses
- }
- }
-
- /**
- * Appends an <code>exception_table</code> entry to the
- * <code>Code_attribute</code>. This method is available
- * only after the <code>codeEnd</code> method is called.
- *
- * @param catchType an index indicating a <code>CONSTANT_Class_info</code>.
- */
- public void addCatch(int startPc, int endPc, int handlerPc, int catchType) {
- ++catchCount;
- output.writeShort(startPc);
- output.writeShort(endPc);
- output.writeShort(handlerPc);
- output.writeShort(catchType);
- }
-
- /**
- * Ends adding a new method. The <code>add</code> method must be
- * called before the <code>end</code> method is called.
- *
- * @param smap a stack map table. may be null.
- * @param aw attributes to the <code>Code_attribute</code>.
- * may be null.
- */
- public void end(StackMapTable.Writer smap, AttributeWriter aw) {
- if (isAbstract)
- return;
-
- // exception_table_length
- output.writeShort(catchPos, catchCount);
-
- int attrCount = smap == null ? 0 : 1;
- writeAttribute(output, aw, attrCount);
-
- if (smap != null) {
- if (stackIndex == 0)
- stackIndex = constPool.addUtf8Info(StackMapTable.tag);
-
- output.writeShort(stackIndex);
- byte[] data = smap.toByteArray();
- output.writeInt(data.length);
- output.write(data);
- }
-
- // Code attribute_length
- output.writeInt(startPos + 2, output.getPos() - startPos - 6);
- }
-
- /**
- * Returns the length of the bytecode that has been added so far.
- *
- * @return the length in bytes.
- * @since 3.19
- */
- public int size() { return output.getPos() - startPos - 14; }
-
- int numOfMethods() { return methodCount; }
-
- int dataSize() { return output.size(); }
-
- /**
- * Writes the added methods.
- */
- void write(OutputStream out) throws IOException {
- output.writeTo(out);
- }
- }
-
- /**
- * Constant Pool.
- */
- public static final class ConstPoolWriter {
- ByteStream output;
- protected int startPos;
- protected int num;
-
- ConstPoolWriter(ByteStream out) {
- output = out;
- startPos = out.getPos();
- num = 1;
- output.writeShort(1); // number of entries
- }
-
- /**
- * Makes <code>CONSTANT_Class_info</code> objects for each class name.
- *
- * @return an array of indexes indicating <code>CONSTANT_Class_info</code>s.
- */
- public int[] addClassInfo(String[] classNames) {
- int n = classNames.length;
- int[] result = new int[n];
- for (int i = 0; i < n; i++)
- result[i] = addClassInfo(classNames[i]);
-
- return result;
- }
-
- /**
- * Adds a new <code>CONSTANT_Class_info</code> structure.
- *
- * <p>This also adds a <code>CONSTANT_Utf8_info</code> structure
- * for storing the class name.
- *
- * @param jvmname the JVM-internal representation of a class name.
- * e.g. <code>java/lang/Object</code>.
- * @return the index of the added entry.
- */
- public int addClassInfo(String jvmname) {
- int utf8 = addUtf8Info(jvmname);
- output.write(ClassInfo.tag);
- output.writeShort(utf8);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_Class_info</code> structure.
- *
- * @param name <code>name_index</code>
- * @return the index of the added entry.
- */
- public int addClassInfo(int name) {
- output.write(ClassInfo.tag);
- output.writeShort(name);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_NameAndType_info</code> structure.
- *
- * @param name <code>name_index</code>
- * @param type <code>descriptor_index</code>
- * @return the index of the added entry.
- */
- public int addNameAndTypeInfo(String name, String type) {
- return addNameAndTypeInfo(addUtf8Info(name), addUtf8Info(type));
- }
-
- /**
- * Adds a new <code>CONSTANT_NameAndType_info</code> structure.
- *
- * @param name <code>name_index</code>
- * @param type <code>descriptor_index</code>
- * @return the index of the added entry.
- */
- public int addNameAndTypeInfo(int name, int type) {
- output.write(NameAndTypeInfo.tag);
- output.writeShort(name);
- output.writeShort(type);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_Fieldref_info</code> structure.
- *
- * @param classInfo <code>class_index</code>
- * @param nameAndTypeInfo <code>name_and_type_index</code>.
- * @return the index of the added entry.
- */
- public int addFieldrefInfo(int classInfo, int nameAndTypeInfo) {
- output.write(FieldrefInfo.tag);
- output.writeShort(classInfo);
- output.writeShort(nameAndTypeInfo);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_Methodref_info</code> structure.
- *
- * @param classInfo <code>class_index</code>
- * @param nameAndTypeInfo <code>name_and_type_index</code>.
- * @return the index of the added entry.
- */
- public int addMethodrefInfo(int classInfo, int nameAndTypeInfo) {
- output.write(MethodrefInfo.tag);
- output.writeShort(classInfo);
- output.writeShort(nameAndTypeInfo);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_InterfaceMethodref_info</code>
- * structure.
- *
- * @param classInfo <code>class_index</code>
- * @param nameAndTypeInfo <code>name_and_type_index</code>.
- * @return the index of the added entry.
- */
- public int addInterfaceMethodrefInfo(int classInfo,
- int nameAndTypeInfo) {
- output.write(InterfaceMethodrefInfo.tag);
- output.writeShort(classInfo);
- output.writeShort(nameAndTypeInfo);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_MethodHandle_info</code>
- * structure.
- *
- * @param kind <code>reference_kind</code>
- * such as {@link ConstPool#REF_invokeStatic <code>REF_invokeStatic</code>}.
- * @param index <code>reference_index</code>.
- * @return the index of the added entry.
- *
- * @since 3.17.1
- */
- public int addMethodHandleInfo(int kind, int index) {
- output.write(MethodHandleInfo.tag);
- output.write(kind);
- output.writeShort(index);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_MethodType_info</code>
- * structure.
- *
- * @param desc <code>descriptor_index</code>.
- * @return the index of the added entry.
- *
- * @since 3.17.1
- */
- public int addMethodTypeInfo(int desc) {
- output.write(MethodTypeInfo.tag);
- output.writeShort(desc);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_InvokeDynamic_info</code>
- * structure.
- *
- * @param bootstrap <code>bootstrap_method_attr_index</code>.
- * @param nameAndTypeInfo <code>name_and_type_index</code>.
- * @return the index of the added entry.
- *
- * @since 3.17.1
- */
- public int addInvokeDynamicInfo(int bootstrap,
- int nameAndTypeInfo) {
- output.write(InvokeDynamicInfo.tag);
- output.writeShort(bootstrap);
- output.writeShort(nameAndTypeInfo);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_Dynamic_info</code>
- * structure.
- *
- * @param bootstrap <code>bootstrap_method_attr_index</code>.
- * @param nameAndTypeInfo <code>name_and_type_index</code>.
- * @return the index of the added entry.
- *
- * @since 3.26
- */
- public int addDynamicInfo(int bootstrap,
- int nameAndTypeInfo) {
- output.write(DynamicInfo.tag);
- output.writeShort(bootstrap);
- output.writeShort(nameAndTypeInfo);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_String_info</code>
- * structure.
- *
- * <p>This also adds a new <code>CONSTANT_Utf8_info</code>
- * structure.
- *
- * @return the index of the added entry.
- */
- public int addStringInfo(String str) {
- int utf8 = addUtf8Info(str);
- output.write(StringInfo.tag);
- output.writeShort(utf8);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_Integer_info</code>
- * structure.
- *
- * @return the index of the added entry.
- */
- public int addIntegerInfo(int i) {
- output.write(IntegerInfo.tag);
- output.writeInt(i);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_Float_info</code>
- * structure.
- *
- * @return the index of the added entry.
- */
- public int addFloatInfo(float f) {
- output.write(FloatInfo.tag);
- output.writeFloat(f);
- return num++;
- }
-
- /**
- * Adds a new <code>CONSTANT_Long_info</code>
- * structure.
- *
- * @return the index of the added entry.
- */
- public int addLongInfo(long l) {
- output.write(LongInfo.tag);
- output.writeLong(l);
- int n = num;
- num += 2;
- return n;
- }
-
- /**
- * Adds a new <code>CONSTANT_Double_info</code>
- * structure.
- *
- * @return the index of the added entry.
- */
- public int addDoubleInfo(double d) {
- output.write(DoubleInfo.tag);
- output.writeDouble(d);
- int n = num;
- num += 2;
- return n;
- }
-
- /**
- * Adds a new <code>CONSTANT_Utf8_info</code>
- * structure.
- *
- * @return the index of the added entry.
- */
- public int addUtf8Info(String utf8) {
- output.write(Utf8Info.tag);
- output.writeUTF(utf8);
- return num++;
- }
-
- /**
- * Writes the contents of this class pool.
- */
- void end() {
- output.writeShort(startPos, num);
- }
- }
- }
|