2006-11-22 17:45:24 +00:00
|
|
|
/*******************************************************************************
|
|
|
|
* Copyright (c) 2002,2006 IBM Corporation.
|
|
|
|
* All rights reserved. This program and the accompanying materials
|
|
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
|
|
* which accompanies this distribution, and is available at
|
|
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
*
|
|
|
|
* Contributors:
|
|
|
|
* IBM Corporation - initial API and implementation
|
|
|
|
*******************************************************************************/
|
|
|
|
package com.ibm.wala.shrikeCT;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This class formats and writes class data into JVM format.
|
|
|
|
*/
|
|
|
|
public final class ClassWriter implements ClassConstants {
|
|
|
|
// input
|
|
|
|
private int majorVersion = 46;
|
2007-06-05 01:17:18 +00:00
|
|
|
|
2006-11-22 17:45:24 +00:00
|
|
|
private int minorVersion = 0;
|
2007-06-05 01:17:18 +00:00
|
|
|
|
2006-11-22 17:45:24 +00:00
|
|
|
private ConstantPoolParser rawCP;
|
2007-06-05 01:17:18 +00:00
|
|
|
|
2006-11-22 17:45:24 +00:00
|
|
|
private HashMap<Object, Integer> cachedCPEntries = new HashMap<Object, Integer>(1);
|
2007-06-05 01:17:18 +00:00
|
|
|
final private ArrayList<Object> newCPEntries = new ArrayList<Object>(1);
|
|
|
|
|
2006-11-22 17:45:24 +00:00
|
|
|
private int nextCPIndex = 1;
|
2007-06-05 01:17:18 +00:00
|
|
|
|
2007-06-01 21:39:03 +00:00
|
|
|
final private ArrayList<Element> fields = new ArrayList<Element>(1);
|
2007-06-05 01:17:18 +00:00
|
|
|
final private ArrayList<Element> methods = new ArrayList<Element>(1);
|
|
|
|
|
2007-06-01 21:39:03 +00:00
|
|
|
final private ArrayList<Element> classAttributes = new ArrayList<Element>(1);
|
2007-06-05 01:17:18 +00:00
|
|
|
|
2006-11-22 17:45:24 +00:00
|
|
|
private int thisClass;
|
2007-06-05 01:17:18 +00:00
|
|
|
|
2006-11-22 17:45:24 +00:00
|
|
|
private int superClass;
|
2007-06-05 01:17:18 +00:00
|
|
|
|
2006-11-22 17:45:24 +00:00
|
|
|
private int[] superInterfaces;
|
2007-06-05 01:17:18 +00:00
|
|
|
|
2006-11-22 17:45:24 +00:00
|
|
|
private int accessFlags;
|
2007-06-05 01:17:18 +00:00
|
|
|
|
2006-11-22 17:45:24 +00:00
|
|
|
private boolean forceAddCPEntries = false;
|
|
|
|
|
|
|
|
// output
|
|
|
|
private byte[] buf;
|
2007-06-05 01:17:18 +00:00
|
|
|
|
2006-11-22 17:45:24 +00:00
|
|
|
private int bufLen;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a blank ClassWriter with no methods, fields, or attributes, an empty
|
|
|
|
* constant pool, no super class, no implemented interfaces, no name,
|
|
|
|
* majorVersion 46, and minorVersion 0.
|
|
|
|
*/
|
|
|
|
public ClassWriter() {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the class file format major version. You probably don't want to use
|
|
|
|
* this unless you really know what you are doing.
|
|
|
|
*/
|
|
|
|
public void setMajorVersion(int major) {
|
|
|
|
if (major < 0 || major > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Major version out of range: " + major);
|
|
|
|
}
|
|
|
|
majorVersion = major;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the class file format minor version. You probably don't want to use
|
|
|
|
* this unless you really know what you are doing.
|
|
|
|
*/
|
|
|
|
public void setMinorVersion(int minor) {
|
|
|
|
if (minor < 0 || minor > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Major version out of range: " + minor);
|
|
|
|
}
|
|
|
|
minorVersion = minor;
|
|
|
|
}
|
|
|
|
|
|
|
|
static abstract class CWItem {
|
|
|
|
abstract byte getType();
|
|
|
|
}
|
|
|
|
|
|
|
|
static class CWString extends CWItem {
|
2007-06-05 01:17:18 +00:00
|
|
|
final private String s;
|
2006-11-22 17:45:24 +00:00
|
|
|
|
|
|
|
CWString(String s) {
|
|
|
|
this.s = s;
|
|
|
|
}
|
2007-06-05 01:17:18 +00:00
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
public boolean equals(Object o) {
|
|
|
|
return o instanceof CWString && ((CWString) o).s.equals(s);
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
public int hashCode() {
|
|
|
|
return s.hashCode() + 3901;
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
byte getType() {
|
|
|
|
return CONSTANT_String;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static class CWClass extends CWItem {
|
2007-06-05 01:17:18 +00:00
|
|
|
final private String c;
|
2006-11-22 17:45:24 +00:00
|
|
|
|
|
|
|
CWClass(String c) {
|
|
|
|
this.c = c;
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
public boolean equals(Object o) {
|
|
|
|
return o instanceof CWClass && ((CWClass) o).c.equals(c);
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
public int hashCode() {
|
|
|
|
return c.hashCode() + 1431;
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
byte getType() {
|
|
|
|
return CONSTANT_Class;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static class CWRef extends CWItem {
|
2007-06-01 21:39:03 +00:00
|
|
|
final private String c;
|
2007-06-05 01:17:18 +00:00
|
|
|
|
|
|
|
final private String n;
|
|
|
|
|
|
|
|
final private String t;
|
|
|
|
|
|
|
|
final private byte type;
|
2006-11-22 17:45:24 +00:00
|
|
|
|
|
|
|
CWRef(byte type, String c, String n, String t) {
|
|
|
|
this.type = type;
|
|
|
|
this.c = c;
|
|
|
|
this.n = n;
|
|
|
|
this.t = t;
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
public boolean equals(Object o) {
|
|
|
|
if (o instanceof CWRef) {
|
|
|
|
CWRef r = (CWRef) o;
|
|
|
|
return r.type == type && r.c.equals(c) && r.n.equals(n) && r.t.equals(t);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
public int hashCode() {
|
|
|
|
return type + (c.hashCode() << 5) + (n.hashCode() << 3) + t.hashCode();
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
byte getType() {
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static class CWNAT extends CWItem {
|
2007-06-05 01:17:18 +00:00
|
|
|
final private String n;
|
|
|
|
|
|
|
|
final private String t;
|
2006-11-22 17:45:24 +00:00
|
|
|
|
|
|
|
CWNAT(String n, String t) {
|
|
|
|
this.n = n;
|
|
|
|
this.t = t;
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
public boolean equals(Object o) {
|
|
|
|
if (o instanceof CWNAT) {
|
|
|
|
CWNAT r = (CWNAT) o;
|
|
|
|
return r.n.equals(n) && r.t.equals(t);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
public int hashCode() {
|
|
|
|
return (n.hashCode() << 3) + t.hashCode();
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
byte getType() {
|
|
|
|
return CONSTANT_NameAndType;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copy a constant pool from some ClassReader into this class. This must be
|
|
|
|
* done before any entries are allocated in this ClassWriter's constant pool,
|
|
|
|
* and it can only be done once. If and only if this is done, it is safe to
|
|
|
|
* copy "raw" fields, methods and attributes from the ClassReader into this
|
|
|
|
* class, because the constant pool references in those fields, methods and
|
|
|
|
* attributes are guaranteed to point to the same constant pool items in this
|
|
|
|
* new class.
|
|
|
|
*
|
|
|
|
* @param cacheEntries
|
|
|
|
* records whether to parse the raw constant pool completely so that
|
|
|
|
* if new entries are required which are the same as entries already
|
|
|
|
* in the raw pool, the existing entries in the raw pool are used
|
|
|
|
* instead. Setting this to 'true' produces smaller constant pools
|
|
|
|
* but may slow down performance because the raw pool must be
|
|
|
|
* completely parsed
|
|
|
|
*/
|
2007-04-26 20:35:01 +00:00
|
|
|
public void setRawCP(ConstantPoolParser cp, boolean cacheEntries) throws InvalidClassFileException, IllegalArgumentException {
|
|
|
|
if (cp == null) {
|
|
|
|
throw new IllegalArgumentException();
|
|
|
|
}
|
2006-11-22 17:45:24 +00:00
|
|
|
if (rawCP != null) {
|
|
|
|
throw new IllegalArgumentException("Cannot set raw constant pool twice");
|
|
|
|
}
|
|
|
|
if (nextCPIndex != 1) {
|
|
|
|
throw new IllegalArgumentException("Cannot set raw constant pool after allocating new entries");
|
|
|
|
}
|
|
|
|
rawCP = cp;
|
|
|
|
nextCPIndex = cp.getItemCount();
|
|
|
|
|
|
|
|
if (cacheEntries) {
|
|
|
|
for (int i = 1; i < nextCPIndex; i++) {
|
|
|
|
byte t = cp.getItemType(i);
|
|
|
|
switch (t) {
|
|
|
|
case CONSTANT_String:
|
|
|
|
cachedCPEntries.put(new CWString(cp.getCPString(i)), new Integer(i));
|
|
|
|
break;
|
|
|
|
case CONSTANT_Class:
|
|
|
|
cachedCPEntries.put(new CWClass(cp.getCPClass(i)), new Integer(i));
|
|
|
|
break;
|
|
|
|
case CONSTANT_FieldRef:
|
|
|
|
case CONSTANT_InterfaceMethodRef:
|
|
|
|
case CONSTANT_MethodRef:
|
|
|
|
cachedCPEntries.put(new CWRef(t, cp.getCPRefClass(i), cp.getCPRefName(i), cp.getCPRefType(i)), new Integer(i));
|
|
|
|
break;
|
|
|
|
case CONSTANT_NameAndType:
|
|
|
|
cachedCPEntries.put(new CWNAT(cp.getCPNATName(i), cp.getCPNATType(i)), new Integer(i));
|
|
|
|
break;
|
|
|
|
case CONSTANT_Integer:
|
|
|
|
cachedCPEntries.put(new Integer(cp.getCPInt(i)), new Integer(i));
|
|
|
|
break;
|
|
|
|
case CONSTANT_Float:
|
|
|
|
cachedCPEntries.put(new Float(cp.getCPFloat(i)), new Integer(i));
|
|
|
|
break;
|
|
|
|
case CONSTANT_Long:
|
|
|
|
cachedCPEntries.put(new Long(cp.getCPLong(i)), new Integer(i));
|
|
|
|
break;
|
|
|
|
case CONSTANT_Double:
|
|
|
|
cachedCPEntries.put(new Double(cp.getCPDouble(i)), new Integer(i));
|
|
|
|
break;
|
|
|
|
case CONSTANT_Utf8:
|
|
|
|
cachedCPEntries.put(cp.getCPUtf8(i), new Integer(i));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param force
|
|
|
|
* true iff you want the addCP methods to always create a new
|
|
|
|
* constant pool entry and never reuse an existing constant pool
|
|
|
|
* entry
|
|
|
|
*/
|
|
|
|
public void setForceAddCPEntries(boolean force) {
|
|
|
|
forceAddCPEntries = force;
|
|
|
|
}
|
|
|
|
|
|
|
|
private int addCPEntry(Object o, int size) {
|
|
|
|
if (cachedCPEntries == null) {
|
|
|
|
throw new IllegalArgumentException("Cannot add a new constant pool entry during makeBytes() processing!");
|
|
|
|
}
|
|
|
|
|
|
|
|
Integer i = forceAddCPEntries ? null : cachedCPEntries.get(o);
|
|
|
|
if (i != null) {
|
|
|
|
return i.intValue();
|
|
|
|
} else {
|
|
|
|
int index = nextCPIndex;
|
|
|
|
nextCPIndex += size;
|
|
|
|
i = new Integer(index);
|
|
|
|
cachedCPEntries.put(o, i);
|
|
|
|
newCPEntries.add(o);
|
|
|
|
if (nextCPIndex > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Constant pool item count exceeded");
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a Utf8 string to the constant pool if necessary.
|
|
|
|
*
|
|
|
|
* @return the index of a constant pool item with the right value
|
|
|
|
*/
|
|
|
|
public int addCPUtf8(String s) {
|
|
|
|
return addCPEntry(s, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add an Integer to the constant pool if necessary.
|
|
|
|
*
|
|
|
|
* @return the index of a constant pool item with the right value
|
|
|
|
*/
|
|
|
|
public int addCPInt(int i) {
|
|
|
|
return addCPEntry(new Integer(i), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a Float to the constant pool if necessary.
|
|
|
|
*
|
|
|
|
* @return the index of a constant pool item with the right value
|
|
|
|
*/
|
|
|
|
public int addCPFloat(float f) {
|
|
|
|
return addCPEntry(new Float(f), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a Long to the constant pool if necessary.
|
|
|
|
*
|
|
|
|
* @return the index of a constant pool item with the right value
|
|
|
|
*/
|
|
|
|
public int addCPLong(long l) {
|
|
|
|
return addCPEntry(new Long(l), 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a Double to the constant pool if necessary.
|
|
|
|
*
|
|
|
|
* @return the index of a constant pool item with the right value
|
|
|
|
*/
|
|
|
|
public int addCPDouble(double d) {
|
|
|
|
return addCPEntry(new Double(d), 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a String to the constant pool if necessary.
|
|
|
|
*
|
|
|
|
* @return the index of a constant pool item with the right value
|
|
|
|
*/
|
|
|
|
public int addCPString(String s) {
|
|
|
|
return addCPEntry(new CWString(s), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a Class to the constant pool if necessary.
|
|
|
|
*
|
|
|
|
* @param s
|
|
|
|
* the class name, in JVM format (e.g., java/lang/Object)
|
|
|
|
* @return the index of a constant pool item with the right value
|
|
|
|
*/
|
|
|
|
public int addCPClass(String s) {
|
|
|
|
return addCPEntry(new CWClass(s), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a FieldRef to the constant pool if necessary.
|
|
|
|
*
|
|
|
|
* @param c
|
|
|
|
* the class name, in JVM format (e.g., java/lang/Object)
|
|
|
|
* @param n
|
|
|
|
* the field name
|
|
|
|
* @param t
|
|
|
|
* the field type, in JVM format (e.g., I, Z, or Ljava/lang/Object;)
|
|
|
|
* @return the index of a constant pool item with the right value
|
|
|
|
*/
|
|
|
|
public int addCPFieldRef(String c, String n, String t) {
|
|
|
|
return addCPEntry(new CWRef(CONSTANT_FieldRef, c, n, t), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a MethodRef to the constant pool if necessary.
|
|
|
|
*
|
|
|
|
* @param c
|
|
|
|
* the class name, in JVM format (e.g., java/lang/Object)
|
|
|
|
* @param n
|
|
|
|
* the method name
|
|
|
|
* @param t
|
|
|
|
* the method type, in JVM format (e.g., V(ILjava/lang/Object;) )
|
|
|
|
* @return the index of a constant pool item with the right value
|
|
|
|
*/
|
|
|
|
public int addCPMethodRef(String c, String n, String t) {
|
|
|
|
return addCPEntry(new CWRef(CONSTANT_MethodRef, c, n, t), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add an InterfaceMethodRef to the constant pool if necessary.
|
|
|
|
*
|
|
|
|
* @param c
|
|
|
|
* the class name, in JVM format (e.g., java/lang/Object)
|
|
|
|
* @param n
|
|
|
|
* the field name
|
|
|
|
* @param t
|
|
|
|
* the method type, in JVM format (e.g., V(ILjava/lang/Object;) )
|
|
|
|
* @return the index of a constant pool item with the right value
|
|
|
|
*/
|
|
|
|
public int addCPInterfaceMethodRef(String c, String n, String t) {
|
|
|
|
return addCPEntry(new CWRef(CONSTANT_InterfaceMethodRef, c, n, t), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a NameAndType to the constant pool if necessary.
|
|
|
|
*
|
|
|
|
* @param n
|
|
|
|
* the name
|
|
|
|
* @param t
|
|
|
|
* the type, in JVM format
|
|
|
|
* @return the index of a constant pool item with the right value
|
|
|
|
*/
|
|
|
|
public int addCPNAT(String n, String t) {
|
|
|
|
return addCPEntry(new CWNAT(n, t), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the access flags for the class.
|
|
|
|
*/
|
|
|
|
public void setAccessFlags(int f) {
|
|
|
|
if (f < 0 || f > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Access flags out of range: " + f);
|
|
|
|
}
|
|
|
|
accessFlags = f;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the constant pool index for the name of the class.
|
|
|
|
*/
|
2007-01-15 18:31:14 +00:00
|
|
|
public void setNameIndex(int c) throws IllegalArgumentException {
|
2006-11-22 17:45:24 +00:00
|
|
|
if (c < 1 || c > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Class name index out of range: " + c);
|
|
|
|
}
|
|
|
|
thisClass = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the constant pool index for the name of the superclass.
|
|
|
|
*/
|
|
|
|
public void setSuperNameIndex(int c) {
|
|
|
|
if (c < 0 || c > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Superclass name index out of range: " + c);
|
|
|
|
}
|
|
|
|
superClass = c;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the constant pool indices for the names of the implemented interfaces.
|
|
|
|
*/
|
|
|
|
public void setInterfaceNameIndices(int[] ifaces) {
|
|
|
|
if (ifaces != null) {
|
|
|
|
if (ifaces.length > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Too many interfaces implemented: " + ifaces.length);
|
|
|
|
}
|
|
|
|
for (int i = 0; i < ifaces.length; i++) {
|
|
|
|
int c = ifaces[i];
|
|
|
|
if (c < 1 || c > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Interface name index out of range: " + c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
superInterfaces = ifaces;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the name of the class.
|
|
|
|
*/
|
|
|
|
public void setName(String c) {
|
|
|
|
setNameIndex(addCPClass(c));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the name of the superclass; if c is null, then there is no superclass
|
|
|
|
* (this must be java/lang/Object).
|
|
|
|
*/
|
|
|
|
public void setSuperName(String c) {
|
|
|
|
setSuperNameIndex(c == null ? 0 : addCPClass(c));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the names of the implemented interfaces.
|
|
|
|
*/
|
|
|
|
public void setInterfaceNames(String[] ifaces) {
|
|
|
|
if (ifaces == null) {
|
|
|
|
setInterfaceNameIndices((int[]) null);
|
|
|
|
} else {
|
|
|
|
int[] ifs = new int[ifaces.length];
|
|
|
|
for (int i = 0; i < ifaces.length; i++) {
|
|
|
|
ifs[i] = addCPClass(ifaces[i]);
|
|
|
|
}
|
|
|
|
setInterfaceNameIndices(ifs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An Element is an object that can be serialized into a byte buffer.
|
|
|
|
* Serialization via 'copyInto' is performed when the user calls makeBytes()
|
|
|
|
* on the ClassWriter. At this time no new constant pool items can be
|
|
|
|
* allocated, so any item indices that need to be emitted must be allocated
|
|
|
|
* earlier.
|
|
|
|
*/
|
|
|
|
public static abstract class Element {
|
|
|
|
public Element() {
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return the number of bytes that will be generated.
|
|
|
|
*/
|
|
|
|
public abstract int getSize();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copy the bytes into 'buf' at offset 'offset'.
|
|
|
|
*
|
|
|
|
* @return the number of bytes copies, which must be equal to getSize()
|
|
|
|
*/
|
|
|
|
public abstract int copyInto(byte[] buf, int offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A RawElement is an Element that is already available as some chunk of a
|
|
|
|
* byte buffer.
|
|
|
|
*/
|
|
|
|
public static final class RawElement extends Element {
|
2007-06-05 01:17:18 +00:00
|
|
|
final private byte[] buf;
|
2007-06-01 21:39:03 +00:00
|
|
|
final private int offset;
|
2007-06-05 01:17:18 +00:00
|
|
|
final private int len;
|
2006-11-22 17:45:24 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an Element for the 'len' bytes in 'buf' at offset 'offset'.
|
|
|
|
*/
|
|
|
|
public RawElement(byte[] buf, int offset, int len) {
|
|
|
|
this.buf = buf;
|
|
|
|
this.offset = offset;
|
|
|
|
this.len = len;
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
public int getSize() {
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
public int copyInto(byte[] dest, int destOffset) {
|
|
|
|
System.arraycopy(buf, offset, dest, destOffset, len);
|
|
|
|
return destOffset + len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a method to the class, the method data given as "raw" bytes (probably
|
|
|
|
* obtained from a ClassReader).
|
|
|
|
*/
|
|
|
|
public void addRawMethod(Element e) {
|
|
|
|
methods.add(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a field to the class, the field data given as "raw" bytes (probably
|
|
|
|
* obtained from a ClassReader).
|
|
|
|
*/
|
|
|
|
public void addRawField(Element e) {
|
|
|
|
fields.add(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a method to the class.
|
|
|
|
*
|
|
|
|
* @param access
|
|
|
|
* the access flags
|
|
|
|
* @param name
|
|
|
|
* the method name
|
|
|
|
* @param type
|
|
|
|
* the method type in JVM format (e.g., V(ILjava/lang/Object;) )
|
|
|
|
* @param attributes
|
|
|
|
* the attributes in raw form, one Element per attribute
|
|
|
|
*/
|
|
|
|
public void addMethod(int access, String name, String type, Element[] attributes) {
|
|
|
|
addMethod(access, addCPUtf8(name), addCPUtf8(type), attributes);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a field to the class.
|
|
|
|
*
|
|
|
|
* @param access
|
|
|
|
* the access flags
|
|
|
|
* @param name
|
|
|
|
* the field name
|
|
|
|
* @param type
|
|
|
|
* the field type in JVM format (e.g., I, Z, Ljava/lang/Object;)
|
|
|
|
* @param attributes
|
|
|
|
* the attributes in raw form, one Element per attribute
|
|
|
|
*/
|
|
|
|
public void addField(int access, String name, String type, Element[] attributes) {
|
|
|
|
addField(access, addCPUtf8(name), addCPUtf8(type), attributes);
|
|
|
|
}
|
|
|
|
|
|
|
|
static final class MemberElement extends Element {
|
2007-06-05 01:17:18 +00:00
|
|
|
final private int access;
|
|
|
|
final private int name;
|
|
|
|
final private int type;
|
|
|
|
|
|
|
|
final private Element[] attributes;
|
2006-11-22 17:45:24 +00:00
|
|
|
|
|
|
|
public MemberElement(int access, int name, int type, Element[] attributes) {
|
|
|
|
if (access < 0 || access > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Access flags out of range: " + access);
|
|
|
|
}
|
|
|
|
if (name < 1 || name > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Name constant pool index out of range: " + name);
|
|
|
|
}
|
|
|
|
if (type < 1 || type > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Type constant pool index out of range: " + name);
|
|
|
|
}
|
2007-08-17 21:09:49 +00:00
|
|
|
if (attributes == null) {
|
|
|
|
throw new IllegalArgumentException("Atrtributes are null");
|
|
|
|
}
|
2006-11-22 17:45:24 +00:00
|
|
|
if (attributes.length > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Too many attributes: " + attributes.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.access = access;
|
|
|
|
this.name = name;
|
|
|
|
this.type = type;
|
|
|
|
this.attributes = attributes;
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
public int getSize() {
|
|
|
|
int size = 8;
|
|
|
|
if (attributes != null) {
|
|
|
|
for (int i = 0; i < attributes.length; i++) {
|
|
|
|
size += attributes[i].getSize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2007-05-30 15:42:59 +00:00
|
|
|
@Override
|
2006-11-22 17:45:24 +00:00
|
|
|
public int copyInto(byte[] buf, int offset) {
|
|
|
|
setUShort(buf, offset, access);
|
|
|
|
setUShort(buf, offset + 2, name);
|
|
|
|
setUShort(buf, offset + 4, type);
|
|
|
|
if (attributes != null) {
|
|
|
|
setUShort(buf, offset + 6, attributes.length);
|
|
|
|
offset += 8;
|
|
|
|
for (int i = 0; i < attributes.length; i++) {
|
|
|
|
offset = attributes[i].copyInto(buf, offset);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
setUShort(buf, offset + 6, 0);
|
|
|
|
offset += 8;
|
|
|
|
}
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a method to the class.
|
|
|
|
*
|
|
|
|
* @param access
|
|
|
|
* the access flags
|
|
|
|
* @param name
|
|
|
|
* the constant pool index of the method name
|
|
|
|
* @param type
|
|
|
|
* the constant pool index of the method type in JVM format (e.g.,
|
|
|
|
* V(ILjava/lang/Object;) )
|
|
|
|
* @param attributes
|
|
|
|
* the attributes in raw form, one Element per attribute
|
|
|
|
*/
|
|
|
|
public void addMethod(int access, int name, int type, Element[] attributes) {
|
2007-06-05 01:17:18 +00:00
|
|
|
// int idx=methods.size()-2;
|
|
|
|
// if (idx<0) idx=0;
|
|
|
|
// methods.add(0,new MemberElement(access, name, type, attributes));
|
2006-11-22 17:45:24 +00:00
|
|
|
methods.add(new MemberElement(access, name, type, attributes));
|
|
|
|
if (methods.size() > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Too many methods");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a field to the class.
|
|
|
|
*
|
|
|
|
* @param access
|
|
|
|
* the access flags
|
|
|
|
* @param name
|
|
|
|
* the constant pool index of the field name
|
|
|
|
* @param type
|
|
|
|
* the constant pool index of the field type in JVM format (e.g., I,
|
|
|
|
* Z, Ljava/lang/Object;)
|
|
|
|
* @param attributes
|
|
|
|
* the attributes in raw form, one Element per attribute
|
|
|
|
*/
|
|
|
|
public void addField(int access, int name, int type, Element[] attributes) {
|
|
|
|
fields.add(new MemberElement(access, name, type, attributes));
|
|
|
|
if (fields.size() > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Too many fields");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add an atttribute to the class.
|
|
|
|
*
|
|
|
|
* @param attribute
|
|
|
|
* the attribute in raw form
|
|
|
|
*/
|
|
|
|
public void addClassAttribute(Element attribute) {
|
|
|
|
classAttributes.add(attribute);
|
|
|
|
if (classAttributes.size() > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("Too many class attributes: " + classAttributes.size());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private int reserveBuf(int size) {
|
|
|
|
if (buf == null) {
|
|
|
|
buf = new byte[size];
|
|
|
|
} else if (bufLen + size > buf.length) {
|
|
|
|
byte[] newBuf = new byte[Math.max(buf.length * 2, bufLen + size)];
|
|
|
|
System.arraycopy(buf, 0, newBuf, 0, bufLen);
|
|
|
|
buf = newBuf;
|
|
|
|
}
|
|
|
|
int offset = bufLen;
|
|
|
|
bufLen += size;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void emitElement(Element e) {
|
|
|
|
int size = e.getSize();
|
|
|
|
int offset = reserveBuf(size);
|
|
|
|
int finalOffset = e.copyInto(buf, offset);
|
|
|
|
if (finalOffset - offset != size) {
|
|
|
|
throw new Error("Element failed to output the promised bytes: promised " + size + ", got " + (finalOffset - offset));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static final char[] noChars = new char[0];
|
|
|
|
|
|
|
|
private void emitConstantPool() {
|
|
|
|
if (rawCP != null) {
|
|
|
|
int len = rawCP.getRawSize();
|
|
|
|
int offset = reserveBuf(len);
|
|
|
|
System.arraycopy(rawCP.getRawBytes(), rawCP.getRawOffset(), buf, offset, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
char[] chars = noChars;
|
|
|
|
|
|
|
|
// BE CAREFUL: the newCPEntries array grows during this loop.
|
|
|
|
for (int i = 0; i < newCPEntries.size(); i++) {
|
|
|
|
Object o = newCPEntries.get(i);
|
|
|
|
if (o instanceof CWItem) {
|
|
|
|
CWItem item = (CWItem) o;
|
|
|
|
byte t = item.getType();
|
|
|
|
int offset;
|
|
|
|
switch (t) {
|
|
|
|
case CONSTANT_Class:
|
|
|
|
offset = reserveBuf(3);
|
|
|
|
setUShort(buf, offset + 1, addCPUtf8(((CWClass) item).c));
|
|
|
|
break;
|
|
|
|
case CONSTANT_String:
|
|
|
|
offset = reserveBuf(3);
|
|
|
|
setUShort(buf, offset + 1, addCPUtf8(((CWString) item).s));
|
|
|
|
break;
|
|
|
|
case CONSTANT_NameAndType: {
|
|
|
|
offset = reserveBuf(5);
|
|
|
|
CWNAT nat = (CWNAT) item;
|
|
|
|
setUShort(buf, offset + 1, addCPUtf8(nat.n));
|
|
|
|
setUShort(buf, offset + 3, addCPUtf8(nat.t));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CONSTANT_MethodRef:
|
|
|
|
case CONSTANT_FieldRef:
|
|
|
|
case CONSTANT_InterfaceMethodRef: {
|
|
|
|
offset = reserveBuf(5);
|
|
|
|
CWRef ref = (CWRef) item;
|
|
|
|
setUShort(buf, offset + 1, addCPClass(ref.c));
|
|
|
|
setUShort(buf, offset + 3, addCPNAT(ref.n, ref.t));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
throw new Error("Invalid type: " + t);
|
|
|
|
}
|
|
|
|
buf[offset] = t;
|
|
|
|
} else {
|
|
|
|
if (o instanceof String) {
|
|
|
|
String s = (String) o;
|
|
|
|
int slen = s.length();
|
|
|
|
|
|
|
|
if (chars.length < slen) {
|
|
|
|
chars = new char[slen];
|
|
|
|
}
|
|
|
|
s.getChars(0, slen, chars, 0);
|
|
|
|
|
|
|
|
int offset = reserveBuf(3);
|
|
|
|
buf[offset] = CONSTANT_Utf8;
|
|
|
|
|
|
|
|
int maxBytes = slen * 3;
|
|
|
|
int p = reserveBuf(maxBytes); // worst case reservation
|
|
|
|
|
|
|
|
for (int j = 0; j < slen; j++) {
|
|
|
|
char ch = chars[j];
|
|
|
|
if (ch == 0) {
|
|
|
|
setUShort(buf, p, 0xC080);
|
|
|
|
p += 2;
|
|
|
|
} else if (ch < 0x80) {
|
|
|
|
buf[p] = (byte) ch;
|
|
|
|
p += 1;
|
|
|
|
} else if (ch < 0x800) {
|
|
|
|
buf[p] = (byte) ((ch >> 6) | 0xC0);
|
|
|
|
buf[p + 1] = (byte) ((ch & 0x3F) | 0x80);
|
|
|
|
p += 2;
|
|
|
|
} else {
|
|
|
|
buf[p] = (byte) ((ch >> 12) | 0xE0);
|
|
|
|
buf[p + 1] = (byte) (((ch >> 6) & 0x3F) | 0x80);
|
|
|
|
buf[p + 2] = (byte) ((ch & 0x3F) | 0x80);
|
|
|
|
p += 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int bytes = p - (offset + 3);
|
|
|
|
reserveBuf(bytes - maxBytes); // negative reservation to push back buf
|
2007-06-05 01:17:18 +00:00
|
|
|
// size
|
2006-11-22 17:45:24 +00:00
|
|
|
if (bytes > 0xFFFF) {
|
|
|
|
throw new IllegalArgumentException("String too long: " + bytes + " bytes");
|
|
|
|
}
|
|
|
|
setUShort(buf, offset + 1, bytes);
|
|
|
|
} else if (o instanceof Integer) {
|
|
|
|
int offset = reserveBuf(5);
|
|
|
|
buf[offset] = CONSTANT_Integer;
|
|
|
|
setInt(buf, offset + 1, ((Integer) o).intValue());
|
|
|
|
} else if (o instanceof Long) {
|
|
|
|
int offset = reserveBuf(9);
|
|
|
|
buf[offset] = CONSTANT_Long;
|
|
|
|
setLong(buf, offset + 1, ((Long) o).longValue());
|
|
|
|
} else if (o instanceof Float) {
|
|
|
|
int offset = reserveBuf(5);
|
|
|
|
buf[offset] = CONSTANT_Float;
|
|
|
|
setFloat(buf, offset + 1, ((Float) o).intValue());
|
|
|
|
} else if (o instanceof Double) {
|
|
|
|
int offset = reserveBuf(9);
|
|
|
|
buf[offset] = CONSTANT_Double;
|
|
|
|
setDouble(buf, offset + 1, ((Double) o).intValue());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* After you've added everything you need to the class, call this method to
|
|
|
|
* generate the actual class file data. This can only be called once.
|
|
|
|
*/
|
2007-01-15 18:31:14 +00:00
|
|
|
public byte[] makeBytes() throws IllegalArgumentException {
|
2006-11-22 17:45:24 +00:00
|
|
|
if (buf != null) {
|
|
|
|
throw new IllegalArgumentException("Can't call makeBytes() twice");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thisClass == 0) {
|
|
|
|
throw new IllegalArgumentException("No class name set");
|
|
|
|
}
|
|
|
|
|
|
|
|
reserveBuf(10);
|
|
|
|
setInt(buf, 0, MAGIC);
|
|
|
|
setUShort(buf, 4, minorVersion);
|
|
|
|
setUShort(buf, 6, majorVersion);
|
|
|
|
|
|
|
|
emitConstantPool();
|
|
|
|
// The constant pool can grow during emmission, so store the size last
|
|
|
|
setUShort(buf, 8, nextCPIndex);
|
|
|
|
// No new constant pool entries can be allocated; make sure we
|
|
|
|
// catch any such error by client code
|
|
|
|
cachedCPEntries = null;
|
|
|
|
|
|
|
|
int offset = reserveBuf(8);
|
|
|
|
setUShort(buf, offset, accessFlags);
|
|
|
|
setUShort(buf, offset + 2, thisClass);
|
|
|
|
setUShort(buf, offset + 4, superClass);
|
|
|
|
if (superInterfaces != null) {
|
|
|
|
setUShort(buf, offset + 6, superInterfaces.length);
|
|
|
|
reserveBuf(superInterfaces.length * 2);
|
|
|
|
for (int i = 0; i < superInterfaces.length; i++) {
|
|
|
|
setUShort(buf, offset + 8 + i * 2, superInterfaces[i]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
setUShort(buf, offset + 6, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
offset = reserveBuf(2);
|
|
|
|
int numFields = fields.size();
|
|
|
|
setUShort(buf, offset, numFields);
|
|
|
|
for (int i = 0; i < numFields; i++) {
|
|
|
|
emitElement(fields.get(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
offset = reserveBuf(2);
|
|
|
|
int numMethods = methods.size();
|
2007-06-05 01:17:18 +00:00
|
|
|
// Xiangyu, debug
|
|
|
|
// System.out.println("numMethods="+numMethods);
|
2006-11-22 17:45:24 +00:00
|
|
|
setUShort(buf, offset, numMethods);
|
|
|
|
for (int i = 0; i < numMethods; i++) {
|
|
|
|
emitElement(methods.get(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
offset = reserveBuf(2);
|
|
|
|
int numAttrs = classAttributes.size();
|
|
|
|
setUShort(buf, offset, numAttrs);
|
|
|
|
for (int i = 0; i < numAttrs; i++) {
|
|
|
|
emitElement(classAttributes.get(i));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buf.length == bufLen) {
|
|
|
|
return buf;
|
|
|
|
} else {
|
|
|
|
byte[] b = new byte[bufLen];
|
|
|
|
System.arraycopy(buf, 0, b, 0, bufLen);
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the byte at offset 'offset' in 'buf' to the unsigned 8-bit value in v.
|
2007-06-05 01:17:18 +00:00
|
|
|
*
|
|
|
|
* @throws IllegalArgumentException
|
|
|
|
* if buf is null
|
2006-11-22 17:45:24 +00:00
|
|
|
*/
|
2007-01-15 18:31:14 +00:00
|
|
|
public static void setUByte(byte[] buf, int offset, int v) throws IllegalArgumentException {
|
2007-05-08 21:23:06 +00:00
|
|
|
if (buf == null) {
|
|
|
|
throw new IllegalArgumentException("buf is null");
|
|
|
|
}
|
2007-01-15 18:31:14 +00:00
|
|
|
if (offset >= buf.length) {
|
|
|
|
throw new IllegalArgumentException("illegal offset " + offset);
|
|
|
|
}
|
2006-11-22 17:45:24 +00:00
|
|
|
buf[offset] = (byte) v;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the 4 bytes at offset 'offset' in 'buf' to the signed 32-bit value in
|
|
|
|
* v.
|
2007-06-05 01:17:18 +00:00
|
|
|
*
|
|
|
|
* @throws IllegalArgumentException
|
|
|
|
* if buf is null
|
2006-11-22 17:45:24 +00:00
|
|
|
*/
|
2007-01-15 18:31:14 +00:00
|
|
|
public static void setInt(byte[] buf, int offset, int v) throws IllegalArgumentException {
|
2007-05-08 21:23:06 +00:00
|
|
|
if (buf == null) {
|
|
|
|
throw new IllegalArgumentException("buf is null");
|
|
|
|
}
|
2007-01-15 18:31:14 +00:00
|
|
|
if (offset >= buf.length) {
|
|
|
|
throw new IllegalArgumentException("illegal offset " + offset);
|
|
|
|
}
|
2006-11-22 17:45:24 +00:00
|
|
|
buf[offset] = (byte) (v >> 24);
|
|
|
|
buf[offset + 1] = (byte) (v >> 16);
|
|
|
|
buf[offset + 2] = (byte) (v >> 8);
|
|
|
|
buf[offset + 3] = (byte) v;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the 8 bytes at offset 'offset' in 'buf' to the signed 64-bit value in
|
|
|
|
* v.
|
|
|
|
*/
|
2007-01-15 18:31:14 +00:00
|
|
|
public static void setLong(byte[] buf, int offset, long v) throws IllegalArgumentException {
|
2006-11-22 17:45:24 +00:00
|
|
|
setInt(buf, offset, (int) (v >> 32));
|
|
|
|
setInt(buf, offset + 4, (int) v);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the 4 bytes at offset 'offset' in 'buf' to the float value in v.
|
|
|
|
*/
|
2007-06-05 01:17:18 +00:00
|
|
|
public static void setFloat(byte[] buf, int offset, float v) throws IllegalArgumentException {
|
2006-11-22 17:45:24 +00:00
|
|
|
setInt(buf, offset, Float.floatToIntBits(v));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the 8 bytes at offset 'offset' in 'buf' to the double value in v.
|
|
|
|
*/
|
2007-06-05 01:17:18 +00:00
|
|
|
public static void setDouble(byte[] buf, int offset, double v) throws IllegalArgumentException {
|
2006-11-22 17:45:24 +00:00
|
|
|
setLong(buf, offset, Double.doubleToRawLongBits(v));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the 2 bytes at offset 'offset' in 'buf' to the unsigned 16-bit value in
|
|
|
|
* v.
|
2007-06-05 01:17:18 +00:00
|
|
|
*
|
|
|
|
* @throws IllegalArgumentException
|
|
|
|
* if buf is null
|
2006-11-22 17:45:24 +00:00
|
|
|
*/
|
2007-01-15 18:31:14 +00:00
|
|
|
public static void setUShort(byte[] buf, int offset, int v) throws IllegalArgumentException {
|
2007-05-08 21:23:06 +00:00
|
|
|
if (buf == null) {
|
|
|
|
throw new IllegalArgumentException("buf is null");
|
|
|
|
}
|
2007-01-15 15:08:12 +00:00
|
|
|
if (offset + 1 >= buf.length) {
|
|
|
|
throw new IllegalArgumentException("buf is too short " + buf.length + " " + offset);
|
|
|
|
}
|
2006-11-22 17:45:24 +00:00
|
|
|
buf[offset] = (byte) (v >> 8);
|
|
|
|
buf[offset + 1] = (byte) v;
|
|
|
|
}
|
|
|
|
}
|