Package org.xmlvm.refcount

Source Code of org.xmlvm.refcount.ReferenceCounting$OneRecusiveCall

/* 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.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.jdom.Attribute;
import org.jdom.DataConversionException;
import org.jdom.Element;
import org.jdom.Namespace;
import org.xmlvm.refcount.optimizations.DeferredNullingOptimization;
import org.xmlvm.refcount.optimizations.RefCountOptimization;
import org.xmlvm.refcount.optimizations.RegisterSizeAndNullingOptimization;

/**
* Overview:
*
* This class uses reference counting to simulate the effects of a JVM garbage
* collector. It operates on the output of the DEX XMLVM process which is a XML
* based representation of a compiled java class that has been transformed from
* a stack machine to a register machine. This class manages object lifespan
* using a reference counting approach. For example, when an object is created
* it is given a reference count of 1. During the lifespan of the object the
* reference count is incremented as other objects store away pointers to the
* object in question. Eventually, the reference count is set to zero and the
* memory for the object is released.
*
* This class is interesting because manages these reference counts without any
* client programmer intervention to simulate the effects of a garbage. This is
* done by following a few simple rules: 1) Each pointer to an object always has
* a 'retain' associated with it. For example, if a register variable points
* toward an object then the following pattern would occur: Register r1 =
* object1; [r1 retain];
*
* When the pointer's end of life occurred, we would release the object
* reference [r1 release]; r1 = null;
*
* The same concept holds for other classes of pointer including, arrays, static
* references to objects, as well as instance references. In general the
* following invariant always holds true:
*
* If there is a pointer to an object, than that pointer has associated with it
* an increment for the objects reference count. If that pointer is overwritten
* for any reason, that reference is released.
*
* Based on this invariant, we know that when we can no longer point to an
* object all of its reference count increments will be gone and hence it will
* be freed by the runtime.
*
* Usage: For a <dex:method/> represented as a jdom.Element, call Process.
*/
public class ReferenceCounting
{
  Namespace dex= InstructionProcessor.dex;
  Namespace vm= InstructionProcessor.vm;
  String tmpRegNameSuffix= "tmp";

  /**
   * The entry point to this class. This function takes a method element and
   * processes it, adding instructions to release and retain objects as
   * needed. For the command set that it adds, see the InstructionProcessor
   * class.
   */
  @SuppressWarnings("unchecked")
  public void process(Element method) throws DataConversionException, ReferenceCountingException
  {

    Attribute isAbstract= method.getAttribute("isAbstract");
    Attribute isNative= method.getAttribute("isNative");
    // abstract and native methods do not require processing
    if (isAbstract != null && isAbstract.getBooleanValue())
    {
      return;
    }

    if (isNative != null && isNative.getBooleanValue())
    {
      return;
    }

    Element codeElement= method.getChild("code", dex);

    int numReg= codeElement.getAttribute("register-size").getIntValue();
    processRecStart(numReg, (List<Element>) codeElement.getChildren(), codeElement);
  }

  /**
   * Set the expected frees that we must do before any optimizations have
   * removed them.
   */
  private void setWillFree(Map<Element, InstructionActions> beenTo) throws ReferenceCountingException, DataConversionException
  {
    {
      for (Map.Entry<Element, InstructionActions> e : beenTo.entrySet())
      {
        RegisterSet objectRegs= e.getValue().getObjectRegs();

        if (!e.getValue().getConflict().isEmpty())
        {
          throw new ReferenceCountingException("Ambigious register contents possible: Conflict: " + e.getValue().getConflict());
        }

        InstructionUseInfo useInfo= e.getValue().useInfo;
        RegisterSet toFree;
        if (e.getKey().getName().startsWith("return"))
        {
          // we want to free everything except what this instruction
          // uses.
          toFree= objectRegs.andNot(useInfo.usedReg());
        }
        else
        {
          // we free any register reference that is overwritten by
          // this
          // instruction
          toFree= objectRegs.and(useInfo.allWrites());
        }
        useInfo.willFree= toFree;
        useInfo.willNull= toFree.clone();

      }
    }
  }

