d2b1e20c7d
Reviewed-by: dholmes
770 lines
26 KiB
Java
770 lines
26 KiB
Java
/*
|
|
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/*
|
|
* An extension of BinaryCode that allows code to be printed.
|
|
* Includes printing of disassembled byte codes, exception info,
|
|
* local variable and line number info.
|
|
*
|
|
*/
|
|
|
|
package ilib;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.CharArrayWriter;
|
|
import java.io.DataInputStream;
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.io.PrintStream;
|
|
import java.util.Map;
|
|
import java.util.HashMap;
|
|
|
|
class InjectBytecodes implements RuntimeConstants {
|
|
|
|
private final ClassReaderWriter c;
|
|
private final PrintStream output;
|
|
private final int length;
|
|
private final int[] map;
|
|
private final byte[] widening;
|
|
private final Injector[] before = new Injector[256];
|
|
private final Injector[] after = new Injector[256];
|
|
private final String className;
|
|
private final String methodName;
|
|
private final Map<Integer,byte[]> snippets = new HashMap<>();
|
|
|
|
private int pos;
|
|
private int newPos;
|
|
|
|
private class Span {
|
|
final int delta;
|
|
final int target;
|
|
final int newDelta;
|
|
final int newTarget;
|
|
|
|
Span(int delta) {
|
|
this.delta = delta;
|
|
this.target = pos + delta;
|
|
this.newTarget = map[target];
|
|
this.newDelta = newTarget - newPos;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
InjectBytecodes(ClassReaderWriter c, int length,
|
|
String className, String methodName) {
|
|
this.c = c;
|
|
this.output = System.out;
|
|
this.length = length;
|
|
this.map = new int[length + 1];
|
|
this.widening = new byte[length + 1];
|
|
this.className = className;
|
|
this.methodName = methodName;
|
|
c.markLocalPositionStart();
|
|
for (int i = 0; i <= length; ++i) {
|
|
map[i] = i;
|
|
}
|
|
}
|
|
|
|
public void inject(int at, byte[] newCode) {
|
|
snippets.put(new Integer(at), newCode);
|
|
trace("external ");
|
|
inject(at, newCode.length);
|
|
}
|
|
|
|
private void inject(int at, int len) {
|
|
if (Inject.verbose) {
|
|
traceln("Injecting " + len + " at " + at);
|
|
}
|
|
for (int i = at; i <= length; ++i) {
|
|
map[i] += len;
|
|
}
|
|
}
|
|
|
|
private void widen(int at, int len) {
|
|
int delta = len - widening[at];
|
|
if (Inject.verbose) {
|
|
traceln();
|
|
traceln("Widening to " + len + " at " + at);
|
|
}
|
|
inject(c.localPosition(), delta); // inject at end of instruction
|
|
widening[at] = (byte)len; // mark at beginning of instruction
|
|
}
|
|
|
|
public void injectBefore(int code, Injector inj) {
|
|
before[code] = inj;
|
|
}
|
|
|
|
public void injectAfter(int code, Injector inj) {
|
|
after[code] = inj;
|
|
}
|
|
|
|
private void trace(String str) {
|
|
if (Inject.verbose) {
|
|
output.print(str);
|
|
}
|
|
}
|
|
|
|
private void traceln(String str) {
|
|
if (Inject.verbose) {
|
|
output.println(str);
|
|
}
|
|
}
|
|
|
|
private void traceln() {
|
|
if (Inject.verbose) {
|
|
output.println();
|
|
}
|
|
}
|
|
|
|
private void trace(int i) {
|
|
if (Inject.verbose) {
|
|
output.print(i);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Print an integer so that it takes 'length' characters in
|
|
* the output. Temporary until formatting code is stable.
|
|
*/
|
|
private void traceFixedWidthInt(int x, int length) {
|
|
if (Inject.verbose) {
|
|
CharArrayWriter baStream = new CharArrayWriter();
|
|
PrintWriter pStream = new PrintWriter(baStream);
|
|
pStream.print(x);
|
|
String str = baStream.toString();
|
|
for (int cnt = length - str.length(); cnt > 0; --cnt)
|
|
trace(" ");
|
|
trace(str);
|
|
}
|
|
}
|
|
|
|
void adjustOffsets() throws IOException {
|
|
if (Inject.verbose) {
|
|
traceln();
|
|
traceln("Method " + methodName);
|
|
traceln();
|
|
}
|
|
c.rewind();
|
|
while (c.localPosition() < length) {
|
|
insertAtInstruction();
|
|
}
|
|
trace("Searching for adjustments...");
|
|
c.rewind();
|
|
while (c.localPosition() < length) {
|
|
if (!adjustInstruction()) {
|
|
c.rewind();
|
|
traceln();
|
|
traceln("Restarting adjustments after change...");
|
|
}
|
|
}
|
|
// write the new bytecodes
|
|
traceln();
|
|
traceln();
|
|
trace("Writing new code...");
|
|
c.rewind();
|
|
while (c.localPosition() < length) {
|
|
writeInstruction();
|
|
}
|
|
if (!snippets.isEmpty()) {
|
|
throw new Error("not all snippets written");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Walk one instruction inserting instrumentation at specified instructions
|
|
*/
|
|
private void insertAtInstruction() throws IOException {
|
|
pos = c.localPosition();
|
|
int opcode = c.readU1();
|
|
if (opcode == opc_wide) {
|
|
// no support for instrumenting wide instructions
|
|
int wopcode = c.readU1();
|
|
int lvIndex = c.readU2();
|
|
switch (wopcode) {
|
|
case opc_aload: case opc_astore:
|
|
case opc_fload: case opc_fstore:
|
|
case opc_iload: case opc_istore:
|
|
case opc_lload: case opc_lstore:
|
|
case opc_dload: case opc_dstore:
|
|
case opc_ret:
|
|
break;
|
|
|
|
case opc_iinc:
|
|
c.readS2();
|
|
break;
|
|
default:
|
|
throw new Error("Invalid wide opcode: " + wopcode);
|
|
}
|
|
} else {
|
|
Injector inj;
|
|
|
|
inj = before[opcode];
|
|
if (inj != null) {
|
|
inject(pos, inj.bytecodes(className, methodName, pos));
|
|
}
|
|
|
|
switch (opcode) {
|
|
case opc_tableswitch:{
|
|
int header = (pos+1+3) & (~3); // 4byte boundry
|
|
c.skip(header - (pos+1)); // skip old padding
|
|
|
|
c.readU4();
|
|
int low = c.readU4();
|
|
int high = c.readU4();
|
|
c.skip((high+1-low) * 4);
|
|
break;
|
|
}
|
|
|
|
case opc_lookupswitch:{
|
|
int header = (pos+1+3) & (~3); // 4byte boundry
|
|
c.skip(header - (pos+1)); // skip padding
|
|
|
|
c.readU4();
|
|
int npairs = c.readU4();
|
|
c.skip(npairs * 8);
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
int instrLen = opcLengths[opcode];
|
|
c.skip(instrLen-1);
|
|
}
|
|
}
|
|
inj = after[opcode];
|
|
if (inj != null) {
|
|
pos = c.localPosition();
|
|
inject(pos, inj.bytecodes(className, methodName, pos));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Walk one instruction adjusting for insertions
|
|
*/
|
|
private boolean adjustInstruction() throws IOException {
|
|
pos = c.localPosition();
|
|
newPos = map[pos];
|
|
int opcode = c.readU1();
|
|
if (Inject.verbose) {
|
|
traceln();
|
|
traceFixedWidthInt(pos, 4);
|
|
traceFixedWidthInt(newPos, 4);
|
|
trace(" ");
|
|
}
|
|
if (opcode == opc_wide) {
|
|
int wopcode = c.readU1();
|
|
int lvIndex = c.readU2();
|
|
if (Inject.verbose) {
|
|
trace(opcNames[wopcode] + "_w ");
|
|
}
|
|
switch (wopcode) {
|
|
case opc_aload: case opc_astore:
|
|
case opc_fload: case opc_fstore:
|
|
case opc_iload: case opc_istore:
|
|
case opc_lload: case opc_lstore:
|
|
case opc_dload: case opc_dstore:
|
|
case opc_ret:
|
|
trace(lvIndex);
|
|
break;
|
|
|
|
case opc_iinc:
|
|
int constVal = c.readS2();
|
|
if (Inject.verbose) {
|
|
trace(lvIndex + " " + constVal);
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error("Invalid wide opcode: " + wopcode);
|
|
}
|
|
} else {
|
|
if (Inject.verbose) {
|
|
trace(opcNames[opcode]);
|
|
}
|
|
switch (opcode) {
|
|
|
|
case opc_tableswitch:{
|
|
int widened = widening[pos];
|
|
int header = (pos+1+3) & (~3); // 4byte boundry
|
|
int newHeader = (newPos+1+3) & (~3); // 4byte boundry
|
|
|
|
c.skip(header - (pos+1)); // skip old padding
|
|
|
|
Span defaultSkip = new Span(c.readU4());
|
|
int low = c.readU4();
|
|
int high = c.readU4();
|
|
if (Inject.verbose) {
|
|
trace(" " + low + " to " + high);
|
|
trace(": default= [was] " + defaultSkip.target);
|
|
trace(" [now] " + defaultSkip.newTarget);
|
|
for (int i = low; i <= high; ++i) {
|
|
Span jump = new Span(c.readU4());
|
|
traceln("");
|
|
trace('\t');
|
|
traceFixedWidthInt(i, 5);
|
|
trace(": " + jump.newTarget);
|
|
}
|
|
} else {
|
|
c.skip((high+1-low) * 4);
|
|
}
|
|
int newPadding = newHeader - newPos;
|
|
int oldPadding = header - pos;
|
|
int deltaPadding = newPadding - oldPadding;
|
|
if (widened != deltaPadding) {
|
|
widen(pos, deltaPadding);
|
|
return false; // cause restart
|
|
}
|
|
break;
|
|
}
|
|
|
|
case opc_lookupswitch:{
|
|
int widened = widening[pos];
|
|
int header = (pos+1+3) & (~3); // 4byte boundry
|
|
int newHeader = (newPos+1+3) & (~3); // 4byte boundry
|
|
|
|
c.skip(header - (pos+1)); // skip old padding
|
|
|
|
Span defaultSkip = new Span(c.readU4());
|
|
int npairs = c.readU4();
|
|
if (Inject.verbose) {
|
|
trace(" npairs: " + npairs);
|
|
trace(": default= [was] " + defaultSkip.target);
|
|
trace(" [now] " + defaultSkip.newTarget);
|
|
for (int i = 0; i< npairs; ++i) {
|
|
int match = c.readU4();
|
|
Span jump = new Span(c.readU4());
|
|
traceln("");
|
|
trace('\t');
|
|
traceFixedWidthInt(match, 5);
|
|
trace(": " + jump.newTarget);
|
|
}
|
|
} else {
|
|
c.skip(npairs * 8);
|
|
}
|
|
int newPadding = newHeader - newPos;
|
|
int oldPadding = header - pos;
|
|
int deltaPadding = newPadding - oldPadding;
|
|
if (widened != deltaPadding) {
|
|
widen(pos, deltaPadding);
|
|
return false; // cause restart
|
|
}
|
|
break;
|
|
}
|
|
|
|
case opc_jsr: case opc_goto:
|
|
case opc_ifeq: case opc_ifge: case opc_ifgt:
|
|
case opc_ifle: case opc_iflt: case opc_ifne:
|
|
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge:
|
|
case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt:
|
|
case opc_if_acmpeq: case opc_if_acmpne:
|
|
case opc_ifnull: case opc_ifnonnull: {
|
|
int widened = widening[pos];
|
|
Span jump = new Span(c.readS2());
|
|
if (widened == 0) { // not yet widened
|
|
int newDelta = jump.newDelta;
|
|
if ((newDelta < -32768) || (newDelta > 32767)) {
|
|
switch (opcode) {
|
|
case opc_jsr: case opc_goto:
|
|
widen(pos, 2); // will convert to wide
|
|
break;
|
|
default:
|
|
widen(pos, 5); // will inject goto_w
|
|
break;
|
|
}
|
|
return false; // cause restart
|
|
}
|
|
}
|
|
if (Inject.verbose) {
|
|
trace(" [was] " + jump.target + " ==> " +
|
|
" [now] " + jump.newTarget);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case opc_jsr_w:
|
|
case opc_goto_w: {
|
|
Span jump = new Span(c.readU4());
|
|
if (Inject.verbose) {
|
|
trace(" [was] " + jump.target +
|
|
" [now] " + jump.newTarget);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
int instrLen = opcLengths[opcode];
|
|
c.skip(instrLen-1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return true; // successful return
|
|
}
|
|
|
|
|
|
/**
|
|
* Walk one instruction writing the transformed instruction.
|
|
*/
|
|
private void writeInstruction() throws IOException {
|
|
pos = c.localPosition();
|
|
newPos = map[pos];
|
|
byte[] newCode = snippets.remove(new Integer(pos));
|
|
if (newCode != null) {
|
|
traceln();
|
|
traceFixedWidthInt(pos, 4);
|
|
trace(" ... -- Inserting new code");
|
|
c.writeBytes(newCode);
|
|
}
|
|
int opcode = c.readU1();
|
|
if (Inject.verbose) {
|
|
traceln();
|
|
traceFixedWidthInt(pos, 4);
|
|
traceFixedWidthInt(newPos, 4);
|
|
trace(" ");
|
|
}
|
|
if (opcode == opc_wide) {
|
|
int wopcode = c.readU1();
|
|
int lvIndex = c.readU2();
|
|
if (Inject.verbose) {
|
|
trace(opcNames[wopcode] + "_w ");
|
|
}
|
|
c.writeU1(opcode);
|
|
c.writeU1(wopcode);
|
|
c.writeU2(lvIndex);
|
|
switch (wopcode) {
|
|
case opc_aload: case opc_astore:
|
|
case opc_fload: case opc_fstore:
|
|
case opc_iload: case opc_istore:
|
|
case opc_lload: case opc_lstore:
|
|
case opc_dload: case opc_dstore:
|
|
case opc_ret:
|
|
trace(lvIndex);
|
|
break;
|
|
|
|
case opc_iinc:
|
|
int constVal = c.readS2();
|
|
c.writeU2(constVal); // ??? U vs S
|
|
if (Inject.verbose) {
|
|
trace(lvIndex + " " + constVal);
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error("Invalid wide opcode: " + wopcode);
|
|
}
|
|
} else {
|
|
if (Inject.verbose) {
|
|
trace(opcNames[opcode]);
|
|
}
|
|
switch (opcode) {
|
|
|
|
case opc_tableswitch:{
|
|
int header = (pos+1+3) & (~3); // 4byte boundry
|
|
int newHeader = (newPos+1+3) & (~3); // 4byte boundry
|
|
|
|
c.skip(header - (pos+1)); // skip old padding
|
|
|
|
Span defaultSkip = new Span(c.readU4());
|
|
int low = c.readU4();
|
|
int high = c.readU4();
|
|
|
|
c.writeU1(opcode); // copy instruction
|
|
for (int i = newPos+1; i < newHeader; ++i) {
|
|
c.writeU1(0); // write new padding
|
|
}
|
|
c.writeU4(defaultSkip.newDelta);
|
|
c.writeU4(low);
|
|
c.writeU4(high);
|
|
|
|
if (Inject.verbose) {
|
|
trace(" " + low + " to " + high);
|
|
trace(": default= [was] " + defaultSkip.target);
|
|
trace(" [now] " + defaultSkip.newTarget);
|
|
}
|
|
for (int i = low; i <= high; ++i) {
|
|
Span jump = new Span(c.readU4());
|
|
c.writeU4(jump.newDelta);
|
|
if (Inject.verbose) {
|
|
traceln("");
|
|
trace('\t');
|
|
traceFixedWidthInt(i, 5);
|
|
trace(": " + jump.newTarget);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case opc_lookupswitch:{
|
|
int header = (pos+1+3) & (~3); // 4byte boundry
|
|
int newHeader = (newPos+1+3) & (~3); // 4byte boundry
|
|
|
|
c.skip(header - (pos+1)); // skip old padding
|
|
|
|
Span defaultSkip = new Span(c.readU4());
|
|
int npairs = c.readU4();
|
|
if (Inject.verbose) {
|
|
trace(" npairs: " + npairs);
|
|
trace(": default= [was] " + defaultSkip.target);
|
|
trace(" [now] " + defaultSkip.newTarget);
|
|
}
|
|
c.writeU1(opcode); // copy instruction
|
|
for (int i = newPos+1; i < newHeader; ++i) {
|
|
c.writeU1(0); // write new padding
|
|
}
|
|
c.writeU4(defaultSkip.newDelta);
|
|
c.writeU4(npairs);
|
|
for (int i = 0; i< npairs; ++i) {
|
|
int match = c.readU4();
|
|
Span jump = new Span(c.readU4());
|
|
c.writeU4(match);
|
|
c.writeU4(jump.newDelta);
|
|
if (Inject.verbose) {
|
|
traceln("");
|
|
trace('\t');
|
|
traceFixedWidthInt(match, 5);
|
|
trace(": " + jump.newTarget);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case opc_jsr: case opc_goto:
|
|
case opc_ifeq: case opc_ifge: case opc_ifgt:
|
|
case opc_ifle: case opc_iflt: case opc_ifne:
|
|
case opc_if_icmpeq: case opc_if_icmpne: case opc_if_icmpge:
|
|
case opc_if_icmpgt: case opc_if_icmple: case opc_if_icmplt:
|
|
case opc_if_acmpeq: case opc_if_acmpne:
|
|
case opc_ifnull: case opc_ifnonnull: {
|
|
int widened = widening[pos];
|
|
Span jump = new Span(c.readS2());
|
|
int newOpcode = opcode; // default to unchanged
|
|
if (widened == 0) { // not widened
|
|
c.writeU1(opcode); // rewrite instruction
|
|
c.writeU2(jump.newDelta);
|
|
} else if (widened == 2) { // wide form
|
|
switch (opcode) {
|
|
case opc_jsr:
|
|
newOpcode = opc_jsr_w;
|
|
break;
|
|
case opc_goto:
|
|
newOpcode = opc_jsr_w;
|
|
break;
|
|
default:
|
|
throw new Error("unexpected opcode: " +
|
|
opcode);
|
|
}
|
|
c.writeU1(newOpcode); // write wide instruction
|
|
c.writeU4(jump.newDelta); // write new and wide delta
|
|
} else if (widened == 5) { // insert goto_w
|
|
switch (opcode) {
|
|
case opc_ifeq:
|
|
newOpcode = opc_ifne;
|
|
break;
|
|
case opc_ifge:
|
|
newOpcode = opc_iflt;
|
|
break;
|
|
case opc_ifgt:
|
|
newOpcode = opc_ifle;
|
|
break;
|
|
case opc_ifle:
|
|
newOpcode = opc_ifgt;
|
|
break;
|
|
case opc_iflt:
|
|
newOpcode = opc_ifge;
|
|
break;
|
|
case opc_ifne:
|
|
newOpcode = opc_ifeq;
|
|
break;
|
|
case opc_if_icmpeq:
|
|
newOpcode = opc_if_icmpne;
|
|
break;
|
|
case opc_if_icmpne:
|
|
newOpcode = opc_if_icmpeq;
|
|
break;
|
|
case opc_if_icmpge:
|
|
newOpcode = opc_if_icmplt;
|
|
break;
|
|
case opc_if_icmpgt:
|
|
newOpcode = opc_if_icmple;
|
|
break;
|
|
case opc_if_icmple:
|
|
newOpcode = opc_if_icmpgt;
|
|
break;
|
|
case opc_if_icmplt:
|
|
newOpcode = opc_if_icmpge;
|
|
break;
|
|
case opc_if_acmpeq:
|
|
newOpcode = opc_if_acmpne;
|
|
break;
|
|
case opc_if_acmpne:
|
|
newOpcode = opc_if_acmpeq;
|
|
break;
|
|
case opc_ifnull:
|
|
newOpcode = opc_ifnonnull;
|
|
break;
|
|
case opc_ifnonnull:
|
|
newOpcode = opc_ifnull;
|
|
break;
|
|
default:
|
|
throw new Error("unexpected opcode: " +
|
|
opcode);
|
|
}
|
|
c.writeU1(newOpcode); // write inverse branch
|
|
c.writeU2(3 + 5); // beyond if and goto_w
|
|
c.writeU1(opc_goto_w);// add a goto_w
|
|
c.writeU4(jump.newDelta); // write new and wide delta
|
|
} else {
|
|
throw new Error("unexpected widening");
|
|
}
|
|
|
|
if (Inject.verbose) {
|
|
trace(" [was] " + jump.target + " ==> " +
|
|
opcNames[newOpcode] +
|
|
" [now] " + jump.newTarget);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case opc_jsr_w:
|
|
case opc_goto_w: {
|
|
Span jump = new Span(c.readU4());
|
|
c.writeU1(opcode); // instruction itself
|
|
c.writeU4(jump.newDelta);
|
|
if (Inject.verbose) {
|
|
trace(" [was] " + jump.target +
|
|
" [now] " + jump.newTarget);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
int instrLen = opcLengths[opcode];
|
|
c.writeU1(opcode); // instruction itself
|
|
c.copy(instrLen-1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy the exception table for this method code
|
|
*/
|
|
void copyExceptionTable() throws IOException {
|
|
int tableLength = c.copyU2(); // exception table len
|
|
if (tableLength > 0) {
|
|
traceln();
|
|
traceln("Exception table:");
|
|
traceln(" from:old/new to:old/new target:old/new type");
|
|
for (int tcnt = tableLength; tcnt > 0; --tcnt) {
|
|
int startPC = c.readU2();
|
|
int newStartPC = map[startPC];
|
|
c.writeU2(newStartPC);
|
|
int endPC = c.readU2();
|
|
int newEndPC = map[endPC];
|
|
c.writeU2(newEndPC);
|
|
int handlerPC = c.readU2();
|
|
int newHandlerPC = map[handlerPC];
|
|
c.writeU2(newHandlerPC);
|
|
int catchType = c.copyU2();
|
|
if (Inject.verbose) {
|
|
traceFixedWidthInt(startPC, 6);
|
|
traceFixedWidthInt(newStartPC, 6);
|
|
traceFixedWidthInt(endPC, 6);
|
|
traceFixedWidthInt(newEndPC, 6);
|
|
traceFixedWidthInt(handlerPC, 6);
|
|
traceFixedWidthInt(newHandlerPC, 6);
|
|
trace(" ");
|
|
if (catchType == 0)
|
|
traceln("any");
|
|
else {
|
|
traceln("" + catchType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy the line number table for this method code
|
|
*/
|
|
void copyLineNumberAttr() throws IOException {
|
|
// name index already read
|
|
c.copy(4); // attr len
|
|
int tableLength = c.copyU2(); // line table len
|
|
if (tableLength > 0) {
|
|
if (Inject.verbose) {
|
|
traceln();
|
|
traceln("Line numbers for method " + methodName);
|
|
}
|
|
for (int tcnt = tableLength; tcnt > 0; --tcnt) {
|
|
int startPC = c.readU2();
|
|
int newStartPC = map[startPC];
|
|
c.writeU2(newStartPC);
|
|
int lineNumber = c.copyU2();
|
|
if (Inject.verbose) {
|
|
traceln(" line " + lineNumber +
|
|
": [was] " + startPC +
|
|
" [now] " + newStartPC);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Copy the local variable table for this method code
|
|
*/
|
|
void copyLocalVarAttr() throws IOException {
|
|
// name index already read
|
|
c.copy(4); // attr len
|
|
int tableLength = c.copyU2(); // local var table len
|
|
if (tableLength > 0) {
|
|
if (Inject.verbose) {
|
|
traceln();
|
|
traceln("Local variables for method " + methodName);
|
|
}
|
|
for (int tcnt = tableLength; tcnt > 0; --tcnt) {
|
|
int startPC = c.readU2();
|
|
int newStartPC = map[startPC];
|
|
c.writeU2(newStartPC);
|
|
int length = c.readU2();
|
|
int endPC = startPC + length;
|
|
int newEndPC = map[endPC];
|
|
int newLength = newEndPC - newStartPC;
|
|
c.writeU2(newLength);
|
|
int nameIndex = c.copyU2();
|
|
int descriptorIndex = c.copyU2();
|
|
int index = c.copyU2();
|
|
if (Inject.verbose) {
|
|
trace(" ");
|
|
trace(descriptorIndex);
|
|
trace(" ");
|
|
trace(nameIndex);
|
|
traceln(" pc= [was] " + startPC + " [now] " + newStartPC +
|
|
", length= [was] " + length + " [now] " + newLength +
|
|
", slot=" + index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|