746 lines
26 KiB
Java
746 lines
26 KiB
Java
/*
|
|
* Copyright 2005 Sun Microsystems, Inc. All Rights Reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
|
* have any questions.
|
|
*/
|
|
|
|
package ilib;
|
|
|
|
import java.io.IOException;
|
|
import java.io.File;
|
|
import java.io.FileOutputStream;
|
|
import java.io.DataOutputStream;
|
|
import java.util.List;
|
|
import java.util.Iterator;
|
|
import java.util.ArrayList;
|
|
|
|
public class Inject implements RuntimeConstants {
|
|
|
|
public static byte[] instrumentation(Options opt,
|
|
ClassLoader loader,
|
|
String className,
|
|
byte[] classfileBuffer) {
|
|
ClassReaderWriter c = new ClassReaderWriter(classfileBuffer);
|
|
(new Inject(className, c, loader == null, opt)).doit();
|
|
return c.result();
|
|
}
|
|
|
|
static boolean verbose = false;
|
|
|
|
final String className;
|
|
final ClassReaderWriter c;
|
|
final boolean isSystem;
|
|
final Options options;
|
|
|
|
int constantPoolCount;
|
|
int methodsCount;
|
|
int methodsCountPos;
|
|
int profiler;
|
|
int wrappedTrackerIndex = 0;
|
|
int thisClassIndex = 0;
|
|
|
|
TrackerInjector callInjector;
|
|
TrackerInjector allocInjector;
|
|
TrackerInjector defaultInjector;
|
|
|
|
static interface TrackerInjector extends Injector {
|
|
void reinit(int tracker);
|
|
int stackSize(int currentSize);
|
|
}
|
|
|
|
static class SimpleInjector implements TrackerInjector {
|
|
byte[] injection;
|
|
|
|
public int stackSize(int currentSize) {
|
|
return currentSize;
|
|
}
|
|
|
|
public void reinit(int tracker) {
|
|
injection = new byte[3];
|
|
injection[0] = (byte)opc_invokestatic;
|
|
injection[1] = (byte)(tracker >> 8);
|
|
injection[2] = (byte)tracker;
|
|
}
|
|
|
|
public byte[] bytecodes(String className, String methodName, int location) {
|
|
return injection;
|
|
}
|
|
}
|
|
|
|
static class ObjectInjector implements TrackerInjector {
|
|
byte[] injection;
|
|
|
|
public int stackSize(int currentSize) {
|
|
return currentSize + 1;
|
|
}
|
|
|
|
public void reinit(int tracker) {
|
|
injection = new byte[4];
|
|
injection[0] = (byte)opc_dup;
|
|
injection[1] = (byte)opc_invokestatic;
|
|
injection[2] = (byte)(tracker >> 8);
|
|
injection[3] = (byte)tracker;
|
|
}
|
|
|
|
public byte[] bytecodes(String className, String methodName, int location) {
|
|
return injection;
|
|
}
|
|
}
|
|
|
|
class IndexedInjector implements TrackerInjector {
|
|
int counter = 0;
|
|
int tracker;
|
|
List<Info> infoList = new ArrayList<Info>();
|
|
|
|
public int stackSize(int currentSize) {
|
|
return currentSize + 1;
|
|
}
|
|
|
|
public void reinit(int tracker) {
|
|
this.tracker = tracker;
|
|
}
|
|
|
|
void dump(File outDir, String filename) throws IOException {
|
|
FileOutputStream fileOut = new FileOutputStream(new File(outDir, filename));
|
|
DataOutputStream dataOut = new DataOutputStream(fileOut);
|
|
|
|
String currentClassName = null;
|
|
|
|
dataOut.writeInt(infoList.size());
|
|
for (Iterator<Info> it = infoList.iterator(); it.hasNext(); ) {
|
|
Info info = it.next();
|
|
if (!info.className.equals(currentClassName)) {
|
|
dataOut.writeInt(123456); // class name marker
|
|
currentClassName = info.className;
|
|
dataOut.writeUTF(currentClassName);
|
|
}
|
|
dataOut.writeInt(info.location);
|
|
dataOut.writeUTF(info.methodName);
|
|
}
|
|
dataOut.close();
|
|
}
|
|
|
|
public byte[] bytecodes(String className, String methodName, int location) {
|
|
byte[] injection = new byte[6];
|
|
int injectedIndex = options.fixedIndex != 0? options.fixedIndex : ++counter;
|
|
infoList.add(new Info(counter, className, methodName, location));
|
|
injection[0] = (byte)opc_sipush;
|
|
injection[1] = (byte)(injectedIndex >> 8);
|
|
injection[2] = (byte)injectedIndex;
|
|
injection[3] = (byte)opc_invokestatic;
|
|
injection[4] = (byte)(tracker >> 8);
|
|
injection[5] = (byte)tracker;
|
|
return injection;
|
|
}
|
|
}
|
|
|
|
Inject(String className, ClassReaderWriter c, boolean isSystem, Options options) {
|
|
this.className = className;
|
|
this.c = c;
|
|
this.isSystem = isSystem;
|
|
this.options = options;
|
|
}
|
|
|
|
void doit() {
|
|
int i;
|
|
c.copy(4 + 2 + 2); // magic min/maj version
|
|
int constantPoolCountPos = c.generatedPosition();
|
|
constantPoolCount = c.copyU2();
|
|
// copy old constant pool
|
|
c.copyConstantPool(constantPoolCount);
|
|
|
|
if (verbose) {
|
|
System.out.println("ConstantPool expanded from: " +
|
|
constantPoolCount);
|
|
}
|
|
|
|
profiler = addClassToConstantPool(options.trackerClassName);
|
|
if (options.shouldInstrumentNew || options.shouldInstrumentObjectInit) {
|
|
if (options.shouldInstrumentIndexed) {
|
|
if (allocInjector == null) {
|
|
// first time - create it
|
|
allocInjector = new IndexedInjector();
|
|
}
|
|
int allocTracker = addMethodToConstantPool(profiler,
|
|
options.allocTrackerMethodName,
|
|
"(I)V");
|
|
allocInjector.reinit(allocTracker);
|
|
} else if (options.shouldInstrumentObject) {
|
|
if (allocInjector == null) {
|
|
// first time - create it
|
|
allocInjector = new ObjectInjector();
|
|
}
|
|
int allocTracker = addMethodToConstantPool(profiler,
|
|
options.allocTrackerMethodName,
|
|
"(Ljava/lang/Object;)V");
|
|
allocInjector.reinit(allocTracker);
|
|
} else {
|
|
if (allocInjector == null) {
|
|
// first time - create it
|
|
allocInjector = new SimpleInjector();
|
|
}
|
|
int allocTracker = addMethodToConstantPool(profiler,
|
|
options.allocTrackerMethodName,
|
|
"()V");
|
|
allocInjector.reinit(allocTracker);
|
|
}
|
|
defaultInjector = allocInjector;
|
|
}
|
|
if (options.shouldInstrumentCall) {
|
|
if (options.shouldInstrumentIndexed) {
|
|
if (callInjector == null) {
|
|
// first time - create it
|
|
callInjector = new IndexedInjector();
|
|
}
|
|
int callTracker = addMethodToConstantPool(profiler,
|
|
options.callTrackerMethodName,
|
|
"(I)V");
|
|
callInjector.reinit(callTracker);
|
|
} else {
|
|
if (callInjector == null) {
|
|
// first time - create it
|
|
callInjector = new SimpleInjector();
|
|
}
|
|
int callTracker = addMethodToConstantPool(profiler,
|
|
options.callTrackerMethodName,
|
|
"()V");
|
|
callInjector.reinit(callTracker);
|
|
}
|
|
defaultInjector = callInjector;
|
|
}
|
|
|
|
if (verbose) {
|
|
System.out.println("To: " + constantPoolCount);
|
|
}
|
|
|
|
c.setSection(1);
|
|
|
|
c.copy(2 + 2 + 2); // access, this, super
|
|
int interfaceCount = c.copyU2();
|
|
if (verbose) {
|
|
System.out.println("interfaceCount: " + interfaceCount);
|
|
}
|
|
c.copy(interfaceCount * 2);
|
|
copyFields(); // fields
|
|
copyMethods(); // methods
|
|
int attrCountPos = c.generatedPosition();
|
|
int attrCount = c.copyU2();
|
|
if (verbose) {
|
|
System.out.println("class attrCount: " + attrCount);
|
|
}
|
|
// copy the class attributes
|
|
copyAttrs(attrCount);
|
|
|
|
c.randomAccessWriteU2(constantPoolCountPos, constantPoolCount);
|
|
}
|
|
|
|
|
|
void copyFields() {
|
|
int count = c.copyU2();
|
|
if (verbose) {
|
|
System.out.println("fields count: " + count);
|
|
}
|
|
for (int i = 0; i < count; ++i) {
|
|
c.copy(6); // access, name, descriptor
|
|
int attrCount = c.copyU2();
|
|
if (verbose) {
|
|
System.out.println("field attr count: " + attrCount);
|
|
}
|
|
copyAttrs(attrCount);
|
|
}
|
|
}
|
|
|
|
void copyMethods() {
|
|
methodsCountPos = c.generatedPosition();
|
|
methodsCount = c.copyU2();
|
|
int initialMethodsCount = methodsCount;
|
|
if (verbose) {
|
|
System.out.println("methods count: " + methodsCount);
|
|
}
|
|
for (int i = 0; i < initialMethodsCount; ++i) {
|
|
copyMethod();
|
|
}
|
|
}
|
|
|
|
void copyMethod() {
|
|
int accessFlags = c.copyU2();// access flags
|
|
if (options.shouldInstrumentNativeMethods && (accessFlags & ACC_NATIVE) != 0) {
|
|
wrapNativeMethod(accessFlags);
|
|
return;
|
|
}
|
|
int nameIndex = c.copyU2(); // name
|
|
String methodName = c.constantPoolString(nameIndex);
|
|
c.copyU2(); // descriptor
|
|
int attrCount = c.copyU2(); // attribute count
|
|
if (verbose) {
|
|
System.out.println("methods attr count: " + attrCount);
|
|
}
|
|
for (int i = 0; i < attrCount; ++i) {
|
|
copyAttrForMethod(methodName, accessFlags);
|
|
}
|
|
}
|
|
|
|
void wrapNativeMethod(int accessFlags) {
|
|
// first, copy the native method with the name changed
|
|
// accessFlags have already been copied
|
|
int nameIndex = c.readU2(); // name
|
|
String methodName = c.constantPoolString(nameIndex);
|
|
String wrappedMethodName = options.wrappedPrefix + methodName;
|
|
int wrappedNameIndex = writeCPEntryUtf8(wrappedMethodName);
|
|
c.writeU2(wrappedNameIndex); // change to the wrapped name
|
|
|
|
int descriptorIndex = c.copyU2(); // descriptor index
|
|
|
|
int attrCount = c.copyU2(); // attribute count
|
|
// need to replicate these attributes (esp Exceptions) in wrapper
|
|
// so mark this location so we can rewind
|
|
c.markLocalPositionStart();
|
|
for (int i = 0; i < attrCount; ++i) {
|
|
copyAttrForMethod(methodName, accessFlags);
|
|
}
|
|
if (true) {
|
|
System.err.println(" wrapped: " + methodName);
|
|
}
|
|
|
|
// now write the wrapper method
|
|
c.writeU2(accessFlags & ~ACC_NATIVE);
|
|
c.writeU2(nameIndex); // original unwrapped name
|
|
c.writeU2(descriptorIndex); // descriptor is the same
|
|
|
|
c.writeU2(attrCount + 1); // wrapped plus a code attribute
|
|
// rewind to wrapped attributes
|
|
c.rewind();
|
|
for (int i = 0; i < attrCount; ++i) {
|
|
copyAttrForMethod(methodName, accessFlags);
|
|
}
|
|
|
|
// generate a Code attribute for the wrapper method
|
|
int wrappedIndex = addMethodToConstantPool(getThisClassIndex(),
|
|
wrappedNameIndex,
|
|
descriptorIndex);
|
|
String descriptor = c.constantPoolString(descriptorIndex);
|
|
createWrapperCodeAttr(nameIndex, accessFlags, descriptor, wrappedIndex);
|
|
|
|
// increment method count
|
|
c.randomAccessWriteU2(methodsCountPos, ++methodsCount);
|
|
}
|
|
|
|
void copyAttrs(int attrCount) {
|
|
for (int i = 0; i < attrCount; ++i) {
|
|
copyAttr();
|
|
}
|
|
}
|
|
|
|
void copyAttr() {
|
|
c.copy(2); // name
|
|
int len = c.copyU4(); // attr len
|
|
if (verbose) {
|
|
System.out.println("attr len: " + len);
|
|
}
|
|
c.copy(len); // attribute info
|
|
}
|
|
|
|
void copyAttrForMethod(String methodName, int accessFlags) {
|
|
int nameIndex = c.copyU2(); // name
|
|
// check for Code attr
|
|
if (nameIndex == c.codeAttributeIndex) {
|
|
try {
|
|
copyCodeAttr(methodName);
|
|
} catch (IOException exc) {
|
|
System.err.println("Code Exception - " + exc);
|
|
System.exit(1);
|
|
}
|
|
} else {
|
|
int len = c.copyU4(); // attr len
|
|
if (verbose) {
|
|
System.out.println("method attr len: " + len);
|
|
}
|
|
c.copy(len); // attribute info
|
|
}
|
|
}
|
|
|
|
void copyAttrForCode(InjectBytecodes ib) throws IOException {
|
|
int nameIndex = c.copyU2(); // name
|
|
|
|
// check for Code attr
|
|
if (nameIndex == c.lineNumberAttributeIndex) {
|
|
ib.copyLineNumberAttr();
|
|
} else if (nameIndex == c.localVarAttributeIndex) {
|
|
ib.copyLocalVarAttr();
|
|
} else {
|
|
int len = c.copyU4(); // attr len
|
|
if (verbose) {
|
|
System.out.println("code attr len: " + len);
|
|
}
|
|
c.copy(len); // attribute info
|
|
}
|
|
}
|
|
|
|
void copyCodeAttr(String methodName) throws IOException {
|
|
if (verbose) {
|
|
System.out.println("Code attr found");
|
|
}
|
|
int attrLengthPos = c.generatedPosition();
|
|
int attrLength = c.copyU4(); // attr len
|
|
int maxStack = c.readU2(); // max stack
|
|
c.writeU2(defaultInjector == null? maxStack :
|
|
defaultInjector.stackSize(maxStack)); // big enough for injected code
|
|
c.copyU2(); // max locals
|
|
int codeLengthPos = c.generatedPosition();
|
|
int codeLength = c.copyU4(); // code length
|
|
if (options.targetMethod != null && !options.targetMethod.equals(methodName)) {
|
|
c.copy(attrLength - 8); // copy remainder minus already copied
|
|
return;
|
|
}
|
|
if (isSystem) {
|
|
if (codeLength == 1 && methodName.equals("finalize")) {
|
|
if (verbose) {
|
|
System.out.println("empty system finalizer not instrumented");
|
|
}
|
|
c.copy(attrLength - 8); // copy remainder minus already copied
|
|
return;
|
|
}
|
|
if (codeLength == 1 && methodName.equals("<init>")) {
|
|
if (verbose) {
|
|
System.out.println("empty system constructor not instrumented");
|
|
}
|
|
if (!options.shouldInstrumentObjectInit) {
|
|
c.copy(attrLength - 8); // copy remainder minus already copied
|
|
return;
|
|
}
|
|
}
|
|
if (methodName.equals("<clinit>")) {
|
|
if (verbose) {
|
|
System.out.println("system class initializer not instrumented");
|
|
}
|
|
c.copy(attrLength - 8); // copy remainder minus already copied
|
|
return;
|
|
}
|
|
}
|
|
if (options.shouldInstrumentObjectInit
|
|
&& (!className.equals("java/lang/Object")
|
|
|| !methodName.equals("<init>"))) {
|
|
c.copy(attrLength - 8); // copy remainder minus already copied
|
|
return;
|
|
}
|
|
|
|
InjectBytecodes ib = new InjectBytecodes(c, codeLength, className, methodName);
|
|
|
|
if (options.shouldInstrumentNew) {
|
|
ib.injectAfter(opc_new, allocInjector);
|
|
ib.injectAfter(opc_newarray, allocInjector);
|
|
ib.injectAfter(opc_anewarray, allocInjector);
|
|
ib.injectAfter(opc_multianewarray, allocInjector);
|
|
}
|
|
if (options.shouldInstrumentCall) {
|
|
ib.inject(0, callInjector.bytecodes(className, methodName, 0));
|
|
}
|
|
if (options.shouldInstrumentObjectInit) {
|
|
ib.inject(0, allocInjector.bytecodes(className, methodName, 0));
|
|
}
|
|
|
|
ib.adjustOffsets();
|
|
|
|
// fix up code length
|
|
int newCodeLength = c.generatedPosition() - (codeLengthPos + 4);
|
|
c.randomAccessWriteU4(codeLengthPos, newCodeLength);
|
|
if (verbose) {
|
|
System.out.println("code length old: " + codeLength +
|
|
", new: " + newCodeLength);
|
|
}
|
|
|
|
ib.copyExceptionTable();
|
|
|
|
int attrCount = c.copyU2();
|
|
for (int i = 0; i < attrCount; ++i) {
|
|
copyAttrForCode(ib);
|
|
}
|
|
|
|
// fix up attr length
|
|
int newAttrLength = c.generatedPosition() - (attrLengthPos + 4);
|
|
c.randomAccessWriteU4(attrLengthPos, newAttrLength);
|
|
if (verbose) {
|
|
System.out.println("attr length old: " + attrLength +
|
|
", new: " + newAttrLength);
|
|
}
|
|
}
|
|
|
|
int nextDescriptorIndex(String descriptor, int index) {
|
|
switch (descriptor.charAt(index)) {
|
|
case 'B': // byte
|
|
case 'C': // char
|
|
case 'I': // int
|
|
case 'S': // short
|
|
case 'Z': // boolean
|
|
case 'F': // float
|
|
case 'D': // double
|
|
case 'J': // long
|
|
return index + 1;
|
|
case 'L': // object
|
|
int i = index + 1;
|
|
while (descriptor.charAt(i) != ';') {
|
|
++i;
|
|
}
|
|
return i + 1;
|
|
case '[': // array
|
|
return nextDescriptorIndex(descriptor, index + 1);
|
|
}
|
|
throw new InternalError("should not reach here");
|
|
}
|
|
|
|
int getWrappedTrackerIndex() {
|
|
if (wrappedTrackerIndex == 0) {
|
|
wrappedTrackerIndex = addMethodToConstantPool(profiler,
|
|
options.wrappedTrackerMethodName,
|
|
"(Ljava/lang/String;I)V");
|
|
}
|
|
return wrappedTrackerIndex;
|
|
}
|
|
|
|
int getThisClassIndex() {
|
|
if (thisClassIndex == 0) {
|
|
thisClassIndex = addClassToConstantPool(className);
|
|
}
|
|
return thisClassIndex;
|
|
}
|
|
|
|
int computeMaxLocals(String descriptor, int accessFlags) {
|
|
int index = 1;
|
|
int slot = 0;
|
|
|
|
if ((accessFlags & ACC_STATIC) == 0) {
|
|
++slot;
|
|
}
|
|
char type;
|
|
while ((type = descriptor.charAt(index)) != ')') {
|
|
switch (type) {
|
|
case 'B': // byte
|
|
case 'C': // char
|
|
case 'I': // int
|
|
case 'S': // short
|
|
case 'Z': // boolean
|
|
case 'F': // float
|
|
case 'L': // object
|
|
case '[': // array
|
|
++slot;
|
|
break;
|
|
case 'D': // double
|
|
case 'J': // long
|
|
slot += 2;
|
|
break;
|
|
}
|
|
index = nextDescriptorIndex(descriptor, index);
|
|
}
|
|
|
|
return slot;
|
|
}
|
|
|
|
|
|
void createWrapperCodeAttr(int methodNameIndex, int accessFlags,
|
|
String descriptor, int wrappedIndex) {
|
|
int maxLocals = computeMaxLocals(descriptor, accessFlags);
|
|
|
|
c.writeU2(c.codeAttributeIndex); //
|
|
int attrLengthPos = c.generatedPosition();
|
|
c.writeU4(0); // attr len -- fix up below
|
|
c.writeU2(maxLocals + 4); // max stack
|
|
c.writeU2(maxLocals); // max locals
|
|
int codeLengthPos = c.generatedPosition();
|
|
c.writeU4(0); // code length -- fix up below
|
|
|
|
int methodStringIndex = writeCPEntryString(methodNameIndex);
|
|
|
|
c.writeU1(opc_ldc_w);
|
|
c.writeU2(methodStringIndex); // send the method name
|
|
c.writeU1(opc_sipush);
|
|
c.writeU2(options.fixedIndex);
|
|
c.writeU1(opc_invokestatic);
|
|
c.writeU2(getWrappedTrackerIndex());
|
|
|
|
// set-up args
|
|
int index = 1;
|
|
int slot = 0;
|
|
if ((accessFlags & ACC_STATIC) == 0) {
|
|
c.writeU1(opc_aload_0); // this
|
|
++slot;
|
|
}
|
|
char type;
|
|
while ((type = descriptor.charAt(index)) != ')') {
|
|
switch (type) {
|
|
case 'B': // byte
|
|
case 'C': // char
|
|
case 'I': // int
|
|
case 'S': // short
|
|
case 'Z': // boolean
|
|
c.writeU1(opc_iload);
|
|
c.writeU1(slot);
|
|
++slot;
|
|
break;
|
|
case 'F': // float
|
|
c.writeU1(opc_fload);
|
|
c.writeU1(slot);
|
|
++slot;
|
|
break;
|
|
case 'D': // double
|
|
c.writeU1(opc_dload);
|
|
c.writeU1(slot);
|
|
slot += 2;
|
|
break;
|
|
case 'J': // long
|
|
c.writeU1(opc_lload);
|
|
c.writeU1(slot);
|
|
slot += 2;
|
|
break;
|
|
case 'L': // object
|
|
case '[': // array
|
|
c.writeU1(opc_aload);
|
|
c.writeU1(slot);
|
|
++slot;
|
|
break;
|
|
}
|
|
index = nextDescriptorIndex(descriptor, index);
|
|
}
|
|
|
|
// call the wrapped version
|
|
if ((accessFlags & ACC_STATIC) == 0) {
|
|
c.writeU1(opc_invokevirtual);
|
|
} else {
|
|
c.writeU1(opc_invokestatic);
|
|
}
|
|
c.writeU2(wrappedIndex);
|
|
|
|
// return correct type
|
|
switch (descriptor.charAt(index+1)) {
|
|
case 'B': // byte
|
|
case 'C': // char
|
|
case 'I': // int
|
|
case 'S': // short
|
|
case 'Z': // boolean
|
|
c.writeU1(opc_ireturn);
|
|
break;
|
|
case 'F': // float
|
|
c.writeU1(opc_freturn);
|
|
break;
|
|
case 'D': // double
|
|
c.writeU1(opc_dreturn);
|
|
break;
|
|
case 'J': // long
|
|
c.writeU1(opc_lreturn);
|
|
break;
|
|
case 'L': // object
|
|
case '[': // array
|
|
c.writeU1(opc_areturn);
|
|
break;
|
|
case 'V': // void
|
|
c.writeU1(opc_return);
|
|
break;
|
|
}
|
|
|
|
// end of code
|
|
|
|
// fix up code length
|
|
int newCodeLength = c.generatedPosition() - (codeLengthPos + 4);
|
|
c.randomAccessWriteU4(codeLengthPos, newCodeLength);
|
|
|
|
c.writeU2(0); // exception table length
|
|
c.writeU2(0); // attribute count
|
|
|
|
// fix up attr length
|
|
int newAttrLength = c.generatedPosition() - (attrLengthPos + 4);
|
|
c.randomAccessWriteU4(attrLengthPos, newAttrLength);
|
|
}
|
|
|
|
|
|
int addClassToConstantPool(String className) {
|
|
int prevSection = c.setSection(0);
|
|
int classNameIndex = writeCPEntryUtf8(className);
|
|
int classIndex = writeCPEntryClass(classNameIndex);
|
|
c.setSection(prevSection);
|
|
return classIndex;
|
|
}
|
|
|
|
int addMethodToConstantPool(int classIndex,
|
|
String methodName,
|
|
String descr) {
|
|
int prevSection = c.setSection(0);
|
|
int methodNameIndex = writeCPEntryUtf8(methodName);
|
|
int descrIndex = writeCPEntryUtf8(descr);
|
|
c.setSection(prevSection);
|
|
return addMethodToConstantPool(classIndex, methodNameIndex, descrIndex);
|
|
}
|
|
|
|
int addMethodToConstantPool(int classIndex,
|
|
int methodNameIndex,
|
|
int descrIndex) {
|
|
int prevSection = c.setSection(0);
|
|
int nameAndTypeIndex = writeCPEntryNameAndType(methodNameIndex,
|
|
descrIndex);
|
|
int methodIndex = writeCPEntryMethodRef(classIndex, nameAndTypeIndex);
|
|
c.setSection(prevSection);
|
|
return methodIndex;
|
|
}
|
|
|
|
int writeCPEntryUtf8(String str) {
|
|
int prevSection = c.setSection(0);
|
|
int len = str.length();
|
|
c.writeU1(CONSTANT_UTF8); // Utf8 tag
|
|
c.writeU2(len);
|
|
for (int i = 0; i < len; ++i) {
|
|
c.writeU1(str.charAt(i));
|
|
}
|
|
c.setSection(prevSection);
|
|
return constantPoolCount++;
|
|
}
|
|
|
|
int writeCPEntryString(int utf8Index) {
|
|
int prevSection = c.setSection(0);
|
|
c.writeU1(CONSTANT_STRING);
|
|
c.writeU2(utf8Index);
|
|
c.setSection(prevSection);
|
|
return constantPoolCount++;
|
|
}
|
|
|
|
int writeCPEntryClass(int classNameIndex) {
|
|
int prevSection = c.setSection(0);
|
|
c.writeU1(CONSTANT_CLASS);
|
|
c.writeU2(classNameIndex);
|
|
c.setSection(prevSection);
|
|
return constantPoolCount++;
|
|
}
|
|
|
|
int writeCPEntryNameAndType(int nameIndex, int descrIndex) {
|
|
int prevSection = c.setSection(0);
|
|
c.writeU1(CONSTANT_NAMEANDTYPE);
|
|
c.writeU2(nameIndex);
|
|
c.writeU2(descrIndex);
|
|
c.setSection(prevSection);
|
|
return constantPoolCount++;
|
|
}
|
|
|
|
int writeCPEntryMethodRef(int classIndex, int nameAndTypeIndex) {
|
|
int prevSection = c.setSection(0);
|
|
c.writeU1(CONSTANT_METHOD);
|
|
c.writeU2(classIndex);
|
|
c.writeU2(nameAndTypeIndex);
|
|
c.setSection(prevSection);
|
|
return constantPoolCount++;
|
|
}
|
|
}
|