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

org.xmlvm.refcount.InstructionProcessor Maven / Gradle / Ivy

There is a newer version: 0.96-beta4
Show newest version
/* 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);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy