Package com.caucho.bytecode

Source Code of com.caucho.bytecode.CodeEnhancer$TableSwitch

/*
* Copyright (c) 1998-2010 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT.  See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
*   Free Software Foundation, Inc.
*   59 Temple Place, Suite 330
*   Boston, MA 02111-1307  USA
*
* @author Scott Ferguson
*/

package com.caucho.bytecode;

import com.caucho.util.ByteBuffer;
import com.caucho.util.IntArray;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Visitor for travelling the code.
*/
public class CodeEnhancer extends CodeVisitor {
  static private final Logger log = Logger.getLogger(CodeEnhancer.class.getName());
  private ByteBuffer _code;

  private ArrayList<Jump> _jumps;
  private ArrayList<Switch> _switches;
  private boolean _changeLength;

  // already visited targets
  private IntArray _pendingTargets;
  private IntArray _completedTargets;

  public CodeEnhancer()
  {
  }

  public CodeEnhancer(JavaClass javaClass, CodeAttribute code)
  {
    init(javaClass, code);
  }

  public void init(JavaClass javaClass, CodeAttribute codeAttr)
  {
    super.init(javaClass, codeAttr);

    _code = new ByteBuffer();
   
    byte []codeBuffer = codeAttr.getCode();
   
    _code.add(codeBuffer, 0, codeBuffer.length);

    _changeLength = false;
  }

  /**
   * Analyzes the code for a method
   */
  public void analyze(Analyzer analyzer, boolean allowFlow)
    throws Exception
  {
    _pendingTargets = new IntArray();
    _completedTargets = new IntArray();
   
    analyzeImpl(analyzer, allowFlow, _pendingTargets, _completedTargets);
  }

  /**
   * Returns the code buffer.
   */
  public byte []getCode()
  {
    return _code.getBuffer();
  }

  /**
   * Returns the length.
   */
  public int getLength()
  {
    return _code.getLength();
  }

  /**
   * Adds a byte to the code.
   */
  public void addByte(int offset, int value)
  {
    insertCode(offset, 1);
   
    _code.set(offset, value);
  }

  /**
   * Adds a byte to the code.
   */
  public void setByte(int offset, int value)
  {
    _code.set(offset, value);
  }

  /**
   * Adds a short to the code.
   */
  public void addShort(int offset, int value)
  {
    insertCode(offset, 2);
   
    _code.set(offset + 0, value >> 8);
    _code.set(offset + 1, value);
  }

  /**
   * Adds a byte to the code.
   */
  public void add(int offset, byte []buffer, int bufOffset, int length)
  {
    insertCode(offset, length);
   
    _code.set(offset, buffer, bufOffset, length);
  }

  /**
   * Removes a range from the code.
   */
  public void remove(int offset, int count)
  {
    removeCode(offset, count);
  }

  /**
   * Adds a byte to the code.
   */
  public void addNulls(int offset, int count)
  {
    insertCode(offset, count);
  }

  /**
   * Updates indices when adding a chunk of code.  The operation at
   * the given offset moves, e.g. adding 6 bytes to the beginning of
   * the program moves the initial byte down by 6 and therefore needs
   * to update the links as well.
   *
   * Therefore, enhancers which expand an opcode from 2 bytes to 3 bytes
   * must insert the new bytes after the initial opcode.
   */
  protected void insertCode(int offset, int count)
  {
    if (_jumps == null)
      analyzeJumps();

    // XXX: revisits the new code
    if (offset <= _offset) {
      _offset += count;
    }

    for (int i = 0; i < _jumps.size(); i++) {
      Jump jump = _jumps.get(i);

      jump.insert(this, offset, count);
    }
   
    ArrayList<CodeAttribute.ExceptionItem> exns = getExceptions();

    for (int i = 0; i < exns.size(); i++) {
      CodeAttribute.ExceptionItem exn = exns.get(i);

      if (offset <= exn.getStart())
  exn.setStart(exn.getStart() + count);
     
      if (offset <= exn.getEnd())
  exn.setEnd(exn.getEnd() + count);
     
      if (offset <= exn.getHandler())
  exn.setHandler(exn.getHandler() + count);
    }

    if (_pendingTargets != null) {
      for (int i = _pendingTargets.size() - 1; i >= 0; i--) {
  int target = _pendingTargets.get(i);

  if (offset <= target)
    _pendingTargets.set(i, target + count);
      }
     
      for (int i = _completedTargets.size() - 1; i >= 0; i--) {
  int target = _completedTargets.get(i);

  if (offset <= target)
    _completedTargets.set(i, target + count);
      }
    }

    for (int i = 0; i < _switches.size(); i++) {
      Branch branch = _switches.get(i);

      branch.insert(this, offset, count);
    }

    for (int i = 0; i < count; i++)
      _code.add(offset, 0);

    for (int i = 0; i < _switches.size(); i++) {
      Switch branch = _switches.get(i);

      branch.insertPad(this, offset, count);
    }
  }
 