  /**
   * This is the last step in the release/retain markup process. It processes
   * all of the DEX instructions that we have traversed in a method, looking
   * at how they have been marked up. Based on how they have been marked up it
   * adds required release/retains or other commands to the body of the method
   * being processed.
   *
   * Returns whether this method needs to have a temp register defined.
   */
  private boolean processReleaseRetain(Map<Element, InstructionActions> beenTo) throws ReferenceCountingException, DataConversionException
  {
    boolean needsTmpReg= false;

    for (Map.Entry<Element, InstructionActions> e : beenTo.entrySet())
    {

      if (!e.getValue().getConflict().isEmpty())
      {
        throw new ReferenceCountingException("Ambigious register contents possible: Conflict: " + e.getValue().getConflict());
      }

      InstructionUseInfo useInfo= e.getValue().useInfo;

      // if we are writing into an object, we may need to free.
      // All objects in registers are held with a reference, so we will
      // need to release.
      List<Element> toAddBefore= new ArrayList<Element>();
      List<Element> toAddAfter= new ArrayList<Element>();
      // Release last -- because other wise we can get into odd situations
      // where we don't to a required retain before the release.
      List<Element> toReleaseLast= new ArrayList<Element>();
      RegisterSet toFree;
      toFree= useInfo.willFree;
      // for the registers we want to free
      for (int oneReg : toFree)
      {
        // If we use the object in the instruction as an argument and
        // overwrite it, we must be careful to preserve it until after
        // the call is done.
        // Example of true case
        // tmp = f1;
        // f1 = func(f1);
        // [release tmp];
        // Example of false case:
        // f1 = func(f1);
        // [release f1]
        if (!useInfo.usesAsObj().and(useInfo.allWrites()).isEmpty())
        {
          if (useInfo.freeTmpAfter)
          {
            throw new ReferenceCountingException("Conflict, tmp register used twice.");
          }

          Element tmpR= new Element(InstructionProcessor.cmd_tmp_equals_r, vm);
          tmpR.setAttribute("reg", oneReg + "");
          toAddBefore.add(tmpR);
          needsTmpReg= true;

          Element releaseTmp= new Element(InstructionProcessor.cmd_release, vm);
          releaseTmp.setAttribute("reg", tmpRegNameSuffix);
          toReleaseLast.add(releaseTmp);

          Element nullTmp= new Element(InstructionProcessor.cmd_set_null, vm);
          nullTmp.setAttribute("num", tmpRegNameSuffix);
          toReleaseLast.add(nullTmp);

        }
        else
        {
          // No need to use tmp
          Element release= new Element(InstructionProcessor.cmd_release, vm);
          release.setAttribute("reg", oneReg + "");
          toAddBefore.add(release);

          if (useInfo.willNull.has(oneReg))
          {
            Element nullTmp= new Element(InstructionProcessor.cmd_set_null, vm);
            nullTmp.setAttribute("num", oneReg + "");
            toAddBefore.add(nullTmp);

          }
        }
      }

      if (useInfo.putRelease != null)
      {
        if (!useInfo.usesAsObj().and(useInfo.allWrites()).isEmpty())
        {
          needsTmpReg= true;
          throw new ReferenceCountingException("We do not handle the case where a release is " + "made in a x = foo(x) situation because it " + " hasn't showed up so far");
        }
        else
        {
          toAddBefore.add(useInfo.putRelease);
        }
      }

      // Add any necessary retains.
      for (int oneReg : useInfo.requiresRetain)
      {
        Element retain= new Element(InstructionProcessor.cmd_retain, vm);
        retain.setAttribute("reg", oneReg + "");
        toAddAfter.add(retain);
      }

      // This handles the case where xmlvm2objc.xsl has set the temp reg
      // to a value because a function call was made, but the result was
      // not used by the program.
      if (useInfo.freeTmpAfter)
      {

        Element releaseTmp= new Element(InstructionProcessor.cmd_release, vm);
        releaseTmp.setAttribute("reg", tmpRegNameSuffix);
        toAddAfter.add(releaseTmp);
        needsTmpReg= true;

      }
      toAddAfter.addAll(toReleaseLast);

      // At this point toAddBefore and toAddAfter have been filled with
      // whatever instructions we need to add before and after this
      // specific element. The helper function adds them.
      addBeforeAndAfter(e.getKey(), toAddBefore, toAddAfter);

    }
    return needsTmpReg;
  }

