- /*
- * 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;
-
- import javassist.bytecode.*;
- import javassist.convert.*;
-
- /**
- * Simple translator of method bodies
- * (also see the <code>javassist.expr</code> package).
- *
- * <p>Instances of this class specifies how to instrument of the
- * bytecodes representing a method body. They are passed to
- * <code>CtClass.instrument()</code> or
- * <code>CtMethod.instrument()</code> as a parameter.
- *
- * <p>Example:
- * <ul><pre>
- * ClassPool cp = ClassPool.getDefault();
- * CtClass point = cp.get("Point");
- * CtClass singleton = cp.get("Singleton");
- * CtClass client = cp.get("Client");
- * CodeConverter conv = new CodeConverter();
- * conv.replaceNew(point, singleton, "makePoint");
- * client.instrument(conv);
- * </pre></ul>
- *
- * <p>This program substitutes "<code>Singleton.makePoint()</code>"
- * for all occurrences of "<code>new Point()</code>"
- * appearing in methods declared in a <code>Client</code> class.
- *
- * @see javassist.CtClass#instrument(CodeConverter)
- * @see javassist.CtMethod#instrument(CodeConverter)
- * @see javassist.expr.ExprEditor
- */
- public class CodeConverter {
- protected Transformer transformers = null;
-
- /**
- * Modify a method body so that instantiation of the specified class
- * is replaced with a call to the specified static method. For example,
- * <code>replaceNew(ctPoint, ctSingleton, "createPoint")</code>
- * (where <code>ctPoint</code> and <code>ctSingleton</code> are
- * compile-time classes for class <code>Point</code> and class
- * <code>Singleton</code>, respectively)
- * replaces all occurrences of:
- *
- * <ul><code>new Point(x, y)</code></ul>
- *
- * in the method body with:
- *
- * <ul><code>Singleton.createPoint(x, y)</code></ul>
- *
- * <p>This enables to intercept instantiation of <code>Point</code>
- * and change the samentics. For example, the following
- * <code>createPoint()</code> implements the singleton pattern:
- *
- * <ul><pre>public static Point createPoint(int x, int y) {
- * if (aPoint == null)
- * aPoint = new Point(x, y);
- * return aPoint;
- * }
- * </pre></ul>
- *
- * <p>The static method call substituted for the original <code>new</code>
- * expression must be
- * able to receive the same set of parameters as the original
- * constructor. If there are multiple constructors with different
- * parameter types, then there must be multiple static methods
- * with the same name but different parameter types.
- *
- * <p>The return type of the substituted static method must be
- * the exactly same as the type of the instantiated class specified by
- * <code>newClass</code>.
- *
- * @param newClass the instantiated class.
- * @param calledClass the class in which the static method is
- * declared.
- * @param calledMethod the name of the static method.
- */
- public void replaceNew(CtClass newClass,
- CtClass calledClass, String calledMethod) {
- transformers = new TransformNew(transformers, newClass.getName(),
- calledClass.getName(), calledMethod);
- }
-
- /**
- * Modify a method body so that instantiation of the class
- * specified by <code>oldClass</code>
- * is replaced with instantiation of another class <code>newClass</code>.
- * For example,
- * <code>replaceNew(ctPoint, ctPoint2)</code>
- * (where <code>ctPoint</code> and <code>ctPoint2</code> are
- * compile-time classes for class <code>Point</code> and class
- * <code>Point2</code>, respectively)
- * replaces all occurrences of:
- *
- * <ul><code>new Point(x, y)</code></ul>
- *
- * in the method body with:
- *
- * <ul><code>new Point2(x, y)</code></ul>
- *
- * <p>Note that <code>Point2</code> must be type-compatible with <code>Point</code>.
- * It must have the same set of methods, fields, and constructors as the
- * replaced class.
- */
- public void replaceNew(CtClass oldClass, CtClass newClass) {
- transformers = new TransformNewClass(transformers, oldClass.getName(),
- newClass.getName());
- }
-
- /**
- * Modify a method body so that field read/write expressions access
- * a different field from the original one.
- *
- * <p>Note that this method changes only the filed name and the class
- * declaring the field; the type of the target object does not change.
- * Therefore, the substituted field must be declared in the same class
- * or a superclass of the original class.
- *
- * <p>Also, <code>clazz</code> and <code>newClass</code> must specify
- * the class directly declaring the field. They must not specify
- * a subclass of that class.
- *
- * @param field the originally accessed field.
- * @param newClass the class declaring the substituted field.
- * @param newFieldname the name of the substituted field.
- */
- public void redirectFieldAccess(CtField field,
- CtClass newClass, String newFieldname) {
- transformers = new TransformFieldAccess(transformers, field,
- newClass.getName(),
- newFieldname);
- }
-
- /**
- * Modify a method body so that an expression reading the specified
- * field is replaced with a call to the specified <i>static</i> method.
- * This static method receives the target object of the original
- * read expression as a parameter. It must return a value of
- * the same type as the field.
- *
- * <p>For example, the program below
- *
- * <ul><pre>Point p = new Point();
- * int newX = p.x + 3;</pre></ul>
- *
- * <p>can be translated into:
- *
- * <ul><pre>Point p = new Point();
- * int newX = Accessor.readX(p) + 3;</pre></ul>
- *
- * <p>where
- *
- * <ul><pre>public class Accessor {
- * public static int readX(Object target) { ... }
- * }</pre></ul>
- *
- * <p>The type of the parameter of <code>readX()</code> must
- * be <code>java.lang.Object</code> independently of the actual
- * type of <code>target</code>. The return type must be the same
- * as the field type.
- *
- * @param field the field.
- * @param calledClass the class in which the static method is
- * declared.
- * @param calledMethod the name of the static method.
- */
- public void replaceFieldRead(CtField field,
- CtClass calledClass, String calledMethod) {
- transformers = new TransformReadField(transformers, field,
- calledClass.getName(),
- calledMethod);
- }
-
- /**
- * Modify a method body so that an expression writing the specified
- * field is replaced with a call to the specified static method.
- * This static method receives two parameters: the target object of
- * the original
- * write expression and the assigned value. The return type of the
- * static method is <code>void</code>.
- *
- * <p>For example, the program below
- *
- * <ul><pre>Point p = new Point();
- * p.x = 3;</pre></ul>
- *
- * <p>can be translated into:
- *
- * <ul><pre>Point p = new Point();
- * Accessor.writeX(3);</pre></ul>
- *
- * <p>where
- *
- * <ul><pre>public class Accessor {
- * public static void writeX(Object target, int value) { ... }
- * }</pre></ul>
- *
- * <p>The type of the first parameter of <code>writeX()</code> must
- * be <code>java.lang.Object</code> independently of the actual
- * type of <code>target</code>. The type of the second parameter
- * is the same as the field type.
- *
- * @param field the field.
- * @param calledClass the class in which the static method is
- * declared.
- * @param calledMethod the name of the static method.
- */
- public void replaceFieldWrite(CtField field,
- CtClass calledClass, String calledMethod) {
- transformers = new TransformWriteField(transformers, field,
- calledClass.getName(),
- calledMethod);
- }
-
- /**
- * Modify a method body, so that ALL accesses to an array are replaced with
- * calls to static methods within another class. In the case of reading an
- * element from the array, this is replaced with a call to a static method with
- * the array and the index as arguments, the return value is the value read from
- * the array. If writing to an array, this is replaced with a call to a static
- * method with the array, index and new value as parameters, the return value of
- * the static method is <code>void</code>.
- *
- * <p>The <code>calledClass</code> parameter is the class containing the static methods to be used
- * for array replacement. The <code>names</code> parameter points to an implementation of
- * <code>ArrayAccessReplacementMethodNames</code> which specifies the names of the method to be
- * used for access for each type of array. For example reading from an <code>int[]</code> will
- * require a different method than if writing to an <code>int[]</code>, and writing to a <code>long[]</code>
- * will require a different method than if writing to a <code>byte[]</code>. If the implementation
- * of <code>ArrayAccessReplacementMethodNames</code> does not contain the name for access for a
- * type of array, that access is not replaced.
- *
- * <p>A default implementation of <code>ArrayAccessReplacementMethodNames</code> called
- * <code>DefaultArrayAccessReplacementMethodNames</code> has been provided and is what is used in the
- * following example. This also assumes that <code>'foo.ArrayAdvisor'</code> is the name of the
- * <code>CtClass</code> passed in.
- *
- * <p>If we have the following class:
- * <pre>class POJO{
- * int[] ints = new int[]{1, 2, 3, 4, 5};
- * long[] longs = new int[]{10, 20, 30};
- * Object objects = new Object[]{true, false};
- * Integer[] integers = new Integer[]{new Integer(10)};
- * }
- * </pre>
- * and this is accessed as:
- * <pre>POJO p = new POJO();
- *
- * //Write to int array
- * p.ints[2] = 7;
- *
- * //Read from int array
- * int i = p.ints[2];
- *
- * //Write to long array
- * p.longs[2] = 1000L;
- *
- * //Read from long array
- * long l = p.longs[2];
- *
- * //Write to Object array
- * p.objects[2] = "Hello";
- *
- * //Read from Object array
- * Object o = p.objects[2];
- *
- * //Write to Integer array
- * Integer integer = new Integer(5);
- * p.integers[0] = integer;
- *
- * //Read from Object array
- * integer = p.integers[0];
- * </pre>
- *
- * Following instrumentation we will have
- * <pre>POJO p = new POJO();
- *
- * //Write to int array
- * ArrayAdvisor.arrayWriteInt(p.ints, 2, 7);
- *
- * //Read from int array
- * int i = ArrayAdvisor.arrayReadInt(p.ints, 2);
- *
- * //Write to long array
- * ArrayAdvisor.arrayWriteLong(p.longs, 2, 1000L);
- *
- * //Read from long array
- * long l = ArrayAdvisor.arrayReadLong(p.longs, 2);
- *
- * //Write to Object array
- * ArrayAdvisor.arrayWriteObject(p.objects, 2, "Hello");
- *
- * //Read from Object array
- * Object o = ArrayAdvisor.arrayReadObject(p.objects, 2);
- *
- * //Write to Integer array
- * Integer integer = new Integer(5);
- * ArrayAdvisor.arrayWriteObject(p.integers, 0, integer);
- *
- * //Read from Object array
- * integer = ArrayAdvisor.arrayWriteObject(p.integers, 0);
- * </pre>
- *
- * @see DefaultArrayAccessReplacementMethodNames
- *
- * @param calledClass the class containing the static methods.
- * @param names contains the names of the methods to replace
- * the different kinds of array access with.
- */
- public void replaceArrayAccess(CtClass calledClass, ArrayAccessReplacementMethodNames names)
- throws NotFoundException
- {
- transformers = new TransformAccessArrayField(transformers, calledClass.getName(), names);
- }
-
- /**
- * Modify method invocations in a method body so that a different
- * method will be invoked.
- *
- * <p>Note that the target object, the parameters, or
- * the type of invocation
- * (static method call, interface call, or private method call)
- * are not modified. Only the method name is changed. The substituted
- * method must have the same signature that the original one has.
- * If the original method is a static method, the substituted method
- * must be static.
- *
- * @param origMethod original method
- * @param substMethod substituted method
- */
- public void redirectMethodCall(CtMethod origMethod,
- CtMethod substMethod)
- throws CannotCompileException
- {
- String d1 = origMethod.getMethodInfo2().getDescriptor();
- String d2 = substMethod.getMethodInfo2().getDescriptor();
- if (!d1.equals(d2))
- throw new CannotCompileException("signature mismatch: "
- + substMethod.getLongName());
-
- int mod1 = origMethod.getModifiers();
- int mod2 = substMethod.getModifiers();
- if (Modifier.isStatic(mod1) != Modifier.isStatic(mod2)
- || (Modifier.isPrivate(mod1) && !Modifier.isPrivate(mod2))
- || origMethod.getDeclaringClass().isInterface()
- != substMethod.getDeclaringClass().isInterface())
- throw new CannotCompileException("invoke-type mismatch "
- + substMethod.getLongName());
-
- transformers = new TransformCall(transformers, origMethod,
- substMethod);
- }
-
- /**
- * Correct invocations to a method that has been renamed.
- * If a method is renamed, calls to that method must be also
- * modified so that the method with the new name will be called.
- *
- * <p>The method must be declared in the same class before and
- * after it is renamed.
- *
- * <p>Note that the target object, the parameters, or
- * the type of invocation
- * (static method call, interface call, or private method call)
- * are not modified. Only the method name is changed.
- *
- * @param oldMethodName the old name of the method.
- * @param newMethod the method with the new name.
- * @see javassist.CtMethod#setName(String)
- */
- public void redirectMethodCall(String oldMethodName,
- CtMethod newMethod)
- throws CannotCompileException
- {
- transformers
- = new TransformCall(transformers, oldMethodName, newMethod);
- }
-
- /**
- * Insert a call to another method before an existing method call.
- * That "before" method must be static. The return type must be
- * <code>void</code>. As parameters, the before method receives
- * the target object and all the parameters to the originally invoked
- * method. For example, if the originally invoked method is
- * <code>move()</code>:
- *
- * <ul><pre>class Point {
- * Point move(int x, int y) { ... }
- * }</pre></ul>
- *
- * <p>Then the before method must be something like this:
- *
- * <ul><pre>class Verbose {
- * static void print(Point target, int x, int y) { ... }
- * }</pre></ul>
- *
- * <p>The <code>CodeConverter</code> would translate bytecode
- * equivalent to:
- *
- * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul>
- *
- * <p>into the bytecode equivalent to:
- *
- * <ul><pre>int tmp1 = x + y;
- * int tmp2 = 0;
- * Verbose.print(p, tmp1, tmp2);
- * Point p2 = p.move(tmp1, tmp2);</pre></ul>
- *
- * @param origMethod the method originally invoked.
- * @param beforeMethod the method invoked before
- * <code>origMethod</code>.
- */
- public void insertBeforeMethod(CtMethod origMethod,
- CtMethod beforeMethod)
- throws CannotCompileException
- {
- try {
- transformers = new TransformBefore(transformers, origMethod,
- beforeMethod);
- }
- catch (NotFoundException e) {
- throw new CannotCompileException(e);
- }
- }
-
- /**
- * Inserts a call to another method after an existing method call.
- * That "after" method must be static. The return type must be
- * <code>void</code>. As parameters, the after method receives
- * the target object and all the parameters to the originally invoked
- * method. For example, if the originally invoked method is
- * <code>move()</code>:
- *
- * <ul><pre>class Point {
- * Point move(int x, int y) { ... }
- * }</pre></ul>
- *
- * <p>Then the after method must be something like this:
- *
- * <ul><pre>class Verbose {
- * static void print(Point target, int x, int y) { ... }
- * }</pre></ul>
- *
- * <p>The <code>CodeConverter</code> would translate bytecode
- * equivalent to:
- *
- * <ul><pre>Point p2 = p.move(x + y, 0);</pre></ul>
- *
- * <p>into the bytecode equivalent to:
- *
- * <ul><pre>int tmp1 = x + y;
- * int tmp2 = 0;
- * Point p2 = p.move(tmp1, tmp2);
- * Verbose.print(p, tmp1, tmp2);</pre></ul>
- *
- * @param origMethod the method originally invoked.
- * @param afterMethod the method invoked after
- * <code>origMethod</code>.
- */
- public void insertAfterMethod(CtMethod origMethod,
- CtMethod afterMethod)
- throws CannotCompileException
- {
- try {
- transformers = new TransformAfter(transformers, origMethod,
- afterMethod);
- }
- catch (NotFoundException e) {
- throw new CannotCompileException(e);
- }
- }
-
- /**
- * Performs code conversion.
- */
- protected void doit(CtClass clazz, MethodInfo minfo, ConstPool cp)
- throws CannotCompileException
- {
- Transformer t;
- CodeAttribute codeAttr = minfo.getCodeAttribute();
- if (codeAttr == null || transformers == null)
- return;
- for (t = transformers; t != null; t = t.getNext())
- t.initialize(cp, clazz, minfo);
-
- CodeIterator iterator = codeAttr.iterator();
- while (iterator.hasNext()) {
- try {
- int pos = iterator.next();
- for (t = transformers; t != null; t = t.getNext())
- pos = t.transform(clazz, pos, iterator, cp);
- }
- catch (BadBytecode e) {
- throw new CannotCompileException(e);
- }
- }
-
- int locals = 0;
- int stack = 0;
- for (t = transformers; t != null; t = t.getNext()) {
- int s = t.extraLocals();
- if (s > locals)
- locals = s;
-
- s = t.extraStack();
- if (s > stack)
- stack = s;
- }
-
- for (t = transformers; t != null; t = t.getNext())
- t.clean();
-
- if (locals > 0)
- codeAttr.setMaxLocals(codeAttr.getMaxLocals() + locals);
-
- if (stack > 0)
- codeAttr.setMaxStack(codeAttr.getMaxStack() + stack);
-
- try {
- minfo.rebuildStackMapIf6(clazz.getClassPool(),
- clazz.getClassFile2());
- }
- catch (BadBytecode b) {
- throw new CannotCompileException(b.getMessage(), b);
- }
- }
-
- /**
- * Interface containing the method names to be used
- * as array access replacements.
- *
- * @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
- * @version $Revision: 1.16 $
- */
- public interface ArrayAccessReplacementMethodNames
- {
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;I)B</code> to replace reading from a byte[].
- */
- String byteOrBooleanRead();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;IB)V</code> to replace writing to a byte[].
- */
- String byteOrBooleanWrite();
-
- /**
- * @return the name of a static method with the signature
- * <code>(Ljava/lang/Object;I)C</code> to replace reading from a char[].
- */
- String charRead();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;IC)V</code> to replace writing to a byte[].
- */
- String charWrite();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;I)D</code> to replace reading from a double[].
- */
- String doubleRead();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;ID)V</code> to replace writing to a double[].
- */
- String doubleWrite();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;I)F</code> to replace reading from a float[].
- */
- String floatRead();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;IF)V</code> to replace writing to a float[].
- */
- String floatWrite();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;I)I</code> to replace reading from a int[].
- */
- String intRead();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;II)V</code> to replace writing to a int[].
- */
- String intWrite();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;I)J</code> to replace reading from a long[].
- */
- String longRead();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;IJ)V</code> to replace writing to a long[].
- */
- String longWrite();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;I)Ljava/lang/Object;</code>
- * to replace reading from a Object[] (or any subclass of object).
- */
- String objectRead();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;ILjava/lang/Object;)V</code>
- * to replace writing to a Object[] (or any subclass of object).
- */
- String objectWrite();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;I)S</code> to replace reading from a short[].
- */
- String shortRead();
-
- /**
- * Returns the name of a static method with the signature
- * <code>(Ljava/lang/Object;IS)V</code> to replace writing to a short[].
- */
- String shortWrite();
- }
-
- /**
- * Default implementation of the <code>ArrayAccessReplacementMethodNames</code>
- * interface giving default values for method names to be used for replacing
- * accesses to array elements.
- *
- * @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
- * @version $Revision: 1.16 $
- */
- public static class DefaultArrayAccessReplacementMethodNames
- implements ArrayAccessReplacementMethodNames
- {
- /**
- * Returns "arrayReadByteOrBoolean" as the name of the static method with the signature
- * (Ljava/lang/Object;I)B to replace reading from a byte[].
- */
- public String byteOrBooleanRead()
- {
- return "arrayReadByteOrBoolean";
- }
-
- /**
- * Returns "arrayWriteByteOrBoolean" as the name of the static method with the signature
- * (Ljava/lang/Object;IB)V to replace writing to a byte[].
- */
- public String byteOrBooleanWrite()
- {
- return "arrayWriteByteOrBoolean";
- }
-
- /**
- * Returns "arrayReadChar" as the name of the static method with the signature
- * (Ljava/lang/Object;I)C to replace reading from a char[].
- */
- public String charRead()
- {
- return "arrayReadChar";
- }
-
- /**
- * Returns "arrayWriteChar" as the name of the static method with the signature
- * (Ljava/lang/Object;IC)V to replace writing to a byte[].
- */
- public String charWrite()
- {
- return "arrayWriteChar";
- }
-
- /**
- * Returns "arrayReadDouble" as the name of the static method with the signature
- * (Ljava/lang/Object;I)D to replace reading from a double[].
- */
- public String doubleRead()
- {
- return "arrayReadDouble";
- }
-
- /**
- * Returns "arrayWriteDouble" as the name of the static method with the signature
- * (Ljava/lang/Object;ID)V to replace writing to a double[].
- */
- public String doubleWrite()
- {
- return "arrayWriteDouble";
- }
-
- /**
- * Returns "arrayReadFloat" as the name of the static method with the signature
- * (Ljava/lang/Object;I)F to replace reading from a float[].
- */
- public String floatRead()
- {
- return "arrayReadFloat";
- }
-
- /**
- * Returns "arrayWriteFloat" as the name of the static method with the signature
- * (Ljava/lang/Object;IF)V to replace writing to a float[].
- */
- public String floatWrite()
- {
- return "arrayWriteFloat";
- }
-
- /**
- * Returns "arrayReadInt" as the name of the static method with the signature
- * (Ljava/lang/Object;I)I to replace reading from a int[].
- */
- public String intRead()
- {
- return "arrayReadInt";
- }
-
- /**
- * Returns "arrayWriteInt" as the name of the static method with the signature
- * (Ljava/lang/Object;II)V to replace writing to a int[].
- */
- public String intWrite()
- {
- return "arrayWriteInt";
- }
-
- /**
- * Returns "arrayReadLong" as the name of the static method with the signature
- * (Ljava/lang/Object;I)J to replace reading from a long[].
- */
- public String longRead()
- {
- return "arrayReadLong";
- }
-
- /**
- * Returns "arrayWriteLong" as the name of the static method with the signature
- * (Ljava/lang/Object;IJ)V to replace writing to a long[].
- */
- public String longWrite()
- {
- return "arrayWriteLong";
- }
-
- /**
- * Returns "arrayReadObject" as the name of the static method with the signature
- * (Ljava/lang/Object;I)Ljava/lang/Object; to replace reading from a Object[] (or any subclass of object).
- */
- public String objectRead()
- {
- return "arrayReadObject";
- }
-
- /**
- * Returns "arrayWriteObject" as the name of the static method with the signature
- * (Ljava/lang/Object;ILjava/lang/Object;)V to replace writing to a Object[] (or any subclass of object).
- */
- public String objectWrite()
- {
- return "arrayWriteObject";
- }
-
- /**
- * Returns "arrayReadShort" as the name of the static method with the signature
- * (Ljava/lang/Object;I)S to replace reading from a short[].
- */
- public String shortRead()
- {
- return "arrayReadShort";
- }
-
- /**
- * Returns "arrayWriteShort" as the name of the static method with the signature
- * (Ljava/lang/Object;IS)V to replace writing to a short[].
- */
- public String shortWrite()
- {
- return "arrayWriteShort";
- }
- }
- }
|