Package cuchaz.enigma.bytecode

Source Code of cuchaz.enigma.bytecode.ConstPoolEditor

/*******************************************************************************
* Copyright (c) 2014 Jeff Martin.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
*     Jeff Martin - initial API and implementation
******************************************************************************/
package cuchaz.enigma.bytecode;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;

import javassist.bytecode.ConstPool;
import javassist.bytecode.Descriptor;
import cuchaz.enigma.bytecode.accessors.ClassInfoAccessor;
import cuchaz.enigma.bytecode.accessors.ConstInfoAccessor;
import cuchaz.enigma.bytecode.accessors.MemberRefInfoAccessor;

public class ConstPoolEditor
{
  private static Method m_getItem;
  private static Method m_addItem;
  private static Method m_addItem0;
  private static Field m_items;
  private static Field m_cache;
  private static Field m_numItems;
  private static Field m_objects;
  private static Field m_elements;
  private static Method m_methodWritePool;
  private static Constructor<ConstPool> m_constructorPool;
 
  static
  {
    try
    {
      m_getItem = ConstPool.class.getDeclaredMethod( "getItem", int.class );
      m_getItem.setAccessible( true );
     
      m_addItem = ConstPool.class.getDeclaredMethod( "addItem", Class.forName( "javassist.bytecode.ConstInfo" ) );
      m_addItem.setAccessible( true );
     
      m_addItem0 = ConstPool.class.getDeclaredMethod( "addItem0", Class.forName( "javassist.bytecode.ConstInfo" ) );
      m_addItem0.setAccessible( true );
     
      m_items = ConstPool.class.getDeclaredField( "items" );
      m_items.setAccessible( true );
     
      m_cache = ConstPool.class.getDeclaredField( "itemsCache" );
      m_cache.setAccessible( true );
     
      m_numItems = ConstPool.class.getDeclaredField( "numOfItems" );
      m_numItems.setAccessible( true );
     
      m_objects = Class.forName( "javassist.bytecode.LongVector" ).getDeclaredField( "objects" );
      m_objects.setAccessible( true );
     
      m_elements = Class.forName( "javassist.bytecode.LongVector" ).getDeclaredField( "elements" );
      m_elements.setAccessible( true );
     
      m_methodWritePool = ConstPool.class.getDeclaredMethod( "write", DataOutputStream.class );
      m_methodWritePool.setAccessible( true );
     
      m_constructorPool = ConstPool.class.getDeclaredConstructor( DataInputStream.class );
      m_constructorPool.setAccessible( true );
    }
    catch( Exception ex )
    {
      throw new Error( ex );
    }
  }
 
  private ConstPool m_pool;
 
  public ConstPoolEditor( ConstPool pool )
  {
    m_pool = pool;
  }
 
  public void writePool( DataOutputStream out )
  {
    try
    {
      m_methodWritePool.invoke( m_pool, out );
    }
    catch( Exception ex )
    {
      throw new Error( ex );
    }
  }
 
  public static ConstPool readPool( DataInputStream in )
  {
    try
    {
      return m_constructorPool.newInstance( in );
    }
    catch( Exception ex )
    {
      throw new Error( ex );
    }
  }
 
  public String getMemberrefClassname( int memberrefIndex )
  {
    return Descriptor.toJvmName( m_pool.getClassInfo( m_pool.getMemberClass( memberrefIndex ) ) );
  }
 
  public String getMemberrefName( int memberrefIndex )
  {
    return m_pool.getUtf8Info( m_pool.getNameAndTypeName( m_pool.getMemberNameAndType( memberrefIndex ) ) );
  }
 
  public String getMemberrefType( int memberrefIndex )
  {
    return m_pool.getUtf8Info( m_pool.getNameAndTypeDescriptor( m_pool.getMemberNameAndType( memberrefIndex ) ) );
  }
 
  public ConstInfoAccessor getItem( int index )
  {
    try
    {
      Object entry = m_getItem.invoke( m_pool, index );
      if( entry == null )
      {
        return null;
      }
      return new ConstInfoAccessor( entry );
    }
    catch( Exception ex )
    {
      throw new Error( ex );
    }
  }
 
  public int addItem( Object item )
  {
    try
    {
      return (Integer)m_addItem.invoke( m_pool, item );
    }
    catch( Exception ex )
    {
      throw new Error( ex );
    }
  }
 