  /**
   * This is here because the jdom XML API is dumb enough that it cannot
   * easily find the element before element X, or the element after element X.
   *
   * This function adds some elements before and after a particular element.
   *
   * TODO: if we believe prevElement map and nextElement map are correct, then
   * we can use them instead to make this run faster.
   */
  @SuppressWarnings("unchecked")
  void addBeforeAndAfter(Element toAddTo, List<Element> toAddBefore, List<Element> toAddAfter) throws ReferenceCountingException
  {
    Element parent= toAddTo.getParentElement();
    List<Object> con= parent.getContent();

    // go through the parents elements looking for this element
    for (int x= 0; x < con.size(); x++)
    {
      if (con.get(x).equals(toAddTo))
      {
        // order here matters so we don't screw up the index for the
        // before add.
        parent.addContent(x + 1, toAddAfter);
        parent.addContent(x, toAddBefore);

        return;
      }
    }
    throw new ReferenceCountingException("Impossible");
  }

  /**
   * label id to label element. Used for construction of code paths.
   */
  Map<Integer, Element> labels= new HashMap<Integer, Element>();
  /**
   * What is the next and previous element for a particular element ?
   */
  Map<Element, Element> nextElement= new HashMap<Element, Element>();
  Map<Element, Element> prevElement= new HashMap<Element, Element>();

  /**
   * Represents a single run of the reference counter. We have this because we
   * currently use a two pass implementation and don't want any interactions
   * between the passes.
   *
   * TODO: We could prevent having to do a whole lot of work in the second
   * pass if we care to.
   */
  class RunState
  {
    public List<CodePath> allCodePaths= new ArrayList<CodePath>();
    /*
     * What we label the next code path as
     */
    public int codePathId= 0;
    /*
     * List of all the elements that we have visited in the method.
     */
    public Map<Element, InstructionActions> beenTo= new HashMap<Element, InstructionActions>();
    /*
     * What are the conflicted registers on this run?
     */
    public RegisterSet allConflict= RegisterSet.none();
    /*
     * Used so we don't have to use stack recursion, which apparently causes
     * big problems in the JVM.
     */
    LinkedList<OneRecusiveCall> callsToDo= new LinkedList<OneRecusiveCall>();
  }

  /*
   * Our current run context. TODO: pass this down the stack instead of having
   * it be an instance variable.
   */
  RunState curRun;

  /**
   * This adds any labels it finds to our labels map. It also populates our
   * previous and next element hashes.
   */
  private void addToNextPrevElement(List<Element> toProcess) throws DataConversionException
  {
    Element prev= null;
    for (int k= 0; k < toProcess.size(); k++)
    {
      Element cur= toProcess.get(k);

      if (cur.getName().equals("label"))
      {
        labels.put(cur.getAttribute("id").getIntValue(), cur);
      }

      if (prev != null)
      {
        nextElement.put(prev, cur);
        prevElement.put(cur, prev);
      }
      prev= cur;
    }
  }

