bytecodemodifier.java

来自「纯java操作系统jnode,安装简单和操作简单的个人使用的Java操作系统」· Java 代码 · 共 428 行

JAVA
428
字号
/*
 * $Id: BytecodeModifier.java,v 1.1 2003/11/25 11:42:13 epr Exp $
 */
package org.jnode.vm.bytecode;

import java.util.List;

import org.jnode.vm.classmgr.VmByteCode;
import org.jnode.vm.classmgr.VmInterpretedExceptionHandler;
import org.jnode.vm.classmgr.VmLineNumberMap;

/**
 * @author Ewout Prangsma (epr@users.sourceforge.net)
 */
public class BytecodeModifier {

	private final VmByteCode byteCode;
	private VmInterpretedExceptionHandler[] eTable;
	private char[] lnTable;
	private byte[] newBc;

	/**
	 * @return The new code
	 */
	public final byte[] getNewCode() {
		return this.newBc;
	}

	/**
	 * Create a new instance
	 * @param byteCode
	 */
	public BytecodeModifier(VmByteCode byteCode) {
		this.byteCode = byteCode;
		this.newBc = byteCode.getBytecode();
		final List eTableList = byteCode.getExceptionHandlers();
		if (eTableList.isEmpty()) {
			this.eTable = null;
		} else {
			eTable = (VmInterpretedExceptionHandler[]) eTableList.toArray(new VmInterpretedExceptionHandler[eTableList.size()]);
		}
		final VmLineNumberMap lnMap = byteCode.getLineNrs();
		if (lnMap == null) {
			this.lnTable = null;
		} else {
			final int len = lnMap.getLength();
			this.lnTable = new char[len * 2];
			for (int i = 0; i < len; i++) {
				lnTable[i * 2 + 0] = (char) lnMap.getStartPCAt(i);
				lnTable[i * 2 + 1] = (char) lnMap.getLineNrAt(i);
			}
		}
	}

	/**
	 * Insert the given src bytes into a clone of the given byteCode
	 * at offset bcOfs, leave a portion of the original bytecode out
	 * and return the cloned bytecode.
	 * @param bcOfs The offset (in byteCode) from where to insert the new bytecode
	 * @param skipLen The number of bytes to leave out starting from bcOfs
	 * @param src The new bytes
	 * @param srcOfs The offset in src from where to start copying
	 * @param len The length of src
	 * @param newNoLocals
	 * @param newMaxStack
	 */
	public void insert(int bcOfs, int skipLen, byte[] src, int srcOfs, int len, int newNoLocals, int newMaxStack) {
		final byte[] curBc = this.newBc;
		final byte[] newBc = new byte[curBc.length - skipLen + len];

		// Copy the first part
		System.arraycopy(curBc, 0, newBc, 0, bcOfs);
		// Copy/Insert the new part
		if (src != null) {
			System.arraycopy(src, srcOfs, newBc, bcOfs, len);
		}
		// Copy the second part
		System.arraycopy(curBc, bcOfs + skipLen, newBc, bcOfs + len, curBc.length - (bcOfs + skipLen));

		final int delta = len - skipLen;

		// Create the new exception table
		adjustExceptionTable(bcOfs, delta);

		// Create the new line number table
		adjustLineNrTable(bcOfs, delta);

		// Create the new VmByteCode
		final VmByteCode newByteCode = new VmByteCode(byteCode.getMethod(), newBc, newNoLocals, newMaxStack, eTable, new VmLineNumberMap(lnTable));

		// Adjust all jump addresses after bcOfs
		final BytecodeJumpModifier modifier = new BytecodeJumpModifier(newBc, bcOfs, delta);
		BytecodeParser.parse(newByteCode, modifier);

		// And we're done
		this.newBc = newBc;
	}

	/**
	 * Remove a number of code bytes from the given bcOfs offset.
	 * 
	 * @param bcOfs The offset (in byteCode) from where to remove the bytecode
	 * @param len The number of bytes to remove
	 * @param newNoLocals
	 * @param newMaxStack
	 */
	public void remove(int bcOfs, int len, int newNoLocals, int newMaxStack) {
		insert(bcOfs, len, null, 0, 0, newNoLocals, newMaxStack);
	}

	/**
	 * Create the modified bytecode structure
	 * @param newNoLocals
	 * @param newMaxStack
	 * @return The bytecode
	 */
	public VmByteCode toByteCode(int newNoLocals, int newMaxStack) {
		return new VmByteCode(byteCode.getMethod(), newBc, newNoLocals, newMaxStack, eTable, new VmLineNumberMap(lnTable));
	}

	/**
	 * Create a new exception table with modified pointers.
	 * All pointers that are >= bcOfs are incremented by delta.
	 * @param bcOfs
	 * @param delta
	 */
	private void adjustExceptionTable(int bcOfs, int delta) {
		if (eTable != null) {
			final int len = eTable.length;
			for (int i = 0; i < len; i++) {
				final VmInterpretedExceptionHandler h = eTable[i];
				if ((h.getStartPC() >= bcOfs) || (h.getEndPC() >= bcOfs) || (h.getHandlerPC() >= bcOfs)) {
					eTable[i] =
						new VmInterpretedExceptionHandler(
							h.getCatchType(),
							adjust(h.getStartPC(), bcOfs, delta),
							adjust(h.getEndPC(), bcOfs, delta),
							adjust(h.getHandlerPC(), bcOfs, delta));
				}
			}
		}
	}