  public int addItemForceNew( Object item )
  {
    try
    {
      return (Integer)m_addItem0.invoke( m_pool, item );
    }
    catch( Exception ex )
    {
      throw new Error( ex );
    }
  }
  @SuppressWarnings( "rawtypes" )
  public void removeLastItem( )
  {
    try
    {
      // remove the item from the cache
      HashMap cache = getCache();
      if( cache != null )
      {
        Object item = getItem( m_pool.getSize() - 1 );
        cache.remove( item );
      }
     
      // remove the actual item
      // based off of LongVector.addElement()
      Object items = m_items.get( m_pool );
      Object[][] objects = (Object[][])m_objects.get( items );
      int numElements = (Integer)m_elements.get( items ) - 1;
      int nth = numElements >> 7;
          int offset = numElements & (128 - 1);
          objects[nth][offset] = null;
     
      // decrement the number of items
      m_elements.set( items, numElements );
      m_numItems.set( m_pool, (Integer)m_numItems.get( m_pool ) - 1 );
    }
    catch( Exception ex )
    {
      throw new Error( ex );
    }
  }
 
  @SuppressWarnings( "rawtypes" )
  /* TEMP */public HashMap getCache( )
  {
    try
    {
      return (HashMap)m_cache.get( m_pool );
    }
    catch( Exception ex )
    {
      throw new Error( ex );
    }
  }
 
  @SuppressWarnings( { "rawtypes", "unchecked" } )
  public void changeMemberrefNameAndType( int memberrefIndex, String newName, String newType )
  {
    // NOTE: when changing values, we always need to copy-on-write
    try
    {
      // get the memberref item
      Object item = getItem( memberrefIndex ).getItem();
     
      // update the cache
      HashMap cache = getCache();
      if( cache != null )
      {
        cache.remove( item );
      }
     
      new MemberRefInfoAccessor( item ).setNameAndTypeIndex( m_pool.addNameAndTypeInfo( newName, newType ) );
     
      // update the cache
      if( cache != null )
      {
        cache.put( item, item );
      }
    }
    catch( Exception ex )
    {
      throw new Error( ex );
    }
   
    // make sure the change worked
    assert( newName.equals( getMemberrefName( memberrefIndex ) ) );
    assert( newType.equals( getMemberrefType( memberrefIndex ) ) );
  }
 
  @SuppressWarnings( { "rawtypes", "unchecked" } )
  public void changeClassName( int classNameIndex, String newName )
  {
    // NOTE: when changing values, we always need to copy-on-write
    try
    {
      // get the class item
      Object item = getItem( classNameIndex ).getItem();
     
      // update the cache
      HashMap cache = getCache();
      if( cache != null )
      {
        cache.remove( item );
      }
     
      // add the new name and repoint the name-and-type to it
      new ClassInfoAccessor( item ).setNameIndex( m_pool.addUtf8Info( newName ) );
     
      // update the cache
      if( cache != null )
      {
        cache.put( item, item );
      }
    }
    catch( Exception ex )
    {
      throw new Error( ex );
    }
  }
 
  public static ConstPool newConstPool( )
  {
    // const pool expects the name of a class to initialize itself
    // but we want an empty pool
    // so give it a bogus name, and then clear the entries afterwards
    ConstPool pool = new ConstPool( "a" );
   
    ConstPoolEditor editor = new ConstPoolEditor( pool );
    int size = pool.getSize();
    for( int i=0; i<size-1; i++ )
    {
      editor.removeLastItem();
    }
   
    // make sure the pool is actually empty
    // although, in this case "empty" means one thing in it
    // the JVM spec says index 0 should be reserved
    assert( pool.getSize() == 1 );
    assert( editor.getItem( 0 ) == null );
    assert( editor.getItem( 1 ) == null );
    assert( editor.getItem( 2 ) == null );
    assert( editor.getItem( 3 ) == null );
   
    // also, clear the cache
    editor.getCache().clear();
   
    return pool;
  }
 
  public String dump( )
  {
    StringBuilder buf = new StringBuilder();
    for( int i=1; i<m_pool.getSize(); i++ )
    {
      buf.append( String.format( "%4d", i ) );
      buf.append( "   " );
      buf.append( getItem( i ).toString() );
      buf.append( "\n" );
    }
    return buf.toString();
  }
}
TOP

Related Classes of cuchaz.enigma.bytecode.ConstPoolEditor

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.