  @SuppressWarnings("unchecked")
  private void processRecStart(int numReg, List<Element> toProcess, Element codeElement) throws DataConversionException, ReferenceCountingException
  {
    addToNextPrevElement(toProcess);
    for (Element x : toProcess)
    {
      if (x.getName().equals("try-catch"))
      {
        // Try catches are special: we must descend into them to
        // mark up their code.
        addToNextPrevElement(x.getChild("try", dex).getChildren());
        for (Element catchE : (List<Element>) x.getChildren("catch", dex))
        {
          addToNextPrevElement(catchE.getChildren());
        }
      }

    }

    doMarkup(toProcess);

    curRun.allConflict= RegisterSet.none();
    for (Entry<Element, InstructionActions> x : curRun.beenTo.entrySet())
    {
      curRun.allConflict.orEq(x.getValue().getConflict());
    }

    // the method element.
    Element parent= toProcess.get(0).getParentElement();

    // We found some code paths that end up with a register that has an
    // object OR a primitive at a point where we think we need to do a
    // object release. This is bad, so we must split the register so that
    // the primitive is always separate from the object.
    if (!curRun.allConflict.isEmpty())
    {
      int newRegSize= splitConflictedRegisters(numReg, curRun.allConflict, curRun.beenTo);
      parent.getAttribute("register-size").setValue(newRegSize + "");

      doMarkup(toProcess);

    }

    refLog("Conflict is: " + curRun.allConflict);

    setWillFree(curRun.beenTo);

    // Start going through optimizations before generating change
    RefCountOptimization.ReturnValue ret= new RegisterSizeAndNullingOptimization().Process(curRun.allCodePaths, curRun.beenTo, codeElement);

    new DeferredNullingOptimization().Process(curRun.allCodePaths, curRun.beenTo, codeElement);

    // TODO fix this optimization
    // new ExcessRetainsOptimization().Process(curRun.allCodePaths,
    // curRun.beenTo, codeElement);
    toProcess.addAll(0, ret.functionInit);

    addExTempReg(toProcess);

    clearReleaseRetainOnSyntheticMembers(curRun, codeElement);
    // Now we want to follow the paths to find unambiguous ones so that we
    // can determine
    // where to do release/retain to prevent ambiguity.
    // we do this by tracking which branch we are on by explicitly
    // constructing paths through the code during
    // our normal traversal.
    boolean usesTemp= processReleaseRetain(curRun.beenTo);
    if (usesTemp)
    {
      Element setupTmp= new Element(InstructionProcessor.cmd_define_register, InstructionProcessor.vm);
      setupTmp.setAttribute("vartype", InstructionProcessor.cmd_define_register_attr_temp);
      toProcess.add(0, setupTmp);
    }

  }

  /*
   * Synthetics help create cycles so we don't do releases or retains on them.
   */
  @SuppressWarnings("unchecked")
  private void clearReleaseRetainOnSyntheticMembers(RunState curRun, Element codeElement) throws DataConversionException
  {
    // Find the synthetic members of the class;
    Element classElement= codeElement.getParentElement().getParentElement();

    HashSet<String> hashSet= new HashSet<String>();

    for (Element elem : (List<Element>) classElement.getChildren())
    {
      if (elem.getName().equals("field") && elem.getAttribute("isSynthetic") != null && elem.getAttributeValue("isSynthetic").equals("true") && elem.getAttributeValue("name").startsWith("this$"))
      {
        hashSet.add(elem.getAttributeValue("name"));
      }
    }

    for (Map.Entry<Element, InstructionActions> e : curRun.beenTo.entrySet())
    {

      String instructionElementName= e.getKey().getName();
      if ((instructionElementName.equals("iput-object") || instructionElementName.equals("iput")) && e.getKey().getAttribute("member-name") != null && hashSet.contains(e.getKey().getAttributeValue("member-name")))
      {
        InstructionUseInfo useInfo= e.getValue().useInfo;
        // We don't want to release what was in there because it was not
        // retained
        useInfo.putRelease= null;
        useInfo.requiresRetain= RegisterSet.none();
      }
    }
  }

  /**
   * Adds definition for exception register if needed.
   */
  private void addExTempReg(List<Element> toProcess)
  {
    boolean useEx= false;
    boolean useTmp= false;
    for (Element e : curRun.beenTo.keySet())
    {
      if (e.getName().equals("throw") || e.getName().equals("try-catch"))
      {
        useEx= true;
      }

      if (useEx && useTmp)
      {
        break; // early quit
      }
    }
    if (useEx)
    {
      Element setupEx= new Element(InstructionProcessor.cmd_define_register, InstructionProcessor.vm);
      setupEx.setAttribute("vartype", InstructionProcessor.cmd_define_register_attr_exception);
      toProcess.add(0, setupEx);
    }
    for (Element e : curRun.beenTo.keySet())
    {
      if (e.getName().startsWith("return"))
      {
        e.setAttribute("catchesException", useEx + "");
      }
    }
  }