  protected void removeCode(int offset, int count)
  {
    if (_jumps == null)
      analyzeJumps();

    if (offset + count < _offset)
      _offset -= count;
    else if (offset <= _offset)
      _offset = offset;

    for (int i = 0; i < _jumps.size(); i++) {
      Branch jump = _jumps.get(i);

      jump.remove(this, offset, count);
    }
   
    ArrayList<CodeAttribute.ExceptionItem> exns = getExceptions();

    for (int i = 0; i < exns.size(); i++) {
      CodeAttribute.ExceptionItem exn = exns.get(i);

      exn.setStart(remove(exn.getStart(), offset, count));
      exn.setEnd(remove(exn.getEnd(), offset, count));
      exn.setHandler(remove(exn.getHandler(), offset, count));
    }

    if (_pendingTargets != null) {
      for (int i = _pendingTargets.size() - 1; i >= 0; i--) {
  int target = _pendingTargets.get(i);

  _pendingTargets.set(i, remove(target, offset, count));
      }
     
      for (int i = _completedTargets.size() - 1; i >= 0; i--) {
  int target = _completedTargets.get(i);

  _completedTargets.set(i, remove(target, offset, count));
      }
    }

    for (int i = 0; i < _switches.size(); i++) {
      Branch branch = _switches.get(i);

      branch.remove(this, offset, count);
    }

    _code.remove(offset, count);

    for (int i = 0; i < _switches.size(); i++) {
      Switch branch = _switches.get(i);

      branch.removePad(this, offset, count);
    }
  }

  protected void analyzeJumps()
  {
    _jumps = new ArrayList<Jump>();
    _switches = new ArrayList<Switch>();

    _changeLength = true;

    JumpAnalyzer analyzer = new JumpAnalyzer();

    CodeVisitor visitor = new CodeVisitor(getJavaClass(), getCodeAttribute());

    try {
      visitor.analyze(analyzer);
    } catch (Exception e) {
      log.log(Level.WARNING, e.toString(), e);
    }
  }

  /**
   * Updates the code.
   */
  public void update()
  {
    byte []code = new byte[_code.size()];

    System.arraycopy(_code.getBuffer(), 0, code, 0, _code.size());

    _codeAttr.setCode(code);

    if (_changeLength) {
      // XXX: really need more sophisticated solution
      ArrayList<Attribute> attrList = getCodeAttribute().getAttributes();
      for (int i = attrList.size() - 1; i >= 0; i--) {
  Attribute attr = attrList.get(i);

  if (attr.getName().equals("LineNumberTable"))
    attrList.remove(i);
      }
    }
  }

  private int remove(int pc, int offset, int count)
  {
    if (pc < offset)
      return pc;
    else if (pc < offset + count)
      return offset;
    else
      return pc - count;
  }

  abstract static class Branch {
    abstract void insert(CodeEnhancer enhancer, int offset, int count);
    abstract void remove(CodeEnhancer enhancer, int offset, int count);
  }
   
  static class Jump extends Branch {
    private int _src;
    private int _delta;

    Jump(int src, int delta)
    {
      _src = src;
      _delta = delta;
    }

    void insert(CodeEnhancer enhancer, int offset, int count)
    {
      // offset is before the jump
      if (offset <= _src && offset <= _src + _delta) {
  _src += count;
      }
      // offset is inside a forward jump
      else if (_src < offset && offset < _src + _delta) {
  _delta += count;
  enhancer.setShort(_src + 1, _delta);
      }
      // offset is inside a backward jump
      else if (_src + _delta <= offset && offset <= _src) {
  _delta -= count;
  enhancer.setShort(_src + 1, _delta);
  _src += count;
      }
    }

    void remove(CodeEnhancer enhancer, int offset, int count)
    {
      // offset is before the jump
      if (offset <= _src && offset <= _src + _delta) {
  _src -= count;
      }
      // offset is inside a forward jump
      else if (_src < offset && offset < _src + _delta) {
  _delta -= count;
  enhancer.setShort(_src + 1, _delta);
      }
      // offset is inside a backward jump
      else if (_src + _delta <= offset && offset <= _src) {
  _delta += count;
  enhancer.setShort(_src + 1, _delta);
  _src -= count;
      }
    }
  }

