/******************************************************************************* * Copyright (c) 2014 Jeff Martin. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html * * Contributors: * Jeff Martin - initial API and implementation ******************************************************************************/ package cuchaz.enigma.bytecode; import java.util.Iterator; import javassist.bytecode.BadBytecode; import javassist.bytecode.Bytecode; import javassist.bytecode.CodeAttribute; import javassist.bytecode.CodeIterator; import javassist.bytecode.Opcode; public class BytecodeIndexIterator implements Iterator { public static class Index { private CodeIterator m_iter; private int m_pos; private boolean m_isWide; protected Index(CodeIterator iter, int pos, boolean isWide) { m_iter = iter; m_pos = pos; m_isWide = isWide; } public int getIndex() { if (m_isWide) { return m_iter.s16bitAt(m_pos); } else { return m_iter.byteAt(m_pos); } } public void setIndex(int val) throws BadBytecode { if (m_isWide) { m_iter.write16bit(val, m_pos); } else { if (val < 256) { // we can write the byte m_iter.writeByte(val, m_pos); } else { // we need to upgrade this instruction to LDC_W assert (m_iter.byteAt(m_pos - 1) == Opcode.LDC); m_iter.insertGap(m_pos - 1, 1); m_iter.writeByte(Opcode.LDC_W, m_pos - 1); m_iter.write16bit(val, m_pos); m_isWide = true; // move the iterator to the next opcode m_iter.move(m_pos + 2); } } // sanity check assert (val == getIndex()); } public boolean isValid(Bytecode bytecode) { return getIndex() >= 0 && getIndex() < bytecode.getConstPool().getSize(); } } private Bytecode m_bytecode; private CodeAttribute m_attribute; private CodeIterator m_iter; private Index m_next; public BytecodeIndexIterator(Bytecode bytecode) throws BadBytecode { m_bytecode = bytecode; m_attribute = bytecode.toCodeAttribute(); m_iter = m_attribute.iterator(); m_next = getNext(); } @Override public boolean hasNext() { return m_next != null; } @Override public Index next() { Index out = m_next; try { m_next = getNext(); } catch (BadBytecode ex) { throw new Error(ex); } return out; } @Override public void remove() { throw new UnsupportedOperationException(); } private Index getNext() throws BadBytecode { while (m_iter.hasNext()) { int pos = m_iter.next(); int opcode = m_iter.byteAt(pos); switch (opcode) { // for only these opcodes, the next two bytes are a const pool reference case Opcode.ANEWARRAY: case Opcode.CHECKCAST: case Opcode.INSTANCEOF: case Opcode.INVOKEDYNAMIC: case Opcode.INVOKEINTERFACE: case Opcode.INVOKESPECIAL: case Opcode.INVOKESTATIC: case Opcode.INVOKEVIRTUAL: case Opcode.LDC_W: case Opcode.LDC2_W: case Opcode.MULTIANEWARRAY: case Opcode.NEW: case Opcode.PUTFIELD: case Opcode.PUTSTATIC: case Opcode.GETFIELD: case Opcode.GETSTATIC: return new Index(m_iter, pos + 1, true); case Opcode.LDC: return new Index(m_iter, pos + 1, false); } } return null; } public Iterable indices() { return new Iterable() { @Override public Iterator iterator() { return BytecodeIndexIterator.this; } }; } public void saveChangesToBytecode() { BytecodeTools.setBytecode(m_bytecode, m_attribute.getCode()); } }