  /**
   * Determines conflicts and retain/release for the method.
   */
  private void doMarkup(List<Element> toProcess) throws DataConversionException
  {
    // create a new run of the processor, prime the recursion, and then
    // run it until its complete.
    curRun= new RunState();
    processRecAdd(RegisterSet.none(), RegisterSet.none(), toProcess.get(0), createNewCodePath(null));
    processWhileCallsToDo();
    // Debug print for state at this stage.
    printInstSeq(toProcess);
  }

  /*
   * Purely for debug, shows the instructions as well as our what we have
   * calculated about them.
   */
  @SuppressWarnings("unchecked")
  private void printInstSeq(List<Element> toProcess)
  {
    refLog("All " + toProcess.size() + " instructions been to " + curRun.beenTo.size());
    for (Element x : toProcess)
    {
      if (curRun.beenTo.containsKey(x))
      {
        String startStr= curRun.beenTo.get(x).useInfo + "";
        if (x.getName().equals("label"))
        {
          refLog(startStr + " ID = " + x.getAttributeValue("id"));
        }
        else
        {
          refLog(startStr + "");
        }

        if (x.getName().equals("try-catch"))
        {
          printInstSeq(x.getChild("try", dex).getChildren());
        }
      }
    }
  }

  /**
   * In certain cases, DEX will create a code path where we think we need to
   * do a release of an object on a particular register which may or may not
   * hold an object depending on the particular path through the code taken at
   * runtime. There are several ways to approach this issue, the most simple
   * is to split a conflicted register into two new registers. Conceptually,
   * this is done by defining a function that maps RX to RY or RZ depending on
   * whether RX is known to hold an object or a primitive.
   *
   * The following code implements this mapping, with the slight optimization
   * that instead of mapping RX to RY and RZ, it maps it to RX and RY. This is
   * because it keeps the sequence of registers intact with no holes, and
   * because it allows us to deal with function parameters more easily.
   *
   * We return the total number of registers required for this method.
   */
  int splitConflictedRegisters(int numReg, RegisterSet allConflict, Map<Element, InstructionActions> beenTo) throws DataConversionException, ReferenceCountingException
  {

    for (int reg : allConflict)
    {
      int newReg= numReg++;

      // When dealing with a passed parameter that has a conflict, we want
      // to make sure to not change the register that the parameter is
      // originally inserted into.
      int regObj= reg;
      int regNonObj= newReg;

      for (Element varE : beenTo.keySet())
      {
        if (varE.getName().equals("var"))
        {
          InstructionUseInfo varUi= this.curRun.beenTo.get(varE).useInfo;
          if (varUi.isWrite)
          {
            if (!varUi.writesObj().isEmpty())
            {
              regObj= reg;
              regNonObj= newReg;
              break;
            }
            else if (!varUi.writesNonObj().isEmpty())
            {
              regObj= newReg;
              regNonObj= reg;
            }
            else
            {
              throw new ReferenceCountingException("impossible");
            }
          }
        }
      }

      refLog(reg + " -> o:" + regObj + ":" + regNonObj);

      // Go through all the instructions in the method, replacing any
      // that use the conflicted value with the new register or the old
      // register depending on whether the instruction expects the
      // register to contain an object or a primitive.
      for (Map.Entry<Element, InstructionActions> beenToKv : beenTo.entrySet())
      {
        InstructionUseInfo ui= beenToKv.getValue().useInfo;

        for (Map.Entry<Attribute, Boolean> kv : ui.typeIsObj.entrySet())
        {
          if (kv.getKey().getIntValue() == reg)
          {
            if (kv.getValue())
            {
              // its an object
              kv.getKey().setValue(regObj + "");
            }
            else
            {
              kv.getKey().setValue(regNonObj + "");
            }
          }
        }
      }
    }
    return numReg;
  }

  /**
   * Given a parent code path, create a child.
   */
  private CodePath createNewCodePath(CodePath curPath)
  {
    CodePath c;
    c= new CodePath(curRun.codePathId++, curPath);
    curRun.allCodePaths.add(c);
    if (curPath != null)
    {
      curPath.subPaths.add(c);
    }
    return c;
  }

