blob: b167987bb6a652120148087d6cabea7394d8d589 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.flex.abc.semantics;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.HashMap;
import org.apache.flex.abc.ABCConstants;
/**
* A representation of an ABC instruction.
* Note that an instance of Instruction may be shared or reused.
*/
public abstract class Instruction
{
/*
* Opcode mappings extracted from ABCConstants
*/
private static Map<String, Integer> opcodeNameToOpcode;
private static Map<Integer, String> opcodeToOpcodeName;
// Populate the opcode maps.
static
{
opcodeNameToOpcode = new HashMap<String, Integer>();
opcodeToOpcodeName = new HashMap<Integer, String>();
loadOpcodes();
}
/**
* Decode an opcode name into a numeric constant.
*
* @param opcodeName - the name of the opcode.
* @return the opcode's value, or -1 if it's not an opcode.
*/
public static int decodeOpcodeName(String opcodeName)
{
String opcodeKey = opcodeName.toLowerCase();
if (opcodeNameToOpcode.size() == 0)
loadOpcodes();
if (opcodeNameToOpcode.containsKey(opcodeKey))
return opcodeNameToOpcode.get(opcodeKey);
else
return -1;
}
/**
* Get the symbolic name of an opcode.
*
* @return the opcode's symbolic name, or a hex representation if none
* exists.
*/
public static String decodeOp(int opcode)
{
if (opcodeToOpcodeName.containsKey(opcode))
return opcodeToOpcodeName.get(opcode);
else
return "OP_" + Integer.toHexString(opcode);
}
/**
* Look at fields of ABCConstants and get the mappings of OP_foo names to
* their values.
*/
private static void loadOpcodes()
{
// Traverse the names of the OP_foo constants
// in ABCConstants and load their values.
for (Field f : ABCConstants.class.getFields())
{
String field_name = f.getName();
if (field_name.startsWith("OP_"))
{
String opcode = field_name.substring(3);
try
{
int field_value = f.getInt(null);
opcodeNameToOpcode.put(opcode, field_value);
opcodeToOpcodeName.put(field_value, opcode);
}
catch (Exception noFieldValue)
{
// Ignore, continue...
}
}
}
}
/**
* Constructor.
*/
protected Instruction(int opcode)
{
this.opcode = opcode;
}
/**
* @see ABCConstants
*/
protected int opcode;
/**
* @return this instruction's opcode.
* @see ABCConstants for opcodes.
*/
public int getOpcode()
{
return this.opcode;
}
/**
* determine if instruction has immediate operands.
*
* @return true if the instruction has an immediate operand.
*/
public boolean isImmediate()
{
return false;
}
/**
* @return this Instruction's immediate operand.
*/
public int getImmediate()
{
unsupported("%s has no immediate operand.");
return -1;
}
/**
* Get the target of a branch instruction.
*
* @return the target label, or null if not present.
*/
public Label getTarget()
{
return null;
}
/**
* Set the target of a branch instruction.
*
* @param target - the AET Label this instruction targets.
*/
public void setTarget(Label target)
{
unsupported("Cannot set target on %s");
}
/**
* Get one of the instruction's non-immediate operands.
*
* @param index - the index of the desired operand.
* @return the operand at the given index.
* @throws IllegalStateException if the instruction has no operands.
*/
public Object getOperand(int index)
{
unsupported("%s has no operands");
return null;
}
/**
* @return the number of operands this instruction has.
*/
public int getOperandCount()
{
return 0;
}
/**
* @return true if this instruction has operands (it may have zero operands,
* but it supports them).
*/
public boolean hasOperands()
{
return false;
}
/**
* Set this Instruction's operands.
*
* @pre hasOperands() must be true.
*/
public void setOperands(Object[] operands)
{
unsupported("%s has no operands");
}
/**
* Is this a branch instruction?
*
* @return true if the instruction is a branch.
*/
public boolean isBranch()
{
return ABCConstants.OP_lookupswitch == getOpcode() || getTarget() != null;
}
/**
* Is this one of the return instructions?
*
* @return true if this instruction is one of the return instructions
*/
public boolean isReturn()
{
int opcode = getOpcode();
return ABCConstants.OP_returnvalue == opcode || ABCConstants.OP_returnvoid == opcode;
}
/**
* Is this a transfer of control?
* @return true if isBranch() or isReturn() is true, or the opcode is OP_throw.
*/
public boolean isTransferOfControl()
{
return isBranch() || isReturn() || ABCConstants.OP_throw == opcode;
}
/**
* Set the immediate field of a local register access instruction.
*
* @pre only valid for get/set/inc/declocal, hasnext, and kill.
*/
public void setImmediate(final int immediate)
{
unsupported("%s has no immediate operand.");
}
/**
* Set the temporary register operands of a hasnext2 instruction.
*/
public void setTempRegisters(Object[] tempregs)
{
unsupported("cannot set temp registers of %s");
}
/**
* @return true if this instruction is an executable (non-debug)
* instruction.
*/
public boolean isExecutable()
{
return this.opcode != ABCConstants.OP_debugline && this.opcode != ABCConstants.OP_debugfile;
}
/**
* Test whether this instructions opcode is targetable
*
* @return true if the instructions opcode is targetable
*/
public boolean isTargetableInstruction()
{
switch (opcode)
{
case ABCConstants.OP_ifnlt:
case ABCConstants.OP_ifnle:
case ABCConstants.OP_ifngt:
case ABCConstants.OP_ifnge:
case ABCConstants.OP_iftrue:
case ABCConstants.OP_iffalse:
case ABCConstants.OP_ifeq:
case ABCConstants.OP_ifne:
case ABCConstants.OP_iflt:
case ABCConstants.OP_ifle:
case ABCConstants.OP_ifgt:
case ABCConstants.OP_ifge:
case ABCConstants.OP_ifstricteq:
case ABCConstants.OP_ifstrictne:
case ABCConstants.OP_jump:
case ABCConstants.OP_lookupswitch:
return true;
}
return false;
}
/**
* @return a string representation of the opcode.
*/
@Override
public String toString()
{
return decodeOp(opcode);
}
protected void unsupported(String diagnostic)
{
throw new UnsupportedOperationException(String.format(diagnostic, toString()));
}
}