  static class Switch extends Branch {
    private int _oldSrc;
    private int _src;
    private int []_offsets;
   
    Switch(int src)
    {
      _src = src;
      _oldSrc = src;
    }

    protected void setOffsets(int []offsets)
    {
      _offsets = offsets;
    }

    void insert(CodeEnhancer enhancer, int offset, int count)
    {
      for (int i = 0; i < _offsets.length; i++) {
  int delta = enhancer.getInt(_offsets[i]);
 
  if (offset <= _src && _src + delta <= offset)
    enhancer.setInt(_offsets[i], delta - count);
  else if (_src < offset && offset < _src + delta)
    enhancer.setInt(_offsets[i], delta + count);

  if (offset <= _src + 1)
    _offsets[i] += count;
      }
     
      if (offset < _src)
  _src += count;
    }

    void remove(CodeEnhancer enhancer, int offset, int count)
    {
      for (int i = 0; i < _offsets.length; i++) {
  int delta = enhancer.getInt(_offsets[i]);
 
  if (offset <= _src && _src + delta <= offset)
    enhancer.setInt(_offsets[i], delta + count);
  else if (_src < offset && offset < _src + delta)
    enhancer.setInt(_offsets[i], delta - count);
 
  if (offset <= _src + 1)
    _offsets[i] -= count;
      }
     
      if (offset < _src)
  _src -= count;
    }

    void insertPad(CodeEnhancer enhancer, int offset, int count)
    {
      // offset is before the jump
      if (_oldSrc != _src) {
  int oldPad = (4 - (_oldSrc + 1) % 4) % 4;
  int newPad = (4 - (_src + 1) % 4) % 4;

  _oldSrc = _src;
 
  if (newPad < oldPad)
    enhancer.remove(_src + 1, oldPad - newPad);
  else if (oldPad < newPad)
    enhancer.addNulls(_src + 1, newPad - oldPad);
      }
    }

    void removePad(CodeEnhancer enhancer, int offset, int count)
    {
      // offset is before the jump
      if (_oldSrc != _src) {
  int oldPad = (4 - (_oldSrc + 1) % 4) % 4;
  int newPad = (4 - (_src + 1) % 4) % 4;

  _oldSrc = _src;
 
  if (newPad < oldPad)
    enhancer.remove(_src + 1, oldPad - newPad);
  else if (oldPad < newPad)
    enhancer.addNulls(_src + 1, newPad - oldPad);
      }
    }

    public boolean equals(Object v)
    {
      if (! (v instanceof Switch))
  return false;
     
      Switch s = (Switch) v;

      return _src == s._src;
    }
  }

  static class TableSwitch extends Switch {
    TableSwitch(int src, CodeVisitor visitor)
    {
      super(src);
     
      int arg = src + 1;
      arg += (4 - arg % 4) % 4;
 
      int low = visitor.getInt(arg + 4);
      int high = visitor.getInt(arg + 8);

      int []offsets = new int[high - low + 2];

      offsets[0] = arg;
 
      for (int i = 0; i <= high - low; i++) {
  offsets[i + 1] = arg + 12 + i * 4;
      }

      setOffsets(offsets);
    }
  }

  static class LookupSwitch extends Switch {
    LookupSwitch(int src, CodeVisitor visitor)
    {
      super(src);
     
      int arg = src + 1;
      arg += (4 - arg % 4) % 4;

      int n = visitor.getInt(arg + 4);

      int []offsets = new int[n + 1];
      offsets[0] = arg;
 
      for (int i = 0; i < n; i++) {
  offsets[i + 1] = arg + 8 + i * 8 + 4;
      }

      setOffsets(offsets);
    }
  }

  class JumpAnalyzer extends Analyzer {
    public void analyze(CodeVisitor visitor)
      throws Exception
    {
      if (visitor.isSwitch()) {
  int src = visitor.getOffset();

  switch (visitor.getOpcode()) {
  case TABLESWITCH:
    {
      TableSwitch branch = new TableSwitch(src, visitor);
      if (! _switches.contains(branch))
        _switches.add(branch);
      break;
    }
   
  case LOOKUPSWITCH:
    {
      LookupSwitch branch = new LookupSwitch(src, visitor);
      if (! _switches.contains(branch))
        _switches.add(branch);
      break;
    }
  }
      }
      else if (visitor.isBranch()) {
  int src = visitor.getOffset();
  int offset = visitor.getShortArg(1);

  _jumps.add(new Jump(src, offset));
      }
    }
  }
}
TOP

Related Classes of com.caucho.bytecode.CodeEnhancer$TableSwitch

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.