  /**
   * Class representing collected parameters for one execution of the body of
   * ProcessWhileCallsToDo
   */
  class OneRecusiveCall
  {
    RegisterSet regHoldingObject;
    RegisterSet regNotHoldingObject;
    Element currentElement;
    CodePath codePath;
  }

  /**
   * Helper to add to the list of recursive calls to do.
   */
  private void processRecAdd(RegisterSet regHoldingObject, RegisterSet regNotHoldingObject, Element currentElement, CodePath codePath) throws DataConversionException
  {
    OneRecusiveCall oneCall= new OneRecusiveCall();
    oneCall.regHoldingObject= regHoldingObject;
    oneCall.regNotHoldingObject= regNotHoldingObject;
    oneCall.currentElement= currentElement;
    oneCall.codePath= codePath;
    this.curRun.callsToDo.add(oneCall);
  }

  /**
   * This function creates a representation of the different execution paths
   * through the method. At the same time, it gathers information on how
   * particular instructions are making use of registers. This is an
   * implementation of a recursive function, however when implemented as a
   * directly recursive function (without the calls to do) the JVM blows up
   * for lack of stack space. Because modifying stack space available to a
   * thread in the JVM (even if you create a new thread) is a pain, we just
   * switched to using a heap list for the recursive stack.
   *
   * In most cases this is *not* tail recursion, so don't try and make a loop
   * out of it.
   */
  @SuppressWarnings("unchecked")
  private void processWhileCallsToDo() throws DataConversionException
  {
    int maxSize= 0;
    while (this.curRun.callsToDo.size() != 0)
    {
      maxSize= Math.max(maxSize, this.curRun.callsToDo.size());
      OneRecusiveCall thisTime= this.curRun.callsToDo.removeFirst();

      // Arguments to the recursive function.
      RegisterSet regHoldingObject= thisTime.regHoldingObject;
      RegisterSet regNotHoldingObject= thisTime.regNotHoldingObject;
      Element currentElement= thisTime.currentElement;
      CodePath codePath= thisTime.codePath;

      if (currentElement == null)
      {
        continue; // base case
      }

      InstructionActions actions= beenHereBefore(this.curRun.beenTo, regHoldingObject, regNotHoldingObject, currentElement, codePath);
      if (actions == null)
      {
        continue; // base case
      }
      InstructionUseInfo useInfo= actions.useInfo;

      Element nextInstruction;

      if (currentElement.getName().startsWith("goto"))
      {
        nextInstruction= labels.get(currentElement.getAttribute("target").getIntValue());
      }
      else
      {
        nextInstruction= nextElement.get(currentElement);
        if (nextInstruction == null && currentElement.getParentElement().getName().equals("try"))
        {
          // Exited the try, move to the element after the
          // try terminates.
          nextInstruction= nextElement.get(currentElement.getParentElement().getParentElement());
        }
      }

      // the ones we came in with
      RegisterSet ourObjUse= regHoldingObject.clone();
      // plus the ones that we write obj to
      ourObjUse.orEq(useInfo.writesObj());
      // minus the ones we write non obj into
      ourObjUse.andEqNot(useInfo.writesNonObj());

      RegisterSet ourNonObjUse= regNotHoldingObject.clone();
      ourNonObjUse.orEq(useInfo.writesNonObj());
      ourNonObjUse.andEqNot(useInfo.writesObj());

      if (currentElement.getName().startsWith("return"))
      {
        continue;
      }

      if (currentElement.getName().equals("try-catch"))
      {
        processRecAdd(ourObjUse, ourNonObjUse, (Element) currentElement.getChild("try", dex).getChildren().get(0), createNewCodePath(codePath));

        for (Element caught : (List<Element>) currentElement.getChildren("catch", dex))
        {
          Element nextInst= labels.get(caught.getAttribute("target").getIntValue());

          processRecAdd(ourObjUse, ourNonObjUse, nextInst, createNewCodePath(codePath));
        }

      }
      else if (currentElement.getName().equals("packed-switch") || currentElement.getName().equals("sparse-switch"))
      {
        processRecAdd(ourObjUse, ourNonObjUse, nextInstruction, createNewCodePath(codePath));

        for (Element target : (List<Element>) currentElement.getChildren("case", dex))
        {
          processRecAdd(ourObjUse, ourNonObjUse, labels.get(target.getAttribute("label").getIntValue()), createNewCodePath(codePath));
        }
      }
      else if (currentElement.getName().startsWith("if"))
      {
        processRecAdd(ourObjUse, ourNonObjUse, nextInstruction, createNewCodePath(codePath));
        processRecAdd(ourObjUse, ourNonObjUse, labels.get(currentElement.getAttribute("target").getIntValue()), createNewCodePath(codePath));
      }
      else if (currentElement.getName().equals("label"))
      {
        // It will be useful in the future to have labels treated as
        // creating a new code path.
        processRecAdd(ourObjUse, ourNonObjUse, nextInstruction, createNewCodePath(codePath));

      }
      else
      {
        // straight line code.
        processRecAdd(ourObjUse, ourNonObjUse, nextInstruction, codePath);
      }
    }
    refLog("Max recusrive depth " + maxSize);
  }