	/**
	 * Create a new line number table with modified start pc pointers.
	 * All pointers that are >= bcOfs are incremented by delta.
	 * @param bcOfs
	 * @param delta
	 */
	private void adjustLineNrTable(int bcOfs, int delta) {
		if (lnTable != null) {
			final int len = lnTable.length;
			for (int i = 0; i < len; i += 2) {
				lnTable[i] = (char) adjust(lnTable[i], bcOfs, delta);
			}
		}
	}

	private final int adjust(int ofs, int bcOfs, int delta) {
		if (ofs >= bcOfs) {
			return ofs + delta;
		} else {
			return ofs;
		}
	}

	static class BytecodeJumpModifier extends BytecodeVisitorSupport {

		private final int bcOfs;
		private final int delta;
		private final byte[] bytecode;
		private BytecodeParser parser;

		public BytecodeJumpModifier(byte[] bytecode, int bcOfs, int delta) {
			this.bcOfs = bcOfs;
			this.delta = delta;
			this.bytecode = bytecode;
		}

		/**
		 * Adjust a 16-bit address at the given offset from the start of
		 * the current instruction.
		 * @param address
		 * @param offset
		 */
		private void adjustBranch16(int address, int offset) {
			final int newAddress = getNewBranchAddress(address);
			if (newAddress != address) {
				final int curAddress = getParser().getAddress();
				final int target = newAddress - curAddress;
				bytecode[curAddress + offset + 0] = (byte) ((target >> 8) & 0xFF);
				bytecode[curAddress + offset + 1] = (byte) (target & 0xFF);
			}
		}

		/**
		 * Adjust a 32-bit address at the given offset from the start of
		 * the current instruction.
		 * @param address
		 * @param offset
		 */
		private void adjustBranch32(int address, int offset) {
			final int newAddress = getNewBranchAddress(address);
			if (newAddress != address) {
				final int curAddress = getParser().getAddress();
				final int target = newAddress - curAddress;
				bytecode[curAddress + offset + 0] = (byte) ((target >> 24) & 0xFF);
				bytecode[curAddress + offset + 1] = (byte) ((target >> 16) & 0xFF);
				bytecode[curAddress + offset + 2] = (byte) ((target >> 8) & 0xFF);
				bytecode[curAddress + offset + 3] = (byte) (target & 0xFF);
			}
		}

		/**
		 * Calculate the new address 
		 * the current instruction.
		 * @param address
		 * @return int
		 */
		private int getNewBranchAddress(int address) {
			final int curAddress = getParser().getAddress();
			final int target = curAddress + address;
			final int newAddress;
			if ((curAddress < bcOfs) && (target >= bcOfs)) {
				// Jump over the inserted area
				newAddress = address + delta;
			} else if ((curAddress >= bcOfs) && (target < bcOfs)) {
				// Jump back over the insert data
				newAddress = address - delta;
			} else {
				newAddress = address;
			}
			return newAddress;
		}

		/**
		 * @param parser
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#setParser(org.jnode.vm.bytecode.BytecodeParser)
		 */
		public void setParser(BytecodeParser parser) {
			this.parser = parser;
			super.setParser(parser);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_goto(int)
		 */
		public void visit_goto(int address) {
			final int opcode = parser.getOpcode();
			if (opcode == 0xA7) {
				adjustBranch16(address, 1);
			} else if (opcode == 0xC8) {
				adjustBranch32(address, 1);
			}
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_acmpeq(int)
		 */
		public void visit_if_acmpeq(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_acmpne(int)
		 */
		public void visit_if_acmpne(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_icmpeq(int)
		 */
		public void visit_if_icmpeq(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_icmpge(int)
		 */
		public void visit_if_icmpge(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_icmpgt(int)
		 */
		public void visit_if_icmpgt(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_icmple(int)
		 */
		public void visit_if_icmple(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_icmplt(int)
		 */
		public void visit_if_icmplt(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_icmpne(int)
		 */
		public void visit_if_icmpne(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifeq(int)
		 */
		public void visit_ifeq(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifge(int)
		 */
		public void visit_ifge(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifgt(int)
		 */
		public void visit_ifgt(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifle(int)
		 */
		public void visit_ifle(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iflt(int)
		 */
		public void visit_iflt(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifne(int)
		 */
		public void visit_ifne(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifnonnull(int)
		 */
		public void visit_ifnonnull(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifnull(int)
		 */
		public void visit_ifnull(int address) {
			adjustBranch16(address, 1);
		}

		/**
		 * @param address
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_jsr(int)
		 */
		public void visit_jsr(int address) {
			final int opcode = parser.getOpcode();
			if (opcode == 0xA8) {
				adjustBranch16(address, 1);
			} else if (opcode == 0xC9) {
				adjustBranch32(address, 1);
			}
		}

		/**
		 * @param defValue
		 * @param matchValues
		 * @param addresses
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lookupswitch(int, int[], int[])
		 */
		public void visit_lookupswitch(int defValue, int[] matchValues, int[] addresses) {
			final int offset = 9 + (parser.getPaddedAddress() - parser.getAddress());
			for (int i = 0; i < matchValues.length; i++) {
				adjustBranch32(addresses[i], offset + (i * 8) + 4);
			}
		}

		/**
		 * @param defValue
		 * @param lowValue
		 * @param highValue
		 * @param addresses
		 * @see org.jnode.vm.bytecode.BytecodeVisitor#visit_tableswitch(int, int, int, int[])
		 */
		public void visit_tableswitch(int defValue, int lowValue, int highValue, int[] addresses) {
			final int offset = 13 + (parser.getPaddedAddress() - parser.getAddress());
			for (int i = 0; i < addresses.length; i++) {
				adjustBranch32(addresses[i], offset + (i * 4));
			}
		}

	}
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?