All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jboss.classfilewriter.code.CodeAttribute Maven / Gradle / Ivy

There is a newer version: 3.0.0.Alpha1
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 *
 * Copyright 2012 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jboss.classfilewriter.code;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.jboss.classfilewriter.ClassMethod;
import org.jboss.classfilewriter.InvalidBytecodeException;
import org.jboss.classfilewriter.attributes.Attribute;
import org.jboss.classfilewriter.attributes.StackMapTableAttribute;
import org.jboss.classfilewriter.constpool.ConstPool;
import org.jboss.classfilewriter.util.ByteArrayDataOutputStream;
import org.jboss.classfilewriter.util.DescriptorUtils;
import org.jboss.classfilewriter.util.LazySize;

public class CodeAttribute extends Attribute {

    public static final String NAME = "Code";

    private final ClassMethod method;

    private final ConstPool constPool;

    private final ByteArrayOutputStream finalDataBytes;

    private final DataOutputStream data;

    private int maxLocals = 0;

    private int maxStackDepth = 0;

    private final LinkedHashMap stackFrames = new LinkedHashMap();

    /**
     * maps bytecode offsets to jump locations. As these jump locations where not known when the instruction was written they
     * need to be overwritten when the final bytecode is written out
     */
    private final Map jumpLocations = new HashMap();

