You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
309 lines
10 KiB
309 lines
10 KiB
/*
|
|
* Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*/
|
|
package com.sun.org.apache.bcel.internal.generic;
|
|
|
|
/* ====================================================================
|
|
* The Apache Software License, Version 1.1
|
|
*
|
|
* Copyright (c) 2001 The Apache Software Foundation. All rights
|
|
* reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* 3. The end-user documentation included with the redistribution,
|
|
* if any, must include the following acknowledgment:
|
|
* "This product includes software developed by the
|
|
* Apache Software Foundation (http://www.apache.org/)."
|
|
* Alternately, this acknowledgment may appear in the software itself,
|
|
* if and wherever such third-party acknowledgments normally appear.
|
|
*
|
|
* 4. The names "Apache" and "Apache Software Foundation" and
|
|
* "Apache BCEL" must not be used to endorse or promote products
|
|
* derived from this software without prior written permission. For
|
|
* written permission, please contact apache@apache.org.
|
|
*
|
|
* 5. Products derived from this software may not be called "Apache",
|
|
* "Apache BCEL", nor may "Apache" appear in their name, without
|
|
* prior written permission of the Apache Software Foundation.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
|
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
* ====================================================================
|
|
*
|
|
* This software consists of voluntary contributions made by many
|
|
* individuals on behalf of the Apache Software Foundation. For more
|
|
* information on the Apache Software Foundation, please see
|
|
* <http://www.apache.org/>.
|
|
*/
|
|
|
|
import com.sun.org.apache.bcel.internal.Constants;
|
|
import com.sun.org.apache.bcel.internal.classfile.Utility;
|
|
import com.sun.org.apache.bcel.internal.classfile.ConstantPool;
|
|
import java.io.*;
|
|
import com.sun.org.apache.bcel.internal.util.ByteSequence;
|
|
|
|
/**
|
|
* Abstract super class for all Java byte codes.
|
|
*
|
|
* @author <A HREF="mailto:markus.dahm@berlin.de">M. Dahm</A>
|
|
*/
|
|
public abstract class Instruction implements Cloneable, Serializable {
|
|
protected short length = 1; // Length of instruction in bytes
|
|
protected short opcode = -1; // Opcode number
|
|
|
|
private static InstructionComparator cmp = InstructionComparator.DEFAULT;
|
|
|
|
/**
|
|
* Empty constructor needed for the Class.newInstance() statement in
|
|
* Instruction.readInstruction(). Not to be used otherwise.
|
|
*/
|
|
Instruction() {}
|
|
|
|
public Instruction(short opcode, short length) {
|
|
this.length = length;
|
|
this.opcode = opcode;
|
|
}
|
|
|
|
/**
|
|
* Dump instruction as byte code to stream out.
|
|
* @param out Output stream
|
|
*/
|
|
public void dump(DataOutputStream out) throws IOException {
|
|
out.writeByte(opcode); // Common for all instructions
|
|
}
|
|
|
|
/** @return name of instruction, i.e., opcode name
|
|
*/
|
|
public String getName() {
|
|
return Constants.OPCODE_NAMES[opcode];
|
|
}
|
|
|
|
/**
|
|
* Long output format:
|
|
*
|
|
* <name of opcode> "["<opcode number>"]"
|
|
* "("<length of instruction>")"
|
|
*
|
|
* @param verbose long/short format switch
|
|
* @return mnemonic for instruction
|
|
*/
|
|
public String toString(boolean verbose) {
|
|
if(verbose)
|
|
return getName() + "[" + opcode + "](" + length + ")";
|
|
else
|
|
return getName();
|
|
}
|
|
|
|
/**
|
|
* @return mnemonic for instruction in verbose format
|
|
*/
|
|
public String toString() {
|
|
return toString(true);
|
|
}
|
|
|
|
/**
|
|
* @return mnemonic for instruction with sumbolic references resolved
|
|
*/
|
|
public String toString(ConstantPool cp) {
|
|
return toString(false);
|
|
}
|
|
|
|
/**
|
|
* Use with caution, since `BranchInstruction's have a `target' reference which
|
|
* is not copied correctly (only basic types are). This also applies for
|
|
* `Select' instructions with their multiple branch targets.
|
|
*
|
|
* @see BranchInstruction
|
|
* @return (shallow) copy of an instruction
|
|
*/
|
|
public Instruction copy() {
|
|
Instruction i = null;
|
|
|
|
// "Constant" instruction, no need to duplicate
|
|
if(InstructionConstants.INSTRUCTIONS[this.getOpcode()] != null)
|
|
i = this;
|
|
else {
|
|
try {
|
|
i = (Instruction)clone();
|
|
} catch(CloneNotSupportedException e) {
|
|
System.err.println(e);
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
/**
|
|
* Read needed data (e.g. index) from file.
|
|
*
|
|
* @param bytes byte sequence to read from
|
|
* @param wide "wide" instruction flag
|
|
*/
|
|
protected void initFromFile(ByteSequence bytes, boolean wide)
|
|
throws IOException
|
|
{}
|
|
|
|
/**
|
|
* Read an instruction from (byte code) input stream and return the
|
|
* appropiate object.
|
|
*
|
|
* @param file file to read from
|
|
* @return instruction object being read
|
|
*/
|
|
public static final Instruction readInstruction(ByteSequence bytes)
|
|
throws IOException
|
|
{
|
|
boolean wide = false;
|
|
short opcode = (short)bytes.readUnsignedByte();
|
|
Instruction obj = null;
|
|
|
|
if(opcode == Constants.WIDE) { // Read next opcode after wide byte
|
|
wide = true;
|
|
opcode = (short)bytes.readUnsignedByte();
|
|
}
|
|
|
|
if(InstructionConstants.INSTRUCTIONS[opcode] != null)
|
|
return InstructionConstants.INSTRUCTIONS[opcode]; // Used predefined immutable object, if available
|
|
|
|
/* Find appropiate class, instantiate an (empty) instruction object
|
|
* and initialize it by hand.
|
|
*/
|
|
Class clazz;
|
|
|
|
try {
|
|
clazz = Class.forName(className(opcode));
|
|
} catch (ClassNotFoundException cnfe){
|
|
// If a class by that name does not exist, the opcode is illegal.
|
|
// Note that IMPDEP1, IMPDEP2, BREAKPOINT are also illegal in a sense.
|
|
throw new ClassGenException("Illegal opcode detected.");
|
|
}
|
|
|
|
try {
|
|
obj = (Instruction)clazz.newInstance();
|
|
|
|
if(wide && !((obj instanceof LocalVariableInstruction) ||
|
|
(obj instanceof IINC) ||
|
|
(obj instanceof RET)))
|
|
throw new Exception("Illegal opcode after wide: " + opcode);
|
|
|
|
obj.setOpcode(opcode);
|
|
obj.initFromFile(bytes, wide); // Do further initializations, if any
|
|
// Byte code offset set in InstructionList
|
|
} catch(Exception e) { throw new ClassGenException(e.toString()); }
|
|
|
|
return obj;
|
|
}
|
|
|
|
private static final String className(short opcode) {
|
|
String name = Constants.OPCODE_NAMES[opcode].toUpperCase();
|
|
|
|
/* ICONST_0, etc. will be shortened to ICONST, etc., since ICONST_0 and the like
|
|
* are not implemented (directly).
|
|
*/
|
|
try {
|
|
int len = name.length();
|
|
char ch1 = name.charAt(len - 2), ch2 = name.charAt(len - 1);
|
|
|
|
if((ch1 == '_') && (ch2 >= '0') && (ch2 <= '5'))
|
|
name = name.substring(0, len - 2);
|
|
|
|
if(name.equals("ICONST_M1")) // Special case
|
|
name = "ICONST";
|
|
} catch(StringIndexOutOfBoundsException e) { System.err.println(e); }
|
|
|
|
return "com.sun.org.apache.bcel.internal.generic." + name;
|
|
}
|
|
|
|
/**
|
|
* This method also gives right results for instructions whose
|
|
* effect on the stack depends on the constant pool entry they
|
|
* reference.
|
|
* @return Number of words consumed from stack by this instruction,
|
|
* or Constants.UNPREDICTABLE, if this can not be computed statically
|
|
*/
|
|
public int consumeStack(ConstantPoolGen cpg) {
|
|
return Constants.CONSUME_STACK[opcode];
|
|
}
|
|
|
|
/**
|
|
* This method also gives right results for instructions whose
|
|
* effect on the stack depends on the constant pool entry they
|
|
* reference.
|
|
* @return Number of words produced onto stack by this instruction,
|
|
* or Constants.UNPREDICTABLE, if this can not be computed statically
|
|
*/
|
|
public int produceStack(ConstantPoolGen cpg) {
|
|
return Constants.PRODUCE_STACK[opcode];
|
|
}
|
|
|
|
/**
|
|
* @return this instructions opcode
|
|
*/
|
|
public short getOpcode() { return opcode; }
|
|
|
|
/**
|
|
* @return length (in bytes) of instruction
|
|
*/
|
|
public int getLength() { return length; }
|
|
|
|
/**
|
|
* Needed in readInstruction.
|
|
*/
|
|
private void setOpcode(short opcode) { this.opcode = opcode; }
|
|
|
|
/** Some instructions may be reused, so don't do anything by default.
|
|
*/
|
|
void dispose() {}
|
|
|
|
/**
|
|
* Call corresponding visitor method(s). The order is:
|
|
* Call visitor methods of implemented interfaces first, then
|
|
* call methods according to the class hierarchy in descending order,
|
|
* i.e., the most specific visitXXX() call comes last.
|
|
*
|
|
* @param v Visitor object
|
|
*/
|
|
public abstract void accept(Visitor v);
|
|
|
|
/** Get Comparator object used in the equals() method to determine
|
|
* equality of instructions.
|
|
*
|
|
* @return currently used comparator for equals()
|
|
*/
|
|
public static InstructionComparator getComparator() { return cmp; }
|
|
|
|
/** Set comparator to be used for equals().
|
|
*/
|
|
public static void setComparator(InstructionComparator c) { cmp = c; }
|
|
|
|
/** Check for equality, delegated to comparator
|
|
* @return true if that is an Instruction and has the same opcode
|
|
*/
|
|
public boolean equals(Object that) {
|
|
return (that instanceof Instruction)?
|
|
cmp.equals(this, (Instruction)that) : false;
|
|
}
|
|
}
|