org.xmlvm.refcount.InstructionProcessor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dragome-bytecode-js-compiler Show documentation
Show all versions of dragome-bytecode-js-compiler Show documentation
Dragome SDK module: bytecode to javascript compiler
/* Copyright (c) 2002-2011 by XMLVM.org
*
* Project Info: http://www.xmlvm.org
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package org.xmlvm.refcount;
import java.util.List;
import java.util.regex.Pattern;
import org.jdom.DataConversionException;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.Attribute;
/**
* The sole job of this class is to fill in a InstructionUseInfo after being fed
* a particular instruction. This includes determining how the instruction uses
* objects. See InstructionUseInfo for more information.
*
* In general, anything in this class must always set InstructionUseInfo.isWrite
* correctly. Forgetting to set it will cause bugs later on.
*/
public class InstructionProcessor
{
/**
* Causes an increment for the reference count of the object in "reg"
* Example:
*/
public final static String cmd_release= "reg-release";
/**
* Causes a decrement for the reference count of the object in "reg"
* Example:
*/
public final static String cmd_retain= "reg-retain";
/**
* Causes a decrement for the reference count of an instance pointer in a
* class. The supplied attributes are identical to that of any other
* instance referencing instruction. Example:
*/
public final static String cmd_i_release= "i-release";
/**
* Causes a decrement for the reference count of an static pointer in a
* class. The supplied attributes are identical to that of any other static
* referencing instruction. Example:
*/
public final static String cmd_s_release= "s-release";
/**
* Causes a decrement for the reference count of an object in an array. The
* supplied attributes are that which are sufficient to access the array in
* the xsl transform. Example:
*/
public final static String cmd_a_release= "a-release";
/**
* Sets the temp register equal to the value of another register. Example:
*
*/
public final static String cmd_tmp_equals_r= "tmp-equals-r";
/**
* Define a register, give it a name. Three types allowed: 'register' stores
* a variable, 'temp' a holder register that is used by XMLVM (rather than
* dex) to store things. 'exception' stores a reference to an exception
*/
public final static String cmd_define_register= "define-register";
/**
* @see cmd_define_register
*/
public final static String cmd_define_register_attr_register= "register";
/**
* @see cmd_define_register
*/
public final static String cmd_define_register_attr_temp= "temp";
/**
* @see cmd_define_register
*/
public final static String cmd_define_register_attr_exception= "exception";
/**
* Set a register to null
*/
public final static String cmd_set_null= "set-null";
/**
* init locals from arguments
*/
public final static String cmd_move_argument= "move-argument";
/**
* Insert a comment in the generated code
*/
public final static String cmd_comment= "comment";
public final static Namespace dex= Namespace.getNamespace("dex", "http://xmlvm.org/dex");
public final static Namespace vm= Namespace.getNamespace("vm", "http://xmlvm.org");
/*
* Most of the time, the destination register in dex is VX, this is a helper
* function to get the register index for VX.
*/
static RegisterSet getDestReg(Element x) throws DataConversionException
{
return getDestReg(x, "vx");
}
/*
* Get the register index for an instruction given the name of the element
* that specifies the register.
*/
static RegisterSet getDestReg(Element x, String name) throws DataConversionException
{
return RegisterSet.from(x.getAttribute(name).getIntValue());
}
/*
* The following patterns help us identify and act upon instructions in DEX
* that are similar.
*/
static String nonObjTypesString= "(wide|boolean|byte|char|short|int|double|float|long)";
static Pattern match3Op= Pattern.compile("^" + "(add|sub|mul|div|rem|and|or|xor|shl|shr|ushr)-" + nonObjTypesString + ".*");
static Pattern match2Op= Pattern.compile("^" + "(add|sub|mul|div|rem|and|or|xor|shl|shr|ushr)-" + nonObjTypesString + "-2addr" + ".*");
static Pattern matchOneOp= Pattern.compile("^" + "(add|rsub|mul|div|rem|and|or|xor)-" + nonObjTypesString + "-lit(8|16)" + ".*");
static Pattern matchIf1= Pattern.compile("^" + "(if)-(eq|ne|lt|ge|gt|le)z" + ".*");
static Pattern matchIf2= Pattern.compile("^" + "(if)-(eq|ne|lt|ge|gt|le)" + ".*");
static Pattern iput= Pattern.compile("^" + "(iput|sput)-" + nonObjTypesString + ".*");
static Pattern iget= Pattern.compile("^" + "(iget|sget)-" + nonObjTypesString + ".*");
static Pattern invoke= Pattern.compile("^" + "invoke-" + ".*");
static Pattern constDef= Pattern.compile("^" + "const" + ".*");
static Pattern aputNonObj= Pattern.compile("^" + "aput-" + nonObjTypesString + ".*");
static Pattern agetNonObj= Pattern.compile("^" + "aget-" + nonObjTypesString + ".*");
static Pattern conversionNegNot= Pattern.compile("^" + "(neg|not)-" + nonObjTypesString + ".*");
static Pattern conversionfromTo= Pattern.compile("^" + nonObjTypesString + "-to-" + nonObjTypesString + ".*");
static Pattern compInstr= Pattern.compile("^" + "cmp" + ".*");
public static Pattern nonObjTypes= Pattern.compile("^" + nonObjTypesString);
/**
* We try and match the instruction to an action given our list of regex.
* Returns whether we were able to find a match and fill in the i. If it
* returns false, the caller must look for a more specific handler.
*/
static boolean processGeneric(Element element, InstructionUseInfo i) throws DataConversionException
{
String elemName= element.getName();
if (matchOneOp.matcher(elemName).matches())
{
i.isWrite= true;
}
else if (match2Op.matcher(elemName).matches())
{
i.isWrite= true;
}
else if (match3Op.matcher(elemName).matches())
{
i.isWrite= true;
}
else if (matchIf1.matcher(elemName).matches())
{
i.isWrite= false;
}
else if (matchIf2.matcher(elemName).matches())
{
i.isWrite= false;
}
else if (iput.matcher(elemName).matches())
{
i.isWrite= false;
}
else if (iget.matcher(elemName).matches())
{
i.isWrite= true;
}
else if (invoke.matcher(elemName).matches())
{
getUsedFromParams(element, i);
getMoveResult(element, i);
}
else if (constDef.matcher(elemName).matches())
{
if (elemName.equals("const-class"))
{
}
// TODO: verify const-class is OK.
i.isWrite= true;
}
else if (aputNonObj.matcher(elemName).matches())
{
i.isWrite= false;
}
else if (agetNonObj.matcher(elemName).matches())
{
i.isWrite= true;
}
else if (conversionNegNot.matcher(elemName).matches() || conversionfromTo.matcher(elemName).matches())
{
i.isWrite= true;
}
else if (compInstr.matcher(elemName).matches())
{
i.isWrite= true;
}
else
{
return false;
}
return true;
}
/*
* Helper for processing function call parameters.
*/
@SuppressWarnings("unchecked")
static void getUsedFromParams(Element funcCallElement, InstructionUseInfo i) throws DataConversionException
{
Element pHolder= funcCallElement.getChild("parameters", dex);
List params= (List) pHolder.getChildren("parameter", dex);
for (Element parameter : params)
{
i.checkUsage(parameter.getAttribute("register"), parameter.getAttribute("type"));
}
}
/*
* Helper for processing function call returns.
*/
static void getMoveResult(Element funcCallElement, InstructionUseInfo i) throws DataConversionException
{
String returnType= funcCallElement.getChild("parameters", dex).getChild("return", dex).getAttribute("type").getValue();
boolean returnsVoid= returnType.equals("void");
Element pHolder= funcCallElement.getChild("move-result", dex);
if (pHolder != null)
{
i.checkUsage(pHolder.getAttribute("vx"), pHolder.getAttribute("vx-type"));
i.isWrite= true;
}
else
{
if (!returnsVoid && !nonObjTypes.matcher(returnType).matches())
{
// if the result is an object and it is not used, we must
// free rTmp. Recall that xmlvm2objc.xsl sticks unused results
// into rTmp.
i.freeTmpAfter= true;
}
i.isWrite= false;
}
}
/*
* The following functions are handers for specific instructions. They are
* named to correspond exactly with the instruction name so that reflection
* can be used to call into them.
*
* In general none of them are going to make any sense what so ever until
* you look at them along side the XML they process and what can be filled
* out in InstructionUseInfo. Hence the lack of specific comments.
*
* Key to note is that they all should fill out i.isWrite before returning
*
* They may also fill out information on retains/releases, or temp register
* usage.
*/
static public void process_return(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_return_void(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_new_instance(Element element, InstructionUseInfo i)
{
i.isWrite= true;
}
static public void process_move_object_from16(Element element, InstructionUseInfo i) throws DataConversionException
{
i.isWrite= true;
i.requiresRetain.orEq(i.writesObj());
}
static public void process_move_exception(Element element, InstructionUseInfo i) throws DataConversionException
{
i.isWrite= true;
// Exceptions don't require retains as they use the retain count the
// exception had when it was thrown
}
static public void process_move_object(Element element, InstructionUseInfo i) throws DataConversionException
{
i.isWrite= true;
i.requiresRetain.orEq(i.writesObj());
}
static public void process_move_wide(Element element, InstructionUseInfo i)
{
i.isWrite= true;
}
static public void process_move_wide_from16(Element element, InstructionUseInfo i)
{
i.isWrite= true;
}
static public void process_move_from16(Element element, InstructionUseInfo i)
{
i.isWrite= true;
}
static public void process_move(Element element, InstructionUseInfo i)
{
i.isWrite= true;
}
static public void process_return_object(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_var(Element element, InstructionUseInfo i) throws DataConversionException
{
i.isWrite= true;
i.possibleWrites.clear();
i.possibleWrites.add("register");
i.requiresRetain= i.writesObj();
}
static public void process_source_position(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_label(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_goto(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_goto_32(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_goto_16(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_aput(Element element, InstructionUseInfo i) throws DataConversionException
{
i.isWrite= false;
i.requiresRetain.orEq(getDestReg(element, "vx").and(i.usesAsObj()));
}
static public void process_aget(Element element, InstructionUseInfo i) throws DataConversionException
{
i.isWrite= true;
i.requiresRetain.orEq(i.writesObj());
}
@SuppressWarnings("unchecked")
static public void process_aput_object(Element element, InstructionUseInfo i) throws DataConversionException
{
i.requiresRetain.orEq(getDestReg(element, "vx").and(i.usesAsObj()));
if (!i.requiresRetain.isEmpty())
{
Element toAdd= new Element(cmd_a_release, vm);
for (Attribute a : (List) i.Instruction.getAttributes())
{
toAdd.setAttribute(a.getName(), a.getValue(), a.getNamespace());
}
i.putRelease= toAdd;
}
i.isWrite= false;
}
static public void process_aget_object(Element element, InstructionUseInfo i) throws DataConversionException
{
i.isWrite= true;
i.requiresRetain.orEq(i.writesObj());
}
@SuppressWarnings("unchecked")
static public void process_iput_object(Element element, InstructionUseInfo i) throws DataConversionException
{
// need to release before we overwrite
i.requiresRetain.orEq(getDestReg(element, "vx").and(i.usesAsObj()));
if (!i.requiresRetain.isEmpty())
{
Element toAdd= new Element(cmd_i_release, vm);
for (Attribute a : (List) i.Instruction.getAttributes())
{
toAdd.setAttribute(a.getName(), a.getValue(), a.getNamespace());
}
i.putRelease= toAdd;
}
i.isWrite= false;
}
static public void process_iget_object(Element element, InstructionUseInfo i) throws DataConversionException
{
i.isWrite= true;
i.requiresRetain.orEq(i.writesObj());
}
@SuppressWarnings("unchecked")
static public void process_sput_object(Element element, InstructionUseInfo i) throws DataConversionException
{
i.requiresRetain.orEq(getDestReg(element, "vx").and(i.usesAsObj()));
if (!i.requiresRetain.isEmpty())
{
// need to release before we overwrite
Element toAdd= new Element(cmd_s_release, vm);
for (Attribute a : (List) i.Instruction.getAttributes())
{
toAdd.setAttribute(a.getName(), a.getValue(), a.getNamespace());
}
i.putRelease= toAdd;
}
i.isWrite= false;
}
static public void process_sget_object(Element element, InstructionUseInfo i) throws DataConversionException
{
i.isWrite= true;
i.requiresRetain.orEq(i.writesObj());
}
static public void process_try_catch(Element element, InstructionUseInfo i)
{
// TODO: what do we do about catches
i.isWrite= false;
}
static public void process_catches(Element element, InstructionUseInfo i)
{
// TODO: what do we do about catches
i.isWrite= false;
}
static public void process_monitor_enter(Element element, InstructionUseInfo i)
{
// TODO: what do we do about catches
i.isWrite= false;
}
static public void process_monitor_exit(Element element, InstructionUseInfo i)
{
// TODO: what do we do about catches
i.isWrite= false;
}
static public void process_sget(Element element, InstructionUseInfo i) throws DataConversionException
{
i.isWrite= true;
i.requiresRetain= i.writesObj();
}
@SuppressWarnings("unchecked")
static public void process_sput(Element element, InstructionUseInfo i) throws DataConversionException
{
i.requiresRetain.orEq(getDestReg(element, "vx").and(i.usesAsObj()));
if (!i.requiresRetain.isEmpty())
{
// need to release before we overwrite
Element toAdd= new Element(cmd_s_release, vm);
for (Attribute a : (List) i.Instruction.getAttributes())
{
toAdd.setAttribute(a.getName(), a.getValue(), a.getNamespace());
}
i.putRelease= toAdd;
}
i.isWrite= false;
}
static public void process_iget(Element element, InstructionUseInfo i) throws DataConversionException
{
i.isWrite= true;
i.requiresRetain= i.writesObj();
}
@SuppressWarnings("unchecked")
static public void process_iput(Element element, InstructionUseInfo i) throws DataConversionException
{
i.requiresRetain.orEq(getDestReg(element, "vx").and(i.usesAsObj()));
if (!i.requiresRetain.isEmpty())
{
// need to release before we overwrite
Element toAdd= new Element(cmd_i_release, vm);
for (Attribute a : (List) i.Instruction.getAttributes())
{
toAdd.setAttribute(a.getName(), a.getValue(), a.getNamespace());
}
i.putRelease= toAdd;
}
i.isWrite= false;
}
static public void process_new_array(Element element, InstructionUseInfo i)
{
i.isWrite= true;
}
static public void process_sparse_switch(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_check_cast(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_packed_switch(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_array_length(Element element, InstructionUseInfo i)
{
i.isWrite= true;
}
static public void process_return_wide(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_throw(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
static public void process_instance_of(Element element, InstructionUseInfo i)
{
i.isWrite= true;
}
static public void process_fill_array_data(Element element, InstructionUseInfo i)
{
i.isWrite= false;
}
@SuppressWarnings("unchecked")
static public void process_filled_new_array(Element element, InstructionUseInfo i) throws DataConversionException
{
Element moveResult= element.getChild("move-result", dex);
for (Element valElement : (List) element.getChildren("value", dex))
{
i.checkUsage(valElement.getAttribute("register"), valElement.getAttribute("type"));
}
i.checkUsage(moveResult.getAttribute("vx"), moveResult.getAttribute("vx-type"));
i.isWrite= true;
}
static public void process_filled_new_array_range(Element element, InstructionUseInfo i) throws DataConversionException
{
process_filled_new_array(element, i);
}
}