    /**
     * maps bytecode offsets to jump locations. As these jump locations where not known when the instruction was written they
     * need to be overwritten when the final bytecode is written out
     * 

* These jump locations are 32 bit offsets, */ private final Map jumpLocations32 = new HashMap(); private StackFrame currentFrame; private int currentOffset; private final List attributes = new ArrayList(); //disable for now, as this has some issues private boolean stackMapAttributeValid = false; private final StackMapTableAttribute stackMapTableAttribute; private final List exceptionTable = new ArrayList(); public CodeAttribute(ClassMethod method, ConstPool constPool) { super(NAME, constPool); this.method = method; this.constPool = constPool; this.finalDataBytes = new ByteArrayOutputStream(); this.data = new DataOutputStream(finalDataBytes); if (!Modifier.isStatic(method.getAccessFlags())) { maxLocals++; } for (String param : method.getParameters()) { if (DescriptorUtils.isWide(param)) { maxLocals += 2; } else { maxLocals++; } } // creates a new initial stack frame currentFrame = new StackFrame(method); stackFrames.put(0, currentFrame); currentOffset = 0; stackMapTableAttribute = new StackMapTableAttribute(method, constPool); } @Override public void writeData(ByteArrayDataOutputStream stream) throws IOException { if (stackMapAttributeValid) { // add the stack map table attributes.add(stackMapTableAttribute); } if (finalDataBytes.size() == 0) { throw new RuntimeException("Code attribute is empty for method " + method.getName() + " " + method.getDescriptor()); } byte[] bytecode = finalDataBytes.toByteArray(); for (Entry e : jumpLocations.entrySet()) { overwriteShort(bytecode, e.getKey(), e.getValue()); } for (Entry e : jumpLocations32.entrySet()) { overwriteInt(bytecode, e.getKey(), e.getValue()); } LazySize size = stream.writeSize(); stream.writeShort(maxStackDepth); stream.writeShort(maxLocals); stream.writeInt(bytecode.length); stream.write(bytecode); stream.writeShort(exceptionTable.size()); // exception table length for (ExceptionHandler exception : exceptionTable) { stream.writeShort(exception.getStart()); stream.writeShort(exception.getEnd()); stream.writeShort(exception.getHandler()); stream.writeShort(exception.getExceptionIndex()); } stream.writeShort(attributes.size()); // attributes count for (Attribute attribute : attributes) { attribute.write(stream); } size.markEnd(); ; } // ------------------------------------------- // Instruction methods, in alphabetical order public void aaload() { assertTypeOnStack(StackEntryType.INT, "aaload requires int on top of stack"); if (!getStack().top_1().getDescriptor().startsWith("[")) { throw new InvalidBytecodeException("aaload needs an array in position 2 on the stack"); } writeByte(Opcode.AALOAD); currentOffset++; advanceFrame(currentFrame.pop2push1("Ljava/lang/Object;")); } public void aastore() { assertTypeOnStack(StackEntryType.OBJECT, "aastore requires reference type on top of stack"); assertTypeOnStack(1, StackEntryType.INT, "aastore requires an int on position 2 stack"); if (!getStack().top_2().getDescriptor().startsWith("[")) { throw new InvalidBytecodeException("aaload needs an array in position 3 on the stack"); } writeByte(Opcode.AASTORE); currentOffset++; advanceFrame(currentFrame.pop3()); } public void aconstNull() { writeByte(Opcode.ACONST_NULL); currentOffset++; advanceFrame(currentFrame.aconstNull()); } public void aload(int no) { LocalVariableState locals = getLocalVars(); if (locals.size() <= no) { throw new InvalidBytecodeException("Cannot load variable at " + no + ". Local Variables: " + locals.toString()); } StackEntry entry = locals.get(no); if (entry.getType() != StackEntryType.OBJECT && entry.getType() != StackEntryType.NULL && entry.getType() != StackEntryType.UNINITIALIZED_THIS && entry.getType() != StackEntryType.UNITITIALIZED_OBJECT) { throw new InvalidBytecodeException("Invalid local variable at location " + no + " Local Variables " + locals.toString()); } if (no > 0xFF) { // wide version writeByte(Opcode.WIDE); writeByte(Opcode.ALOAD); writeShort(no); currentOffset += 4; } else if (no >= 0 && no < 4) { writeByte(Opcode.ALOAD_0 + no); currentOffset++; } else { writeByte(Opcode.ALOAD); writeByte(no); currentOffset += 2; } advanceFrame(currentFrame.push(entry)); } public void anewarray(String arrayType) { assertTypeOnStack(StackEntryType.INT, "anewarray requires int on stack"); int index = constPool.addClassEntry(arrayType); writeByte(Opcode.ANEWARRAY); writeShort(index); currentOffset += 3; if (arrayType.startsWith("[")) { advanceFrame(currentFrame.replace("[" + arrayType)); } else { advanceFrame(currentFrame.replace("[L" + arrayType + ";")); } } public void arraylength() { assertTypeOnStack(StackEntryType.OBJECT, "arraylength requires array on stack"); writeByte(Opcode.ARRAYLENGTH); currentOffset++; advanceFrame(currentFrame.replace("I")); } public void astore(int no) { assertTypeOnStack(StackEntryType.OBJECT, "aastore requires reference type on stack"); if (no > 0xFF) { // wide version writeByte(Opcode.WIDE); writeByte(Opcode.ASTORE); writeShort(no); currentOffset += 4; } else if (no >= 0 && no < 4) { writeByte(Opcode.ASTORE_0 + no); currentOffset++; } else { writeByte(Opcode.ASTORE); writeByte(no); currentOffset += 2; } advanceFrame(currentFrame.store(no)); } public void athrow() { assertTypeOnStack(StackEntryType.OBJECT, "athrow requires an object on the stack"); writeByte(Opcode.ATHROW); currentOffset++; currentFrame = null; } public void baload() { assertTypeOnStack(StackEntryType.INT, "baload requires an int on top of the stack"); assertTypeOnStack(1, StackEntryType.OBJECT, "baload requires an array in position 2 on the stack"); writeByte(Opcode.BALOAD); currentOffset++; advanceFrame(currentFrame.pop2push1("I")); } public void bastore() { assertTypeOnStack(StackEntryType.INT, "bastore requires an int on top of the stack"); assertTypeOnStack(1, StackEntryType.INT, "bastore requires an int in position 2 on the stack"); assertTypeOnStack(2, StackEntryType.OBJECT, "bastore requires an array reference in position 3 on the stack"); writeByte(Opcode.BASTORE); currentOffset++; advanceFrame(currentFrame.pop3()); } public void caload() { assertTypeOnStack(StackEntryType.INT, "caload requires an int on top of the stack"); assertTypeOnStack(1, StackEntryType.OBJECT, "caload requires an array in position 2 on the stack"); writeByte(Opcode.CALOAD); currentOffset++; advanceFrame(currentFrame.pop2push1("I")); } public void castore() { assertTypeOnStack(StackEntryType.INT, "castore requires an int on top of the stack"); assertTypeOnStack(1, StackEntryType.INT, "castore requires an int in position 2 on the stack"); assertTypeOnStack(2, StackEntryType.OBJECT, "castore requires an array reference in position 3 on the stack"); writeByte(Opcode.CASTORE); currentOffset++; advanceFrame(currentFrame.pop3()); } public void bipush(byte value) { writeByte(Opcode.BIPUSH); writeByte(value); currentOffset += 2; advanceFrame(currentFrame.push("B")); } /** * marks the end of a branch. The current stack frame is checked for compatibility with the stack frame at the branch start */ public void branchEnd(BranchEnd end) { mergeStackFrames(end.getStackFrame()); final int jump = currentOffset - end.getOffsetLocation(); if (end.isJump32Bit()) { jumpLocations32.put(end.getBranchLocation(), jump); } else { if(jump > Short.MAX_VALUE) { throw new RuntimeException(jump + " is to big to be written as a 16 bit value"); } jumpLocations.put(end.getBranchLocation(), jump); } } /** * Do not use Descriptor format (e.g. Ljava/lang/Object;), the correct form is just java/lang/Object or java.lang.Object */ public void checkcast(String className) { assertTypeOnStack(StackEntryType.OBJECT, "checkcast requires reference type on stack"); int classIndex = constPool.addClassEntry(className); writeByte(Opcode.CHECKCAST); writeShort(classIndex); currentOffset += 3; advanceFrame(currentFrame.replace(className)); } public void checkcast(Class clazz) { checkcast(clazz.getName()); } public void d2f() { assertTypeOnStack(StackEntryType.DOUBLE, "d2f requires double on stack"); writeByte(Opcode.D2F); currentOffset++; advanceFrame(currentFrame.pop2push1("F")); } public void d2i() { assertTypeOnStack(StackEntryType.DOUBLE, "d2i requires double on stack"); writeByte(Opcode.D2I); currentOffset++; advanceFrame(currentFrame.pop2push1("I")); } public void d2l() { assertTypeOnStack(StackEntryType.DOUBLE, "d2l requires double on stack"); writeByte(Opcode.D2L); currentOffset++; advanceFrame(currentFrame.pop2push1("J")); } public void dadd() { assertTypeOnStack(StackEntryType.DOUBLE, "dadd requires double on stack"); assertTypeOnStack(2, StackEntryType.DOUBLE, "dadd requires double on stack"); writeByte(Opcode.DADD); currentOffset++; advanceFrame(currentFrame.pop2()); } public void daload() { assertTypeOnStack(StackEntryType.INT, "daload requires an int on top of the stack"); assertTypeOnStack(1, StackEntryType.OBJECT, "daload requires an array in position 2 on the stack"); writeByte(Opcode.DALOAD); currentOffset++; advanceFrame(currentFrame.pop2push1("D")); } public void dastore() { assertTypeOnStack(StackEntryType.DOUBLE, "dastore requires an int on top of the stack"); assertTypeOnStack(2, StackEntryType.INT, "dastore requires an int in position 2 on the stack"); assertTypeOnStack(3, StackEntryType.OBJECT, "dastore requires an array reference in position 3 on the stack"); writeByte(Opcode.DASTORE); currentOffset++; advanceFrame(currentFrame.pop4()); } public void dcmpg() { assertTypeOnStack(StackEntryType.DOUBLE, "dcmpg requires double on stack"); assertTypeOnStack(2, StackEntryType.DOUBLE, "dcmpg requires double on stack"); writeByte(Opcode.DCMPG); currentOffset++; advanceFrame(currentFrame.pop4push1("I")); } public void dcmpl() { assertTypeOnStack(StackEntryType.DOUBLE, "dcmpl requires double on stack"); assertTypeOnStack(2, StackEntryType.DOUBLE, "dcmpl requires double in position 3 on stack"); writeByte(Opcode.DCMPL); currentOffset++; advanceFrame(currentFrame.pop4push1("I")); } /** * Adds the appropriate dconst instruction. *

* note, if the value is not 0 or 1 then ldc is used instead */ public void dconst(double value) { if (value == 0.0) { writeByte(Opcode.DCONST_0); } else if (value == 1.0) { writeByte(Opcode.DCONST_1); } else { ldc2(value); return; } currentOffset++; advanceFrame(currentFrame.push("D")); } public void ddiv() { assertTypeOnStack(StackEntryType.DOUBLE, "ddiv requires double on stack"); assertTypeOnStack(2, StackEntryType.DOUBLE, "ddiv requires double in position 3 on stack"); writeByte(Opcode.DDIV); currentOffset++; advanceFrame(currentFrame.pop2()); } public void dload(int no) { LocalVariableState locals = getLocalVars(); if (locals.size() <= no) { throw new InvalidBytecodeException("Cannot load variable at " + no + ". Local Variables: " + locals.toString()); } StackEntry entry = locals.get(no); if (entry.getType() != StackEntryType.DOUBLE) { throw new InvalidBytecodeException("Invalid local variable at location " + no + " Local Variables " + locals.toString()); } if (no > 0xFF) { // wide version writeByte(Opcode.WIDE); writeByte(Opcode.DLOAD); writeShort(no); currentOffset += 4; } else if (no >= 0 && no < 4) { writeByte(Opcode.DLOAD_0 + no); currentOffset++; } else { writeByte(Opcode.DLOAD); writeByte(no); currentOffset += 2; } advanceFrame(currentFrame.push(entry)); } public void dmul() { assertTypeOnStack(StackEntryType.DOUBLE, "dmul requires double on stack"); assertTypeOnStack(2, StackEntryType.DOUBLE, "dmul requires double in position 3 on stack"); writeByte(Opcode.DMUL); currentOffset++; advanceFrame(currentFrame.pop2()); } public void dneg() { assertTypeOnStack(StackEntryType.DOUBLE, "dneg requires double on stack"); writeByte(Opcode.DNEG); currentOffset++; duplicateFrame(); } public void drem() { assertTypeOnStack(StackEntryType.DOUBLE, "drem requires double on stack"); assertTypeOnStack(2, StackEntryType.DOUBLE, "drem requires double in position 3 on stack"); writeByte(Opcode.DREM); currentOffset++; advanceFrame(currentFrame.pop2()); } public void dstore(int no) { assertTypeOnStack(StackEntryType.DOUBLE, "dastore requires double on stack"); if (no > 0xFF) { // wide version writeByte(Opcode.WIDE); writeByte(Opcode.DSTORE); writeShort(no); currentOffset += 4; } else if (no >= 0 && no < 4) { writeByte(Opcode.DSTORE_0 + no); currentOffset++; } else { writeByte(Opcode.DSTORE); writeByte(no); currentOffset += 2; } advanceFrame(currentFrame.store(no)); } public void dsub() { assertTypeOnStack(StackEntryType.DOUBLE, "dsub requires double on stack"); assertTypeOnStack(2, StackEntryType.DOUBLE, "dsub requires double in position 3 on stack"); writeByte(Opcode.DSUB); currentOffset++; advanceFrame(currentFrame.pop2()); } public void dup() { assertNotWideOnStack("dup acnnot be used if double or long is on top of the stack"); writeByte(Opcode.DUP); currentOffset++; advanceFrame(currentFrame.dup()); } public void dupX1() { assertNotWideOnStack("dup_x1 cannot be used if double or long is on top of the stack"); assertNotWideOnStack(1, "dup_x1 cannot be used if double or long is in position 2 on the stack"); writeByte(Opcode.DUP_X1); currentOffset++; advanceFrame(currentFrame.dupX1()); } public void dupX2() { assertNotWideOnStack("dup_x2 acnnot be used if double or long is on top of the stack"); writeByte(Opcode.DUP_X2); currentOffset++; advanceFrame(currentFrame.dupX2()); } public void dup2() { writeByte(Opcode.DUP2); currentOffset++; advanceFrame(currentFrame.dup2()); } public void dup2X1() { assertNotWideOnStack(2, "dup2_x1 cannot be used if double or long is in position 3 on the stack"); writeByte(Opcode.DUP2_X1); currentOffset++; advanceFrame(currentFrame.dup2X1()); } public void dup2X2() { assertNotWideOnStack(3, "dup2_x2 cannot be used if double or long is in position 4 on the stack"); writeByte(Opcode.DUP2_X2); currentOffset++; advanceFrame(currentFrame.dup2X2()); } /** * Begin writing an exception handler block. The handler is not actually persisted until exceptionHandler is called. */ public ExceptionHandler exceptionBlockStart(String exceptionType) { ExceptionHandler handler = new ExceptionHandler(currentOffset, constPool.addClassEntry(exceptionType), exceptionType, currentFrame); return handler; } /** * Mark the end of an exception handler block. The last instruction that was written will be the last instruction covered by * the handler */ public void exceptionBlockEnd(ExceptionHandler handler) { handler.setEnd(currentOffset); } /** * Marks the current code location as the exception handler and adds the handler to the exception handler table; */ public void exceptionHandlerStart(ExceptionHandler handler) { if (handler.getEnd() == 0) { throw new InvalidBytecodeException( "handler end location must be initialised via exceptionHandlerEnd before calling exceptionHandlerAdd"); } handler.setHandler(currentOffset); exceptionTable.add(handler); mergeStackFrames(new StackFrame(new StackState(handler.getExceptionType(), constPool), handler.getFrame() .getLocalVariableState(), StackFrameType.FULL_FRAME)); } public void f2d() { assertTypeOnStack(StackEntryType.FLOAT, "f2s requires float on stack"); writeByte(Opcode.F2D); currentOffset++; advanceFrame(currentFrame.replace("D")); } public void f2i() { assertTypeOnStack(StackEntryType.FLOAT, "f2i requires float on stack"); writeByte(Opcode.F2I); currentOffset++; advanceFrame(currentFrame.replace("I")); } public void f2l() { assertTypeOnStack(StackEntryType.FLOAT, "f2l requires float on stack"); writeByte(Opcode.F2L); currentOffset++; advanceFrame(currentFrame.replace("J")); } public void fadd() { assertTypeOnStack(StackEntryType.FLOAT, "fadd requires float on stack"); assertTypeOnStack(1, StackEntryType.FLOAT, "fadd requires float on stack"); writeByte(Opcode.FADD); currentOffset++; advanceFrame(currentFrame.pop()); } public void faload() { assertTypeOnStack(StackEntryType.INT, "faload requires an int on top of the stack"); assertTypeOnStack(1, StackEntryType.OBJECT, "faload requires an array in position 2 on the stack"); writeByte(Opcode.FALOAD); currentOffset++; advanceFrame(currentFrame.pop2push1("F")); } public void fastore() { assertTypeOnStack(StackEntryType.FLOAT, "fastore requires an int on top of the stack"); assertTypeOnStack(1, StackEntryType.INT, "fastore requires an int in position 2 on the stack"); assertTypeOnStack(2, StackEntryType.OBJECT, "fastore requires an array reference in position 3 on the stack"); writeByte(Opcode.FASTORE); currentOffset++; advanceFrame(currentFrame.pop3()); } public void fcmpg() { assertTypeOnStack(StackEntryType.FLOAT, "fcmpg requires float on stack"); assertTypeOnStack(1, StackEntryType.FLOAT, "fcmpg requires float on stack"); writeByte(Opcode.FCMPG); currentOffset++; advanceFrame(currentFrame.pop2push1("I")); } public void fcmpl() { assertTypeOnStack(StackEntryType.FLOAT, "fcmpl requires float on stack"); assertTypeOnStack(1, StackEntryType.FLOAT, "fcmpl requires float in position 2 on stack"); writeByte(Opcode.FCMPL); currentOffset++; advanceFrame(currentFrame.pop2push1("I")); } /** * Adds the appropriate fconst instruction. *

* note, if the value is not 0, 1, 2 then ldc is used instead */ public void fconst(float value) { if (value == 0) { writeByte(Opcode.FCONST_0); } else if (value == 1) { writeByte(Opcode.FCONST_1); } else if (value == 2) { writeByte(Opcode.FCONST_2); } else { ldc(value); return; } currentOffset++; advanceFrame(currentFrame.push("F")); } public void fdiv() { assertTypeOnStack(StackEntryType.FLOAT, "fdiv requires float on stack"); assertTypeOnStack(1, StackEntryType.FLOAT, "fdiv requires float in position 2 on stack"); writeByte(Opcode.FDIV); currentOffset++; advanceFrame(currentFrame.pop()); } public void fload(int no) { LocalVariableState locals = getLocalVars(); if (locals.size() <= no) { throw new InvalidBytecodeException("Cannot load variable at " + no + ". Local Variables: " + locals.toString()); } StackEntry entry = locals.get(no); if (entry.getType() != StackEntryType.FLOAT) { throw new InvalidBytecodeException("Invalid local variable at location " + no + " Local Variables " + locals.toString()); } if (no > 0xFF) { // wide version writeByte(Opcode.WIDE); writeByte(Opcode.FLOAD); writeShort(no); currentOffset += 4; } else if (no >= 0 && no < 4) { writeByte(Opcode.FLOAD_0 + no); currentOffset++; } else { writeByte(Opcode.FLOAD); writeByte(no); currentOffset += 2; } advanceFrame(currentFrame.push(entry)); } public void fmul() { assertTypeOnStack(StackEntryType.FLOAT, "fmul requires float on stack"); assertTypeOnStack(1, StackEntryType.FLOAT, "fmul requires float in position 2 on stack"); writeByte(Opcode.FMUL); currentOffset++; advanceFrame(currentFrame.pop()); } public void fneg() { assertTypeOnStack(StackEntryType.FLOAT, "fneg requires float on stack"); writeByte(Opcode.FNEG); currentOffset++; duplicateFrame(); } public void frem() { assertTypeOnStack(StackEntryType.FLOAT, "frem requires float on stack"); assertTypeOnStack(1, StackEntryType.FLOAT, "frem requires float in position 2 on stack"); writeByte(Opcode.FREM); currentOffset++; advanceFrame(currentFrame.pop()); } public void fstore(int no) { assertTypeOnStack(StackEntryType.FLOAT, "fstore requires float on stack"); if (no > 0xFF) { // wide version writeByte(Opcode.WIDE); writeByte(Opcode.FSTORE); writeShort(no); currentOffset += 4; } else if (no >= 0 && no < 4) { writeByte(Opcode.FSTORE_0 + no); currentOffset++; } else { writeByte(Opcode.FSTORE); writeByte(no); currentOffset += 2; } advanceFrame(currentFrame.store(no)); } public void fsub() { assertTypeOnStack(StackEntryType.FLOAT, "fsub requires float on stack"); assertTypeOnStack(1, StackEntryType.FLOAT, "fsub requires float in position 2 on stack"); writeByte(Opcode.FSUB); currentOffset++; advanceFrame(currentFrame.pop()); } public void getfield(String className, String field, Class fieldType) { getfield(className, field, DescriptorUtils.makeDescriptor(fieldType)); } public void getfield(String className, String field, String descriptor) { assertTypeOnStack(StackEntryType.OBJECT, "getfield requires object on stack"); int index = constPool.addFieldEntry(className, field, descriptor); writeByte(Opcode.GETFIELD); writeShort(index); currentOffset += 3; advanceFrame(currentFrame.replace(descriptor)); } public void getstatic(String className, String field, Class fieldType) { getstatic(className, field, DescriptorUtils.makeDescriptor(fieldType)); } public void getstatic(String className, String field, String descriptor) { int index = constPool.addFieldEntry(className, field, descriptor); writeByte(Opcode.GETSTATIC); writeShort(index); currentOffset += 3; advanceFrame(currentFrame.push(descriptor)); } /** * writes a goto instruction. *

* TODO: implemented goto_w */ public void gotoInstruction(CodeLocation location) { writeByte(Opcode.GOTO); writeShort(location.getLocation() - currentOffset); mergeStackFrames(location.getStackFrame()); currentOffset += 3; currentFrame = null; } /** * writes a goto instruction. *

* TODO: implemented goto_w */ public BranchEnd gotoInstruction() { writeByte(Opcode.GOTO); writeShort(0); currentOffset += 3; BranchEnd ret = new BranchEnd(currentOffset - 2, currentFrame, currentOffset - 3); currentFrame = null; return ret; } public void i2b() { assertTypeOnStack(StackEntryType.INT, "i2b requires int on stack"); writeByte(Opcode.I2B); currentOffset++; advanceFrame(currentFrame.replace("B")); } public void i2c() { assertTypeOnStack(StackEntryType.INT, "i2c requires int on stack"); writeByte(Opcode.I2C); currentOffset++; advanceFrame(currentFrame.replace("C")); } public void i2d() { assertTypeOnStack(StackEntryType.INT, "i2d requires int on stack"); writeByte(Opcode.I2D); currentOffset++; advanceFrame(currentFrame.replace("D")); } public void i2f() { assertTypeOnStack(StackEntryType.INT, "i2f requires int on stack"); writeByte(Opcode.I2F); currentOffset++; advanceFrame(currentFrame.replace("F")); } public void i2l() { assertTypeOnStack(StackEntryType.INT, "i2l requires int on stack"); writeByte(Opcode.I2L); currentOffset++; advanceFrame(currentFrame.replace("J")); } public void i2s() { assertTypeOnStack(StackEntryType.INT, "i2s requires int on stack"); writeByte(Opcode.I2S); currentOffset++; advanceFrame(currentFrame.replace("S")); } public void iadd() { assertTypeOnStack(StackEntryType.INT, "iadd requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, "iadd requires int on stack"); writeByte(Opcode.IADD); currentOffset++; advanceFrame(currentFrame.pop()); } public void iaload() { assertTypeOnStack(StackEntryType.INT, "iaload requires an int on top of the stack"); assertTypeOnStack(1, StackEntryType.OBJECT, "iaload requires an array in position 2 on the stack"); writeByte(Opcode.IALOAD); currentOffset++; advanceFrame(currentFrame.pop2push1("I")); } public void iand() { assertTypeOnStack(StackEntryType.INT, "iand requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, "iand requires int on stack"); writeByte(Opcode.IAND); currentOffset++; advanceFrame(currentFrame.pop()); } public void iastore() { assertTypeOnStack(StackEntryType.INT, "iastore requires an int on top of the stack"); assertTypeOnStack(1, StackEntryType.INT, "iastore requires an int in position 2 on the stack"); assertTypeOnStack(2, StackEntryType.OBJECT, "iastore requires an array reference in position 3 on the stack"); writeByte(Opcode.IASTORE); currentOffset++; advanceFrame(currentFrame.pop3()); } /** * Adds the appropriate iconst instruction. *

* note, if the value is not in the range -1 to 5 ldc is written instead * * @param value */ public void iconst(int value) { if (value < -1 || value > 5) { if(value < -128 || value > 127) { ldc(value); } else { writeByte(Opcode.BIPUSH); writeByte(value); currentOffset+=2; advanceFrame(currentFrame.push("I")); } return; } writeByte(Opcode.ICONST_0 + value); currentOffset++; advanceFrame(currentFrame.push("I")); } public void idiv() { assertTypeOnStack(StackEntryType.INT, "idiv requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, "idiv requires int in position 2 on stack"); writeByte(Opcode.IDIV); currentOffset++; advanceFrame(currentFrame.pop()); } public void ifAcmpeq(CodeLocation location) { assertTypeOnStack(StackEntryType.OBJECT, "ifAcmpeq requires reference type on stack"); assertTypeOnStack(1, StackEntryType.OBJECT, "ifAcmpeq requires reference type in position 2 on stack"); writeByte(Opcode.IF_ACMPEQ); writeShort(location.getLocation() - currentOffset); mergeStackFrames(location.getStackFrame()); currentOffset += 3; advanceFrame(currentFrame.pop2()); } public BranchEnd ifAcmpeq() { assertTypeOnStack(StackEntryType.OBJECT, "ifAcmpeq requires reference type on stack"); assertTypeOnStack(1, StackEntryType.OBJECT, "ifAcmpeq requires reference type int position 2 on stack"); writeByte(Opcode.IF_ACMPEQ); writeShort(0); currentOffset += 3; advanceFrame(currentFrame.pop2()); BranchEnd ret = new BranchEnd(currentOffset - 2, currentFrame, currentOffset - 3); return ret; } public void ifAcmpne(CodeLocation location) { assertTypeOnStack(StackEntryType.OBJECT, "ifAcmpne requires reference type on stack"); assertTypeOnStack(1, StackEntryType.OBJECT, "ifAcmpne requires reference type in position 2 on stack"); writeByte(Opcode.IF_ACMPNE); writeShort(location.getLocation() - currentOffset); mergeStackFrames(location.getStackFrame()); currentOffset += 3; advanceFrame(currentFrame.pop2()); } public BranchEnd ifAcmpne() { assertTypeOnStack(StackEntryType.OBJECT, "ifAcmpne requires reference type on stack"); assertTypeOnStack(1, StackEntryType.OBJECT, "ifAcmpne requires reference type int position 2 on stack"); writeByte(Opcode.IF_ACMPNE); writeShort(0); currentOffset += 3; advanceFrame(currentFrame.pop2()); BranchEnd ret = new BranchEnd(currentOffset - 2, currentFrame, currentOffset - 3); return ret; } public void ifIcmpeq(CodeLocation location) { addIfIcmp(location, Opcode.IF_ICMPEQ, "ifIcmpeq"); } public BranchEnd ifIcmpeq() { return addIfIcmp(Opcode.IF_ICMPEQ, "ifIcmpeq"); } public void ifIcmpne(CodeLocation location) { addIfIcmp(location, Opcode.IF_ICMPNE, "ifIcmpne"); } public BranchEnd ifIcmpne() { return addIfIcmp(Opcode.IF_ICMPNE, "ifIcmpne"); } public void ifIcmplt(CodeLocation location) { addIfIcmp(location, Opcode.IF_ICMPLT, "ifIcmplt"); } public BranchEnd ifIcmplt() { return addIfIcmp(Opcode.IF_ICMPLT, "ifIcmplt"); } public void ifIcmple(CodeLocation location) { addIfIcmp(location, Opcode.IF_ICMPLE, "ifIcmple"); } public BranchEnd ifIcmple() { return addIfIcmp(Opcode.IF_ICMPLE, "ifIcmple"); } public void ifIcmpgt(CodeLocation location) { addIfIcmp(location, Opcode.IF_ICMPGT, "ifIcmpgt"); } public BranchEnd ifIcmpgt() { return addIfIcmp(Opcode.IF_ICMPGT, "ifIcmpgt"); } public void ifIcmpge(CodeLocation location) { addIfIcmp(location, Opcode.IF_ICMPGE, "ifIcmpge"); } public BranchEnd ifIcmpge() { return addIfIcmp(Opcode.IF_ICMPGE, "ifIcmpge"); } public void ifEq(CodeLocation location) { addIf(location, Opcode.IFEQ, "ifeq"); } public BranchEnd ifeq() { return addIf(Opcode.IFEQ, "ifeq"); } public void ifne(CodeLocation location) { addIf(location, Opcode.IFNE, "ifne"); } public BranchEnd ifne() { return addIf(Opcode.IFNE, "ifne"); } public void iflt(CodeLocation location) { addIf(location, Opcode.IFLT, "iflt"); } public BranchEnd iflt() { return addIf(Opcode.IFLT, "iflt"); } public void ifle(CodeLocation location) { addIf(location, Opcode.IFLE, "ifle"); } public BranchEnd ifle() { return addIf(Opcode.IFLE, "ifle"); } public void ifgt(CodeLocation location) { addIf(location, Opcode.IFGT, "ifgt"); } public BranchEnd ifgt() { return addIf(Opcode.IFGT, "ifgt"); } public void ifge(CodeLocation location) { addIf(location, Opcode.IFGE, "ifge"); } public BranchEnd ifge() { return addIf(Opcode.IFGE, "ifge"); } public void ifnotnull(CodeLocation location) { addNullComparison(location, Opcode.IFNONNULL, "ifnotnull"); } public BranchEnd ifnotnull() { return addNullComparison(Opcode.IFNONNULL, "ifnotnull"); } /** * Jump to the given location if the reference type on the top of the stack is null */ public void ifnull(CodeLocation location) { addNullComparison(location, Opcode.IFNULL, "ifnull"); } /** * Jump to the given location if the reference type on the top of the stack is null. *

* The {@link BranchEnd} returned from this method is used to set the end point to a future point in the bytecode stream */ public BranchEnd ifnull() { return addNullComparison(Opcode.IFNULL, "ifnull"); } public void iinc(int local, int amount) { if (getLocalVars().get(local).getType() != StackEntryType.INT) { throw new InvalidBytecodeException("iinc requires int at local variable position " + local + " " + getLocalVars().toString()); } if (local > 0xFF || amount > 0xFF) { writeByte(Opcode.WIDE); writeByte(Opcode.IINC); writeShort(local); writeShort(amount); currentOffset += 6; } else { writeByte(Opcode.IINC); writeByte(local); writeByte(amount); currentOffset += 3; } duplicateFrame(); } public void iload(int no) { LocalVariableState locals = getLocalVars(); if (locals.size() <= no) { throw new InvalidBytecodeException("Cannot load variable at " + no + ". Local Variables: " + locals.toString()); } StackEntry entry = locals.get(no); if (entry.getType() != StackEntryType.INT) { throw new InvalidBytecodeException("Invalid local variable at location " + no + " Local Variables " + locals.toString()); } if (no > 0xFF) { // wide version writeByte(Opcode.WIDE); writeByte(Opcode.ILOAD); writeShort(no); currentOffset += 4; } else if (no >= 0 && no < 4) { writeByte(Opcode.ILOAD_0 + no); currentOffset++; } else { writeByte(Opcode.ILOAD); writeByte(no); currentOffset += 2; } advanceFrame(currentFrame.push(entry)); } public void imul() { assertTypeOnStack(StackEntryType.INT, "imul requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, "imul requires int in position 2 on stack"); writeByte(Opcode.IMUL); currentOffset++; advanceFrame(currentFrame.pop()); } public void ineg() { assertTypeOnStack(StackEntryType.INT, "ineg requires int on stack"); writeByte(Opcode.INEG); currentOffset++; duplicateFrame(); } public void instanceofInstruction(String className) { assertTypeOnStack(StackEntryType.OBJECT, "instanceof requires an object reference on the stack"); int classIndex = constPool.addClassEntry(className); writeByte(Opcode.INSTANCEOF); writeShort(classIndex); currentOffset += 3; advanceFrame(currentFrame.replace("I")); } public void invokespecial(String className, String methodName, String descriptor) { String[] params = DescriptorUtils.parameterDescriptors(descriptor); String returnType = DescriptorUtils.returnType(descriptor); invokespecial(className, methodName, descriptor, returnType, params); } public void invokespecial(String className, String methodName, String returnType, String[] parameterTypes) { String descriptor = DescriptorUtils.methodDescriptor(parameterTypes, returnType); invokespecial(className, methodName, descriptor, returnType, parameterTypes); } public void invokespecial(Constructor constructor) { invokespecial(constructor.getDeclaringClass().getName(), "", DescriptorUtils .makeDescriptor(constructor), "V", DescriptorUtils.parameterDescriptors(constructor.getParameterTypes())); } public void invokespecial(Method method) { if (Modifier.isStatic(method.getModifiers())) { throw new InvalidBytecodeException("Cannot use invokespacial to invoke a static method"); } invokespecial(method.getDeclaringClass().getName(), method.getName(), DescriptorUtils.methodDescriptor(method), DescriptorUtils.makeDescriptor(method.getReturnType()), DescriptorUtils.parameterDescriptors(method .getParameterTypes())); } private void invokespecial(String className, String methodName, String descriptor, String returnType, String[] parameterTypes) { // TODO: validate stack int method = constPool.addMethodEntry(className, methodName, descriptor); writeByte(Opcode.INVOKESPECIAL); writeShort(method); currentOffset += 3; int pop = 1 + parameterTypes.length; for (String argument : parameterTypes) { if (argument.equals("D") || argument.equals("J")) { pop++; } } if (methodName.equals("")) { advanceFrame(currentFrame.constructorCall(pop - 1)); } else if (returnType.equals("V")) { advanceFrame(currentFrame.pop(pop)); } else { advanceFrame(currentFrame.pop(pop).push(returnType)); } } public void invokestatic(String className, String methodName, String descriptor) { String[] params = DescriptorUtils.parameterDescriptors(descriptor); String returnType = DescriptorUtils.returnType(descriptor); invokestatic(className, methodName, descriptor, returnType, params); } public void invokestatic(String className, String methodName, String returnType, String[] parameterTypes) { String descriptor = DescriptorUtils.methodDescriptor(parameterTypes, returnType); invokestatic(className, methodName, descriptor, returnType, parameterTypes); } public void invokestatic(Method method) { if (!Modifier.isStatic(method.getModifiers())) { throw new InvalidBytecodeException("Cannot use invokestatic to invoke a non static method"); } invokestatic(method.getDeclaringClass().getName(), method.getName(), DescriptorUtils.methodDescriptor(method), DescriptorUtils.makeDescriptor(method.getReturnType()), DescriptorUtils.parameterDescriptors(method .getParameterTypes())); } private void invokestatic(String className, String methodName, String descriptor, String returnType, String[] parameterTypes) { // TODO: validate stack int method = constPool.addMethodEntry(className, methodName, descriptor); writeByte(Opcode.INVOKESTATIC); writeShort(method); currentOffset += 3; int pop = parameterTypes.length; for (String argument : parameterTypes) { if (argument.equals("D") || argument.equals("J")) { pop++; } } if (returnType.equals("V")) { advanceFrame(currentFrame.pop(pop)); } else { advanceFrame(currentFrame.pop(pop).push(returnType)); } } public void invokevirtual(String className, String methodName, String descriptor) { String[] params = DescriptorUtils.parameterDescriptors(descriptor); String returnType = DescriptorUtils.returnType(descriptor); invokevirtual(className, methodName, descriptor, returnType, params); } public void invokevirtual(String className, String methodName, String returnType, String[] parameterTypes) { String descriptor = DescriptorUtils.methodDescriptor(parameterTypes, returnType); invokevirtual(className, methodName, descriptor, returnType, parameterTypes); } public void invokevirtual(Method method) { if (Modifier.isStatic(method.getModifiers())) { throw new InvalidBytecodeException("Cannot use invokevirtual to invoke a static method"); } else if (Modifier.isPrivate(method.getModifiers())) { throw new InvalidBytecodeException("Cannot use invokevirtual to invoke a private method"); } else if (method.getDeclaringClass().isInterface()) { throw new InvalidBytecodeException("Cannot use invokevirtual to invoke an interface method"); } invokevirtual(method.getDeclaringClass().getName(), method.getName(), DescriptorUtils.methodDescriptor(method), DescriptorUtils.makeDescriptor(method.getReturnType()), DescriptorUtils.parameterDescriptors(method .getParameterTypes())); } private void invokevirtual(String className, String methodName, String descriptor, String returnType, String[] parameterTypes) { // TODO: validate stack int method = constPool.addMethodEntry(className, methodName, descriptor); writeByte(Opcode.INVOKEVIRTUAL); writeShort(method); currentOffset += 3; int pop = 1 + parameterTypes.length; for (String argument : parameterTypes) { if (argument.equals("D") || argument.equals("J")) { pop++; } } if (returnType.equals("V")) { advanceFrame(currentFrame.pop(pop)); } else { advanceFrame(currentFrame.pop(pop).push(returnType)); } } public void invokeinterface(String className, String methodName, String descriptor) { String[] params = DescriptorUtils.parameterDescriptors(descriptor); String returnType = DescriptorUtils.returnType(descriptor); invokeinterface(className, methodName, descriptor, returnType, params); } public void invokeinterface(String className, String methodName, String returnType, String[] parameterTypes) { String descriptor = DescriptorUtils.methodDescriptor(parameterTypes, returnType); invokeinterface(className, methodName, descriptor, returnType, parameterTypes); } public void invokeinterface(Method method) { if (Modifier.isStatic(method.getModifiers())) { throw new InvalidBytecodeException("Cannot use invokeinterface to invoke a static method"); } else if (Modifier.isPrivate(method.getModifiers())) { throw new InvalidBytecodeException("Cannot use invokeinterface to invoke a private method"); } else if (!method.getDeclaringClass().isInterface()) { throw new InvalidBytecodeException("Cannot use invokeinterface to invoke a non interface method"); } invokeinterface(method.getDeclaringClass().getName(), method.getName(), DescriptorUtils.methodDescriptor(method), DescriptorUtils.makeDescriptor(method.getReturnType()), DescriptorUtils.parameterDescriptors(method .getParameterTypes())); } private void invokeinterface(String className, String methodName, String descriptor, String returnType, String[] parameterTypes) { // TODO: validate stack int pop = 1 + parameterTypes.length; for (String argument : parameterTypes) { if (argument.equals("D") || argument.equals("J")) { pop++; } } int method = constPool.addInterfaceMethodEntry(className, methodName, descriptor); writeByte(Opcode.INVOKEINTERFACE); writeShort(method); writeByte(pop); writeByte(0); currentOffset += 5; if (returnType.equals("V")) { advanceFrame(currentFrame.pop(pop)); } else { advanceFrame(currentFrame.pop(pop).push(returnType)); } } public void ior() { assertTypeOnStack(StackEntryType.INT, "ior requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, "ior requires int on stack"); writeByte(Opcode.IOR); currentOffset++; advanceFrame(currentFrame.pop()); } public void irem() { assertTypeOnStack(StackEntryType.INT, "irem requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, "irem requires int on stack"); writeByte(Opcode.IREM); currentOffset++; advanceFrame(currentFrame.pop()); } public void ishl() { assertTypeOnStack(StackEntryType.INT, "ishl requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, "ishl requires int on stack"); writeByte(Opcode.ISHL); currentOffset++; advanceFrame(currentFrame.pop()); } public void ishr() { assertTypeOnStack(StackEntryType.INT, "ishr requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, "ishr requires int on stack"); writeByte(Opcode.ISHR); currentOffset++; advanceFrame(currentFrame.pop()); } public void istore(int no) { assertTypeOnStack(StackEntryType.INT, "istore requires int on stack"); if (no > 0xFF) { // wide version writeByte(Opcode.WIDE); writeByte(Opcode.ISTORE); writeShort(no); currentOffset += 4; } else if (no >= 0 && no < 4) { writeByte(Opcode.ISTORE_0 + no); currentOffset++; } else { writeByte(Opcode.ISTORE); writeByte(no); currentOffset += 2; } advanceFrame(currentFrame.store(no)); } public void isub() { assertTypeOnStack(StackEntryType.INT, "isub requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, "isub requires int on stack"); writeByte(Opcode.ISUB); currentOffset++; advanceFrame(currentFrame.pop()); } public void iushr() { assertTypeOnStack(StackEntryType.INT, "iushr requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, "iushr requires int on stack"); writeByte(Opcode.IUSHR); currentOffset++; advanceFrame(currentFrame.pop()); } public void ixor() { assertTypeOnStack(StackEntryType.INT, "ixor requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, "ixor requires int on stack"); writeByte(Opcode.IXOR); currentOffset++; advanceFrame(currentFrame.pop()); } public void l2d() { assertTypeOnStack(StackEntryType.LONG, "l2d requires long on stack"); writeByte(Opcode.L2D); currentOffset++; advanceFrame(currentFrame.pop2push1("D")); } public void l2f() { assertTypeOnStack(StackEntryType.LONG, "l2f requires long on stack"); writeByte(Opcode.L2F); currentOffset++; advanceFrame(currentFrame.pop2push1("F")); } public void l2i() { assertTypeOnStack(StackEntryType.LONG, "l2i requires long on stack"); writeByte(Opcode.L2I); currentOffset++; advanceFrame(currentFrame.pop2push1("I")); } public void ladd() { assertTypeOnStack(StackEntryType.LONG, "ladd requires long on stack"); assertTypeOnStack(2, StackEntryType.LONG, "ladd requires long on stack"); writeByte(Opcode.LADD); currentOffset++; advanceFrame(currentFrame.pop2()); } public void laload() { assertTypeOnStack(StackEntryType.INT, "laload requires an int on top of the stack"); assertTypeOnStack(1, StackEntryType.OBJECT, "laload requires an array in position 2 on the stack"); writeByte(Opcode.LALOAD); currentOffset++; advanceFrame(currentFrame.pop2push1("J")); } public void land() { assertTypeOnStack(StackEntryType.LONG, "land requires long on stack"); assertTypeOnStack(2, StackEntryType.LONG, "land requires long on stack"); writeByte(Opcode.LAND); currentOffset++; advanceFrame(currentFrame.pop2()); } public void lastore() { assertTypeOnStack(StackEntryType.LONG, "lastore requires an long on top of the stack"); assertTypeOnStack(2, StackEntryType.INT, "lastore requires an int in position 2 on the stack"); assertTypeOnStack(3, StackEntryType.OBJECT, "lastore requires an array reference in position 3 on the stack"); writeByte(Opcode.LASTORE); currentOffset++; advanceFrame(currentFrame.pop4()); } public void lcmp() { assertTypeOnStack(StackEntryType.LONG, "lcmp requires long on stack"); assertTypeOnStack(2, StackEntryType.LONG, "lcmp requires long on stack"); writeByte(Opcode.LCMP); currentOffset++; advanceFrame(currentFrame.pop4push1("I")); } /** * Adds the appropriate lconst instruction. *

* note, if the value is not 0 or 1 then ldc is used instead */ public void lconst(long value) { if (value == 0) { writeByte(Opcode.LCONST_0); } else if (value == 1) { writeByte(Opcode.LCONST_1); } else { ldc2(value); return; } currentOffset++; advanceFrame(currentFrame.push("J")); } /** * Adds an ldc instruction for an int. * * @param value */ public void ldc(int value) { if (value > -2 && value < 6) { iconst(value); return; } int index = constPool.addIntegerEntry(value); ldcInternal(index); advanceFrame(currentFrame.push("I")); } /** * Adds an ldc instruction for float */ public void ldc(float value) { int index = constPool.addFloatEntry(value); ldcInternal(index); advanceFrame(currentFrame.push("F")); } /** * Adds an ldc instruction for a String *

* To load a class literal using ldc use the @{link #loadType(String)} method. */ public void ldc(String value) { int index = constPool.addStringEntry(value); ldcInternal(index); advanceFrame(currentFrame.push("Ljava/lang/String;")); } /** * Adds an ldc instruction for an int. */ private void ldcInternal(int index) { if (index > 0xFF) { writeByte(Opcode.LDC_W); writeShort(index); currentOffset += 3; } else { writeByte(Opcode.LDC); writeByte(index); currentOffset += 2; } } /** * Adds an ldc2_w instruction for double */ public void ldc2(double value) { int index = constPool.addDoubleEntry(value); writeByte(Opcode.LDC2_W); writeShort(index); currentOffset += 3; advanceFrame(currentFrame.push("D")); } /** * Adds an ldc2_w instruction for long */ public void ldc2(long value) { int index = constPool.addLongEntry(value); writeByte(Opcode.LDC2_W); writeShort(index); currentOffset += 3; advanceFrame(currentFrame.push("J")); } public void ldiv() { assertTypeOnStack(StackEntryType.LONG, "ldiv requires long on stack"); assertTypeOnStack(2, StackEntryType.LONG, "ldiv requires long in position 3 on stack"); writeByte(Opcode.LDIV); currentOffset++; advanceFrame(currentFrame.pop2()); } public void lload(int no) { LocalVariableState locals = getLocalVars(); if (locals.size() <= no) { throw new InvalidBytecodeException("Cannot load variable at " + no + ". Local Variables: " + locals.toString()); } StackEntry entry = locals.get(no); if (entry.getType() != StackEntryType.LONG) { throw new InvalidBytecodeException("Invalid local variable at location " + no + " Local Variables " + locals.toString()); } if (no > 0xFF) { // wide version writeByte(Opcode.WIDE); writeByte(Opcode.LLOAD); writeShort(no); currentOffset += 4; } else if (no >= 0 && no < 4) { writeByte(Opcode.LLOAD_0 + no); currentOffset++; } else { writeByte(Opcode.LLOAD); writeByte(no); currentOffset += 2; } advanceFrame(currentFrame.push(entry)); } public void lmul() { assertTypeOnStack(StackEntryType.LONG, "lmul requires long on stack"); assertTypeOnStack(2, StackEntryType.LONG, "lmul requires long in position 3 on stack"); writeByte(Opcode.LMUL); currentOffset++; advanceFrame(currentFrame.pop2()); } public void lneg() { assertTypeOnStack(StackEntryType.LONG, "lneg requires long on stack"); writeByte(Opcode.LNEG); currentOffset++; duplicateFrame(); } /** * Generates the apprpriate load instruction for the given type * * @param type The type of variable * @param no local variable number */ public void load(Class type, int no) { load(DescriptorUtils.makeDescriptor(type), no); } /** * Generates the apprpriate load instruction for the given type * * @param descriptor The descriptor of the variable * @param no local variable number */ public void load(String descriptor, int no) { if (descriptor.length() != 1) { aload(no); } else { char type = descriptor.charAt(0); switch (type) { case 'F': fload(no); break; case 'J': lload(no); break; case 'D': dload(no); break; case 'I': case 'S': case 'B': case 'C': case 'Z': iload(no); break; default: throw new InvalidBytecodeException("Could not load primitive type: " + type); } } } public void loadClass(String className) { int index = constPool.addClassEntry(className); ldcInternal(index); advanceFrame(currentFrame.push("Ljava/lang/Class;")); } /** * Loads a java.lang.Class for the given descriptor into the stack. */ public void loadType(String descriptor) { if (descriptor.length() != 1) { if (descriptor.startsWith("L") && descriptor.endsWith(";")) { descriptor = descriptor.substring(1, descriptor.length() - 1); } loadClass(descriptor); } else { char type = descriptor.charAt(0); switch (type) { case 'I': getstatic(Integer.class.getName(), "TYPE", "Ljava/lang/Class;"); break; case 'J': getstatic(Long.class.getName(), "TYPE", "Ljava/lang/Class;"); break; case 'S': getstatic(Short.class.getName(), "TYPE", "Ljava/lang/Class;"); break; case 'F': getstatic(Float.class.getName(), "TYPE", "Ljava/lang/Class;"); break; case 'D': getstatic(Double.class.getName(), "TYPE", "Ljava/lang/Class;"); break; case 'B': getstatic(Byte.class.getName(), "TYPE", "Ljava/lang/Class;"); break; case 'C': getstatic(Character.class.getName(), "TYPE", "Ljava/lang/Class;"); break; case 'Z': getstatic(Boolean.class.getName(), "TYPE", "Ljava/lang/Class;"); break; case 'V': getstatic(Void.class.getName(), "TYPE", "Ljava/lang/Class;"); break; default: throw new InvalidBytecodeException("Unkown primitive type: " + type); } } } /** * Adds a lookup switch statement * * @param lookupSwitchBuilder * @return */ public void lookupswitch(final LookupSwitchBuilder lookupSwitchBuilder) { assertTypeOnStack(StackEntryType.INT, "lookupswitch requires an int on the stack"); writeByte(Opcode.LOOKUPSWITCH); final int startOffset = currentOffset; currentOffset++; while (currentOffset % 4 != 0) { writeByte(0); currentOffset++; } StackFrame frame = currentFrame.pop(); final List values = new ArrayList(lookupSwitchBuilder.getValues()); if (lookupSwitchBuilder.getDefaultLocation() != null) { writeInt(lookupSwitchBuilder.getDefaultLocation().getLocation() - currentOffset); } else { writeInt(0); final BranchEnd ret = new BranchEnd(currentOffset, frame, true, startOffset); lookupSwitchBuilder.getDefaultBranchEnd().set(ret); } writeInt(values.size()); currentOffset += 8; Collections.sort(values); for (final LookupSwitchBuilder.ValuePair value : values) { writeInt(value.getValue()); currentOffset += 4; if (value.getLocation() != null) { writeInt(value.getLocation().getLocation()); currentOffset += 4; } else { writeInt(0); final BranchEnd ret = new BranchEnd(currentOffset, frame, true, startOffset); value.getBranchEnd().set(ret); currentOffset += 4; } } currentFrame = null; } public void lor() { assertTypeOnStack(StackEntryType.LONG, "lor requires long on stack"); assertTypeOnStack(2, StackEntryType.LONG, "lor requires long in position 3 on stack"); writeByte(Opcode.LOR); currentOffset++; advanceFrame(currentFrame.pop2()); } public void lrem() { assertTypeOnStack(StackEntryType.LONG, "lrem requires long on stack"); assertTypeOnStack(2, StackEntryType.LONG, "lrem requires long in position 3 on stack"); writeByte(Opcode.LREM); currentOffset++; advanceFrame(currentFrame.pop2()); } public void lshl() { assertTypeOnStack(StackEntryType.INT, "lshl requires int on stack"); assertTypeOnStack(1, StackEntryType.LONG, "lshl requires long in position 2 on stack"); writeByte(Opcode.LSHL); currentOffset++; advanceFrame(currentFrame.pop()); } public void lshr() { assertTypeOnStack(StackEntryType.INT, "lshr requires int on stack"); assertTypeOnStack(1, StackEntryType.LONG, "lshr requires long in position 2 on stack"); writeByte(Opcode.LSHR); currentOffset++; advanceFrame(currentFrame.pop()); } public void lstore(int no) { assertTypeOnStack(StackEntryType.LONG, "lstore requires long on stack"); if (no > 0xFF) { // wide version writeByte(Opcode.WIDE); writeByte(Opcode.LSTORE); writeShort(no); currentOffset += 4; } else if (no >= 0 && no < 4) { writeByte(Opcode.LSTORE_0 + no); currentOffset++; } else { writeByte(Opcode.LSTORE); writeByte(no); currentOffset += 2; } advanceFrame(currentFrame.store(no)); } public void lsub() { assertTypeOnStack(StackEntryType.LONG, "lsub requires long on stack"); assertTypeOnStack(2, StackEntryType.LONG, "lsub requires long in position 3 on stack"); writeByte(Opcode.LSUB); currentOffset++; advanceFrame(currentFrame.pop2()); } public void lushr() { assertTypeOnStack(StackEntryType.INT, "lushr requires int on stack"); assertTypeOnStack(1, StackEntryType.LONG, "lushr requires long in position 2 on stack"); writeByte(Opcode.LUSHR); currentOffset++; advanceFrame(currentFrame.pop()); } public void lxor() { assertTypeOnStack(StackEntryType.LONG, "lxor requires long on stack"); assertTypeOnStack(2, StackEntryType.LONG, "lxor requires long in position 3 on stack"); writeByte(Opcode.LXOR); currentOffset++; advanceFrame(currentFrame.pop2()); } /** * Gets the location object for the current location in the bytecode. Jumps to this location will begin executing the next * instruction that is written to the bytecode stream */ public CodeLocation mark() { return new CodeLocation(currentOffset, currentFrame); } public void monitorenter() { assertTypeOnStack(StackEntryType.OBJECT, "monitorenter requires object reference on stack"); writeByte(Opcode.MONITORENTER); currentOffset++; advanceFrame(currentFrame.pop()); } public void monitorexit() { assertTypeOnStack(StackEntryType.OBJECT, "monitorexit requires object reference on stack"); writeByte(Opcode.MONITOREXIT); currentOffset++; advanceFrame(currentFrame.pop()); } public void multianewarray(String arrayType, int dimensions) { StringBuilder newType = new StringBuilder(); for (int i = 0; i < dimensions; ++i) { assertTypeOnStack(i, StackEntryType.INT, "multianewarray requires int on stack in position " + i); newType.append('['); } if (!arrayType.startsWith("[")) { newType.append('L'); newType.append(arrayType); newType.append(";"); } else { newType.append(arrayType); } int classIndex = constPool.addClassEntry(newType.toString()); writeByte(Opcode.MULTIANEWARRAY); writeShort(classIndex); writeByte(dimensions); currentOffset += 4; advanceFrame(currentFrame.pop(dimensions).push(newType.toString())); } public void newInstruction(String classname) { int classIndex = constPool.addClassEntry(classname); writeByte(Opcode.NEW); writeShort(classIndex); StackEntry entry = new StackEntry(StackEntryType.UNITITIALIZED_OBJECT, classname, currentOffset); currentOffset += 3; advanceFrame(currentFrame.push(entry)); } public void newInstruction(Class clazz) { newInstruction(clazz.getName()); } /** * arrayType must be a {@link Class} object that represents a primitive type */ public void newarray(Class arrayType) { assertTypeOnStack(StackEntryType.INT, "newarray requires int on stack"); int type = 0; String desc; if (arrayType == boolean.class) { type = Opcode.T_BOOLEAN; desc = "[Z"; } else if (arrayType == char.class) { type = Opcode.T_CHAR; desc = "[C"; } else if (arrayType == float.class) { type = Opcode.T_FLOAT; desc = "[F"; } else if (arrayType == double.class) { type = Opcode.T_DOUBLE; desc = "[D"; } else if (arrayType == byte.class) { type = Opcode.T_BYTE; desc = "[B"; } else if (arrayType == short.class) { type = Opcode.T_SHORT; desc = "[S"; } else if (arrayType == int.class) { type = Opcode.T_INT; desc = "[I"; } else if (arrayType == long.class) { type = Opcode.T_LONG; desc = "[J"; } else { throw new InvalidBytecodeException("Class " + arrayType + " is not a primitive type"); } writeByte(Opcode.NEWARRAY); writeByte(type); currentOffset += 2; advanceFrame(currentFrame.replace(desc)); } public void nop() { writeByte(Opcode.NOP); currentOffset++; duplicateFrame(); } public void pop() { writeByte(Opcode.POP); currentOffset++; advanceFrame(currentFrame.pop()); } public void pop2() { writeByte(Opcode.POP2); currentOffset++; advanceFrame(currentFrame.pop2()); } public void putfield(String className, String field, Class fieldType) { putfield(className, field, DescriptorUtils.makeDescriptor(fieldType)); } public void putfield(String className, String field, String descriptor) { if (!getStack().isOnTop(descriptor)) { throw new InvalidBytecodeException("Attempting to put wrong type into field. Field:" + className + "." + field + " (" + descriptor + "). Stack State: " + getStack().toString()); } if (getStack().top_1().getType() != StackEntryType.UNINITIALIZED_THIS) { assertTypeOnStack(1, StackEntryType.OBJECT, "expected object in position 2 on stack"); } int index = constPool.addFieldEntry(className, field, descriptor); writeByte(Opcode.PUTFIELD); writeShort(index); currentOffset += 3; advanceFrame(currentFrame.pop2()); } public void putstatic(String className, String field, Class fieldType) { putstatic(className, field, DescriptorUtils.makeDescriptor(fieldType)); } public void putstatic(String className, String field, String descriptor) { if (!getStack().isOnTop(descriptor)) { throw new InvalidBytecodeException("Attempting to put wrong type into static field. Field:" + className + "." + field + " (" + descriptor + "). Stack State: " + getStack().toString()); } int index = constPool.addFieldEntry(className, field, descriptor); writeByte(Opcode.PUTSTATIC); writeShort(index); currentOffset += 3; advanceFrame(currentFrame.pop()); } /** * Adds the appropriate return instruction for the methods return type. */ public void returnInstruction() { String returnType = method.getReturnType(); if (!returnType.equals("V")) { if (!getStack().isOnTop(returnType)) { throw new InvalidBytecodeException(returnType + " is not on top of stack. " + getStack().toString()); } } // all these instructions are one byte currentOffset++; // return instructions do not create stack map entries if (returnType.length() > 1) { writeByte(Opcode.ARETURN); } else { char ret = method.getReturnType().charAt(0); switch (ret) { case 'V': writeByte(Opcode.RETURN); break; case 'I': case 'Z': case 'S': case 'B': case 'C': writeByte(Opcode.IRETURN); break; case 'F': writeByte(Opcode.FRETURN); break; case 'D': writeByte(Opcode.DRETURN); break; case 'J': writeByte(Opcode.LRETURN); } } currentFrame = null; } public void saload() { assertTypeOnStack(StackEntryType.INT, "saload requires an int on top of the stack"); assertTypeOnStack(1, StackEntryType.OBJECT, "saload requires an array in position 2 on the stack"); writeByte(Opcode.SALOAD); currentOffset++; advanceFrame(currentFrame.pop2push1("I")); } public void sastore() { assertTypeOnStack(StackEntryType.INT, "sastore requires an int on top of the stack"); assertTypeOnStack(1, StackEntryType.INT, "sastore requires an int in position 2 on the stack"); assertTypeOnStack(2, StackEntryType.OBJECT, "sastore requires an array reference in position 3 on the stack"); writeByte(Opcode.SASTORE); currentOffset++; advanceFrame(currentFrame.pop3()); } public void sipush(short value) { writeByte(Opcode.SIPUSH); writeShort(value); currentOffset += 3; advanceFrame(currentFrame.push("S")); } public void swap() { assertNotWideOnStack("swap cannot be used when wide type is on top of stack"); assertNotWideOnStack(1, "swap cannot be used when wide type is on position 1 of the stack"); writeByte(Opcode.SWAP); currentOffset++; advanceFrame(currentFrame.swap()); } public void tableswitch(final TableSwitchBuilder builder) { assertTypeOnStack(StackEntryType.INT, "lookupswitch requires an int on the stack"); writeByte(Opcode.TABLESWITCH); final int startOffset = currentOffset; currentOffset++; while (currentOffset % 4 != 0) { writeByte(0); currentOffset++; } if(builder.getHigh() - builder.getLow() + 1 != builder.getValues().size()) { throw new RuntimeException("high - low + 1 != the number of values in the table"); } StackFrame frame = currentFrame.pop(); if (builder.getDefaultLocation() != null) { writeInt(builder.getDefaultLocation().getLocation() - currentOffset); } else { writeInt(0); final BranchEnd ret = new BranchEnd(currentOffset, frame, true, startOffset); builder.getDefaultBranchEnd().set(ret); } writeInt(builder.getLow()); writeInt(builder.getHigh()); currentOffset += 12; for (final TableSwitchBuilder.ValuePair value : builder.getValues()) { if (value.getLocation() != null) { writeInt(value.getLocation().getLocation()); currentOffset += 4; } else { writeInt(0); final BranchEnd ret = new BranchEnd(currentOffset, frame, true, startOffset); value.getBranchEnd().set(ret); currentOffset += 4; } } currentFrame = null; } /** * loads all parameters onto the stack. *

* If this method is non-static then the parameter at location 0 (i.e. this object) is not pushed. */ public void loadMethodParameters() { int index = method.isStatic() ? 0 : 1; for (String type : method.getParameters()) { if (type.length() > 1) { // object or array aload(index); } else if (type.equals("D")) { dload(index); index++; } else if (type.equals("J")) { lload(index); index++; } else if (type.equals("F")) { fload(index); } else { iload(index); } index++; } } private void writeByte(int n) { try { data.writeByte(n); } catch (IOException e) { throw new RuntimeException(e); } } private void writeShort(int n) { try { if(n > Short.MAX_VALUE) { throw new RuntimeException(n + " is to big to be written as a 16 bit value"); } data.writeShort(n); } catch (IOException e) { throw new RuntimeException(e); } } private void writeInt(int n) { try { data.writeInt(n); } catch (IOException e) { throw new RuntimeException(e); } } /** * overwrites a 16 bit value in the already written bytecode data */ private void overwriteShort(byte[] bytecode, int offset, int value) { bytecode[offset] = (byte) (value >> 8); bytecode[offset + 1] = (byte) (value); } /** * overwrites a 32 bit value in the already written bytecode data */ private void overwriteInt(byte[] bytecode, int offset, int value) { bytecode[offset] = (byte) (value >> 24); bytecode[offset + 1] = (byte) (value >> 16); bytecode[offset + 2] = (byte) (value >> 8); bytecode[offset + 3] = (byte) (value); } public LinkedHashMap getStackFrames() { return new LinkedHashMap(stackFrames); } public void setupFrame(String ... types) { final LocalVariableState localVariableState = new LocalVariableState(constPool, types); final StackFrame f = new StackFrame(new StackState(constPool), localVariableState, StackFrameType.FULL_FRAME); mergeStackFrames(f); } public ConstPool getConstPool() { return constPool; } /** * Adds a duplicate of the current frame to the current position. *

* currently this just puts the same frame into a different position */ private void duplicateFrame() { stackFrames.put(currentOffset, currentFrame); updateMaxValues(); } private void advanceFrame(StackFrame frame) { stackFrames.put(currentOffset, frame); currentFrame = frame; updateMaxValues(); } private void updateMaxValues() { if (getStack().getContents().size() > maxStackDepth) { maxStackDepth = getStack().getContents().size(); } if (getLocalVars().getContents().size() > maxLocals) { maxLocals = getLocalVars().getContents().size(); } } private LocalVariableState getLocalVars() { if(currentFrame == null) { throw new RuntimeException("No local variable information available, call setupFrame first"); } return currentFrame.getLocalVariableState(); } private StackState getStack() { return currentFrame.getStackState(); } public void assertTypeOnStack(int position, StackEntryType type, String message) { if (getStack().size() <= position) { throw new InvalidBytecodeException(message + " Stack State: " + getStack().toString()); } int index = getStack().getContents().size() - 1 - position; if (type == StackEntryType.DOUBLE || type == StackEntryType.LONG) { index -= 1; } StackEntryType stype = getStack().getContents().get(index).getType(); if (stype != type) { if (!(type == StackEntryType.OBJECT && stype == StackEntryType.NULL)) { throw new InvalidBytecodeException(message + " Stack State: " + getStack().toString()); } } } public void assertTypeOnStack(StackEntryType type, String message) { assertTypeOnStack(0, type, message); } public void assertNotWideOnStack(int position, String message) { if (getStack().size() <= position) { throw new InvalidBytecodeException(message + " Stack State: " + getStack().toString()); } int index = getStack().getContents().size() - 1 - position; StackEntryType stype = getStack().getContents().get(index).getType(); if (stype == StackEntryType.TOP) { throw new InvalidBytecodeException(message + " Stack State: " + getStack().toString()); } } public void assertNotWideOnStack(String message) { assertNotWideOnStack(0, message); } /** * Merge the stack frames. *

* If the frames are incompatible then an {@link InvalidBytecodeException} is thrown. If the frames cannot be properly * merged then the stack map is marked as invalid */ private void mergeStackFrames(StackFrame stackFrame) { if (currentFrame == null) { currentFrame = stackFrame; stackFrames.put(currentOffset, currentFrame); return; } StackState currentStackState = getStack(); StackState mergeStackState = stackFrame.getStackState(); if (currentStackState.size() != mergeStackState.size()) { throw new InvalidBytecodeException("Cannot merge stack frames, different stack sizes"); } for (int i = 0; i < mergeStackState.size(); ++i) { StackEntry currentEntry = currentStackState.getContents().get(i); StackEntry mergeEntry = mergeStackState.getContents().get(i); if (mergeEntry.getType() == currentEntry.getType()) { if (mergeEntry.getType() == StackEntryType.OBJECT) { if (!mergeEntry.getDescriptor().equals(currentEntry.getDescriptor())) { if (!mergeEntry.equals("Ljava/lang/Object;")) { // we cannot reliably determine if closes common superclass at this point // so we will just mark the stack map attribute as invalid stackMapAttributeValid = false; } } } } else if (!((mergeEntry.getType() == StackEntryType.NULL && currentEntry.getType() == StackEntryType.OBJECT) || (mergeEntry .getType() == StackEntryType.OBJECT && currentEntry.getType() == StackEntryType.NULL))) { throw new InvalidBytecodeException("Cannot merge stack frame " + currentStackState + " with frame " + mergeStackState + " stack entry " + i + " is invalid"); } } LocalVariableState currentLocalVariableState = getLocalVars(); LocalVariableState mergeLocalVariableState = getLocalVars(); if (currentLocalVariableState.size() < mergeLocalVariableState.size()) { throw new InvalidBytecodeException( "Cannot merge stack frames, merge location has less locals than current location"); } for (int i = 0; i < mergeLocalVariableState.size(); ++i) { StackEntry currentEntry = currentLocalVariableState.getContents().get(i); StackEntry mergeEntry = mergeLocalVariableState.getContents().get(i); if (mergeEntry.getType() == currentEntry.getType()) { if (mergeEntry.getType() == StackEntryType.OBJECT) { if (!mergeEntry.getDescriptor().equals(currentEntry.getDescriptor())) { if (!mergeEntry.equals("Ljava/lang/Object;")) { // we cannot reliably determine if closes common superclass at this point // so we will just mark the stack map attribute as invalid stackMapAttributeValid = false; } } } } else if (!((mergeEntry.getType() == StackEntryType.NULL && currentEntry.getType() == StackEntryType.OBJECT) || (mergeEntry .getType() == StackEntryType.OBJECT && currentEntry.getType() == StackEntryType.NULL))) { throw new InvalidBytecodeException("Cannot merge stack frame " + currentLocalVariableState + " with frame " + currentLocalVariableState + " local variable entry " + i + " is invalid"); } } } private void addIfIcmp(CodeLocation location, int opcode, String name) { assertTypeOnStack(StackEntryType.INT, name + " requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, name + " requires int in position 2 on stack"); writeByte(opcode); writeShort(location.getLocation() - currentOffset); currentOffset += 3; advanceFrame(currentFrame.pop2()); mergeStackFrames(location.getStackFrame()); } private BranchEnd addIfIcmp(int opcode, String name) { assertTypeOnStack(StackEntryType.INT, name + " requires int on stack"); assertTypeOnStack(1, StackEntryType.INT, name + " requires int int position 2 on stack"); writeByte(opcode); writeShort(0); currentOffset += 3; advanceFrame(currentFrame.pop2()); BranchEnd ret = new BranchEnd(currentOffset - 2, currentFrame, currentOffset - 3); return ret; } private void addIf(CodeLocation location, int opcode, String name) { assertTypeOnStack(StackEntryType.INT, name + " requires int on stack"); writeByte(opcode); writeShort(location.getLocation() - currentOffset); currentOffset += 3; advanceFrame(currentFrame.pop()); mergeStackFrames(location.getStackFrame()); } private BranchEnd addIf(int opcode, String name) { assertTypeOnStack(StackEntryType.INT, name + " requires int on stack"); writeByte(opcode); writeShort(0); currentOffset += 3; advanceFrame(currentFrame.pop()); BranchEnd ret = new BranchEnd(currentOffset - 2, currentFrame, currentOffset - 3); return ret; } private void addNullComparison(CodeLocation location, int opcode, String name) { assertTypeOnStack(StackEntryType.OBJECT, name + " requires reference type on stack"); writeByte(opcode); writeShort(location.getLocation() - currentOffset); currentOffset += 3; advanceFrame(currentFrame.pop()); mergeStackFrames(location.getStackFrame()); } private BranchEnd addNullComparison(int opcode, String name) { assertTypeOnStack(StackEntryType.OBJECT, name + " requires reference type on stack"); writeByte(opcode); writeShort(0); currentOffset += 3; advanceFrame(currentFrame.pop()); BranchEnd ret = new BranchEnd(currentOffset - 2, currentFrame, currentOffset - 3); return ret; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy