/* * * 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())); } }