  /**
   * This thing is used to determine whether or not our recursion keeps going
   * It terminates the recursion if we have been to this instruction with the
   * exact same state before. We return information collected about the
   * current instruction to the caller.
   */
  private static InstructionActions beenHereBefore(Map<Element, InstructionActions> beenTo, RegisterSet regHoldingObject, RegisterSet regNotHoldingObject, Element currentElement, CodePath c) throws DataConversionException
  {
    InstructionActions toRet;

    if (beenTo.containsKey(currentElement))
    {
      // Visited here on another code branch
      toRet= beenTo.get(currentElement);
    }
    else
    {
      // Haven't been here yet.
      toRet= new InstructionActions();
      toRet.useInfo= processElement(currentElement);
      beenTo.put(currentElement, toRet);

    }

    // Labels are not code, and thus do not belong in code paths.
    if (!currentElement.getName().equals("label"))
    {
      c.path.add(new OnePathInstructionRegisterContents(currentElement, regHoldingObject, regNotHoldingObject));
    }

    boolean enteredNotHolding= false;
    // figure out if we have been here before with the same
    // enteredNotHolding state
    for (RegisterSet m : toRet.enteredNot)
    {
      if (m.equals(regNotHoldingObject))
      {
        enteredNotHolding= true;
        break;
      }
    }

    boolean enteredHolding= false;
    // figure out if we have been here before with the same enteredHolding
    // state.
    for (RegisterSet m : toRet.enteredHoldingObj)
    {
      if (m.equals(regHoldingObject))
      {
        enteredHolding= true;
        break;
      }
    }

    if (enteredNotHolding && enteredHolding)
    {
      // We were here before with the exact same state: time to terminate
      // the search along this path.
      return null;
    }
    // Add info about the state we were in when we got to here along this
    // code path.
    if (!enteredHolding)
    {
      toRet.enteredHoldingObj.add(regHoldingObject);
    }
    if (!enteredNotHolding)
    {
      toRet.enteredNot.add(regNotHoldingObject);
    }
    return toRet;
  }

  /**
   * This function creates a InstructionUseInfo based on the current element
   * TODO: if anyone really cares this can be made faster by not using
   * reflection.
   */
  private static InstructionUseInfo processElement(Element element) throws DataConversionException
  {
    InstructionUseInfo use= new InstructionUseInfo(element);
    // If we find the instruction using the generic handler, return
    // immediately.
    if (InstructionProcessor.processGeneric(element, use))
    {
      return use;
    }
    else
    {
      // Otherwise, we need to hit the correct processor function:
      String todo= "process_" + element.getName().replace("-", "_");
      Method method;
      try
      {
        method= InstructionProcessor.class.getMethod(todo, Element.class, InstructionUseInfo.class);
        method.invoke(null, element, use);
      }
      catch (Exception ex)
      {
        throw new DataConversionException(ex.getMessage(), "When attempting to: " + todo);
      }
    }
    return use;
  }

  private static void refLog(String message)
  {
    // Log.debug("ref", message);
  }

}
TOP

Related Classes of org.xmlvm.refcount.ReferenceCounting$OneRecusiveCall

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.