baliuka 02/03/03 11:35:57
Added: simplestore/src/java/org/apache/commons/simplestore/tools
Enhancer.java
Log:
Added enchancer for not interface types,
add BCEL to classpath to compile enchancer
Revision Changes Path
1.1
jakarta-commons-sandbox/simplestore/src/java/org/apache/commons/simplestore/tools/Enhancer.java
Index: Enhancer.java
===================================================================
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache Cocoon" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.commons.simplestore.tools;
import org.apache.bcel.classfile.*;
import org.apache.bcel.generic.*;
import org.apache.bcel.util.Class2HTML;
import org.apache.bcel.Repository;
import org.apache.bcel.util.InstructionFinder;
/**
*@author Juozas Baliuka <a href="mailto:[EMAIL PROTECTED]">
* [EMAIL PROTECTED]</a>
*@version $Id: Enhancer.java,v 1.1 2002/03/03 19:35:57 baliuka Exp $
*/
public class Enhancer implements org.apache.bcel.Constants{
static boolean DEBUG = true;
static final String INTERCEPTOR_CLASS = MethodInterceptor.class.getName();
static final ObjectType BOOLEAN_OBJECT = new
ObjectType(Boolean.class.getName());
static final ObjectType INTEGER_OBJECT = new
ObjectType(Integer.class.getName());
static final ObjectType CHARACTER_OBJECT = new
ObjectType(Character.class.getName());
static final ObjectType BYTE_OBJECT = new
ObjectType(Byte.class.getName());
static final ObjectType SHORT_OBJECT = new
ObjectType(Short.class.getName());
static final ObjectType LONG_OBJECT = new
ObjectType(Long.class.getName());
static final ObjectType DOUBLE_OBJECT = new
ObjectType(Double.class.getName());
static final ObjectType FLOAT_OBJECT = new
ObjectType(Float.class.getName() );
static final ObjectType METHOD_OBJECT = new
ObjectType(java.lang.reflect.Method.class.getName() );
static final ObjectType NUMBER_OBJECT = new
ObjectType(Number.class.getName());
static final String CONSTRUCTOR_NAME = "<init>";
static final String FIELD_NAME = "h";
static final String SOURCE_FILE = "<generated>";
private static int addAfterRef( ConstantPoolGen cp ){
return cp.addInterfaceMethodref(INTERCEPTOR_CLASS,"afterReturn",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;Ljava/lang/Object;ZLjava/lang/Object;Ljava/lang/Throwable;)Ljava/lang/Object;"
);
}
private static int addInvokeSupperRef( ConstantPoolGen cp ){
return cp.addInterfaceMethodref(INTERCEPTOR_CLASS,"invokeSuper",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;Ljava/lang/Object;)Z");
}
private static int addBeforeRef(ConstantPoolGen cp){
return cp.addInterfaceMethodref(INTERCEPTOR_CLASS,"beforeInvoke",
"(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
}
/** Creates a new instance of Enchancer */
private Enhancer() {
}
public static Object enhance(Class cls , MethodInterceptor ih) {
return enhance(cls,ih,Thread.currentThread().getContextClassLoader());
}
public static Object enhance(Class cls, MethodInterceptor ih,ClassLoader loader)
{
JavaClass clazz = enhance(cls);
try{
byte b [] = clazz.getBytes();
java.lang.reflect.Method m =
ClassLoader.class.getDeclaredMethod("defineClass",
new Class[]{String.class,byte[].class,int.class,int.class}
);
boolean flag = m.isAccessible();
m.setAccessible(true);
Class result = (Class)m.invoke(loader,new
Object[]{clazz.getClassName(),b,new Integer(0),new Integer(b.length)});
m.setAccessible(flag);
java.lang.reflect.Method methods [] = result.getMethods();
for( int i = 0; i < methods.length; i++ ){
try{
result.getField("mtd_" + methods[i].getName() + "$" +
methods[i].getParameterTypes().length
).set(null,methods[i]);
}catch( java.lang.NoSuchFieldException nsfe ){
}
}
return result.getConstructor(new Class[]{ MethodInterceptor.class} ).
newInstance(new Object[]{ih});
}catch( Exception e){
e.printStackTrace();
}
return null;
}
private static void addConstructor(ClassGen cg ){
String parentClass = cg.getSuperclassName();
InstructionFactory factory = new InstructionFactory(cg);
ConstantPoolGen cp = cg.getConstantPool(); // cg creates constant pool
InstructionList il = new InstructionList();
MethodGen costructor = new MethodGen(ACC_PUBLIC,// access flags
Type.VOID,// return type
new Type[] { // argument types
new ObjectType(INTERCEPTOR_CLASS)
}, null, // arg names
CONSTRUCTOR_NAME, cg.getClassName(), il, cp);
il.append( new ALOAD(0));
il.append( factory.createInvoke(parentClass,CONSTRUCTOR_NAME,Type.VOID ,new
Type[]{}, INVOKESPECIAL ));
il.append( new ALOAD(0));
il.append( new ALOAD(1));
il.append( factory.createFieldAccess(cg.getClassName(),FIELD_NAME,new
ObjectType(INTERCEPTOR_CLASS),PUTFIELD));
il.append( new RETURN() );
cg.addMethod( getMethod( costructor ) );
}
private static void addHandlerField(ClassGen cg){
ConstantPoolGen cp = cg.getConstantPool();
FieldGen fg = new FieldGen( ACC_PUBLIC ,
new ObjectType(INTERCEPTOR_CLASS) ,
FIELD_NAME , cp);
cg.addField(fg.getField());
}
private static ClassGen getClassGen( String class_name, Class parentClass){
return new ClassGen( class_name , parentClass.getName(),
SOURCE_FILE, ACC_PUBLIC , null );
}
private static JavaClass enhance( Class parentClass ) {
String class_name = parentClass.getName() + "$$FinalImpl";
JavaClass parent = Repository.lookupClass(parentClass.getName());
ClassGen cg = getClassGen(class_name,parentClass);
ConstantPoolGen cp = cg.getConstantPool(); // cg creates constant pool
addHandlerField(cg);
addConstructor(cg);
int before = addBeforeRef(cp);
int after = addAfterRef(cp);
int invokeSuper = addInvokeSupperRef(cp);
Method methods[] = parent.getMethods();
for( int i = 0 ; i < methods.length; i++ ){
if( !methods[i].getName().equals(CONSTRUCTOR_NAME) && (
( methods[i].getAccessFlags()& ACC_STATIC ) == 0 ) ){
cg.addMethod( generateMethod( methods[i], cg,
before, after, invokeSuper ) );
}
}
JavaClass jcl = cg.getJavaClass();
return jcl;
}
private static void addMethodField(String fieldName, ClassGen cg ){
ConstantPoolGen cp = cg.getConstantPool();
FieldGen fg = new FieldGen( ACC_PUBLIC | ACC_STATIC , METHOD_OBJECT ,
fieldName , cp);
cg.addField(fg.getField());
}
private static int createArgArray(InstructionList il, InstructionFactory
factory, ConstantPoolGen cp, Type[] args ){
int argCount = args.length;
if( argCount > 5 )
il.append( new BIPUSH((byte)argCount) );
else il.append( new ICONST((byte)argCount) );
il.append( new ANEWARRAY( cp.addClass( Type.OBJECT )) );
int load = 1;
for(int i = 0; i< argCount; i++) {
il.append( new DUP() );
if( i > 5 )
il.append( new BIPUSH( (byte) i ) );
else il.append( new ICONST( (byte) i ) );
if ( args[i] instanceof BasicType ){
if ( args[i].equals( Type.BOOLEAN ) ){
il.append( new NEW( cp.addClass( BOOLEAN_OBJECT ) ) );
il.append( new DUP() );
il.append( new ILOAD( load++ ) );
il.append( new
INVOKESPECIAL(cp.addMethodref(Boolean.class.getName(),CONSTRUCTOR_NAME, "(Z)V")) );
}else if ( args[i].equals( Type.INT ) ){
il.append( new NEW( cp.addClass( INTEGER_OBJECT ) ) );
il.append( new DUP() );
il.append( new ILOAD( load++ ) );
il.append( new
INVOKESPECIAL(cp.addMethodref(Integer.class.getName(),CONSTRUCTOR_NAME, "(I)V")) );
}else if ( args[i].equals( Type.CHAR ) ){
il.append( new NEW( cp.addClass( CHARACTER_OBJECT ) ) );
il.append( new DUP() );
il.append( new ILOAD( load++ ) );
il.append( new
INVOKESPECIAL(cp.addMethodref(Character.class.getName(),CONSTRUCTOR_NAME, "(C)V")) );
}else if ( args[i].equals( Type.BYTE ) ){
il.append( new NEW( cp.addClass( BYTE_OBJECT ) ) );
il.append( new DUP() );
il.append( new ILOAD( load++ ) );
il.append( new
INVOKESPECIAL(cp.addMethodref(Byte.class.getName(),CONSTRUCTOR_NAME, "(B)V")) );
}else if ( args[i].equals( Type.SHORT ) ){
il.append( new NEW( cp.addClass( SHORT_OBJECT ) ));
il.append( new DUP() );
il.append( new ILOAD( load++ ) );
il.append( new
INVOKESPECIAL(cp.addMethodref(Short.class.getName(),CONSTRUCTOR_NAME, "(S)V")) );
}else if ( args[i].equals( Type.LONG ) ){
il.append( new NEW( cp.addClass( LONG_OBJECT ) ));
il.append( new DUP() );
il.append( new LLOAD( load ) );
load += 2;
il.append( new
INVOKESPECIAL(cp.addMethodref(Long.class.getName(),CONSTRUCTOR_NAME, "(J)V")) );
}else if ( args[i].equals( Type.DOUBLE ) ){
il.append( new NEW( cp.addClass( DOUBLE_OBJECT ) ) );
il.append( new DUP() );
il.append( new DLOAD( load ) );
load += 2;
il.append( new
INVOKESPECIAL(cp.addMethodref(Double.class.getName(),CONSTRUCTOR_NAME, "(D)V")) );
}else if ( args[i].equals( Type.FLOAT ) ){
il.append( new NEW( cp.addClass( FLOAT_OBJECT ) ));
il.append( new DUP() );
il.append( new FLOAD( load++ ) );
il.append( new
INVOKESPECIAL(cp.addMethodref(Float.class.getName(),CONSTRUCTOR_NAME, "(F)V")) );
}
il.append( new AASTORE() );
// il.append( new DUP() );
}else{
il.append( new ALOAD( load++ ) );
il.append( new AASTORE() );
}
}
return load;
}
private static Method getMethod(MethodGen mg){
mg.stripAttributes(true);
mg.setMaxLocals();
mg.setMaxStack();
return mg.getMethod();
}
private static InstructionHandle generateReturnValue( InstructionList il,
InstructionFactory factory, ConstantPoolGen cp, Type returnType, int stack ){
if( returnType.equals(Type.VOID)){
return il.append( new RETURN() );
}
il.append( new ASTORE( stack ) );
il.append( new ALOAD( stack ) );
if( returnType instanceof ObjectType ) {
return il.append( new ARETURN() );
}else if ( returnType instanceof BasicType ){
if ( returnType.equals( Type.BOOLEAN ) ){
il.append( new CHECKCAST( cp.addClass( BOOLEAN_OBJECT ) ) );
il.append( factory.createInvoke(Boolean.class.getName(),"boolValue",
Type.BOOLEAN,new Type[]{},INVOKEVIRTUAL )
);
return il.append( new IRETURN());
}else if (returnType.equals( Type.CHAR )){
il.append( new CHECKCAST( cp.addClass( CHARACTER_OBJECT ) ) );
il.append(
factory.createInvoke(Character.class.getName(),"charValue",
Type.CHAR,new Type[]{},INVOKEVIRTUAL )
);
return il.append( new IRETURN());
}else if (returnType.equals( Type.LONG )){
il.append( new CHECKCAST( cp.addClass( NUMBER_OBJECT ) ) );
il.append( factory.createInvoke(Number.class.getName(),"longValue",
Type.LONG,new Type[]{},INVOKEVIRTUAL )
);
return il.append( new LRETURN());
}else if (returnType.equals( Type.DOUBLE )){
il.append( new CHECKCAST( cp.addClass( NUMBER_OBJECT ) ) );
il.append( factory.createInvoke(Number.class.getName(),"doubleValue",
Type.DOUBLE,new Type[]{},INVOKEVIRTUAL )
);
return il.append( new DRETURN());
}else if (returnType.equals( Type.FLOAT )){
il.append( new CHECKCAST( cp.addClass( NUMBER_OBJECT ) ) );
il.append(
factory.createInvoke(java.lang.Number.class.getName(),"floatValue",
Type.FLOAT,new Type[]{},INVOKEVIRTUAL )
);
return il.append( new FRETURN());
}else {
il.append( new CHECKCAST( cp.addClass( NUMBER_OBJECT ) ) );
il.append( factory.createInvoke(Number.class.getName(),"intValue",
Type.INT,new Type[]{},INVOKEVIRTUAL )
);
return il.append( new IRETURN());
}
}
return null;
}
private static Instruction newWrapper(Type type,ConstantPoolGen cp) {
if ( type instanceof BasicType ){
if ( type.equals( Type.BOOLEAN ) ){
return new NEW( cp.addClass( BOOLEAN_OBJECT ) );
}else if ( type.equals( Type.INT ) ){
return new NEW( cp.addClass( INTEGER_OBJECT ) );
}else if ( type.equals( Type.CHAR ) ){
return new NEW( cp.addClass( CHARACTER_OBJECT ) );
}else if ( type.equals( Type.BYTE ) ){
return new NEW( cp.addClass( BYTE_OBJECT ));
}else if ( type.equals( Type.SHORT ) ){
return new NEW( cp.addClass( SHORT_OBJECT ));
}else if ( type.equals( Type.LONG ) ){
return new NEW( cp.addClass( LONG_OBJECT ));
}else if ( type.equals( Type.DOUBLE ) ){
return new NEW( cp.addClass( DOUBLE_OBJECT ) );
}else if ( type.equals( Type.FLOAT ) ){
return new NEW( cp.addClass( FLOAT_OBJECT ) );
}
}
return null;
}
private static Instruction initWrapper(Type type,ConstantPoolGen cp){
if ( type instanceof BasicType ){
if ( type.equals( Type.BOOLEAN ) ){
return new
INVOKESPECIAL(cp.addMethodref(Boolean.class.getName(),CONSTRUCTOR_NAME, "(Z)V"));
}else if ( type.equals( Type.INT ) ){
return new
INVOKESPECIAL(cp.addMethodref(Integer.class.getName(),CONSTRUCTOR_NAME, "(I)V"));
}else if ( type.equals( Type.CHAR ) ){
return new
INVOKESPECIAL(cp.addMethodref(Character.class.getName(),CONSTRUCTOR_NAME, "(C)V"));
}else if ( type.equals( Type.BYTE ) ){
return new
INVOKESPECIAL(cp.addMethodref(Byte.class.getName(),CONSTRUCTOR_NAME, "(B)V") );
}else if ( type.equals( Type.SHORT ) ){
return new
INVOKESPECIAL(cp.addMethodref(Short.class.getName(),CONSTRUCTOR_NAME, "(S)V") );
}else if ( type.equals( Type.LONG ) ){
return new
INVOKESPECIAL(cp.addMethodref(Long.class.getName(),CONSTRUCTOR_NAME, "(J)V")) ;
}else if ( type.equals( Type.DOUBLE ) ){
return new
INVOKESPECIAL(cp.addMethodref(Double.class.getName(),CONSTRUCTOR_NAME, "(D)V") );
}else if ( type.equals( Type.FLOAT ) ){
return new
INVOKESPECIAL(cp.addMethodref(Float.class.getName(),CONSTRUCTOR_NAME, "(F)V")) ;
}
}
return null;
}
private static int loadArg(InstructionList il, Type t, int index,int pos ){
if( t instanceof BasicType ){
if( t.equals( Type.LONG )) {
il.append(new LLOAD( pos ));
pos += 2;
return pos;
}else if( t.equals( Type.DOUBLE )){
il.append(new DLOAD( pos ));
pos += 2;
return pos;
}else if (t.equals( Type.FLOAT )) {
il.append(new FLOAD( pos ));
return ++pos;
}else {
il.append(new ILOAD( pos ));
return ++pos;
}
}else{
il.append(new ALOAD(pos));
return ++pos;
}
}
private static Method generateMethod( Method method,
ClassGen cg,int before,
int after, int invokeSuper){
InstructionList il = new InstructionList();
InstructionFactory factory = new InstructionFactory(cg);
ConstantPoolGen cp = cg.getConstantPool();
MethodGen mg = new MethodGen(method, cg.getClassName(), cp);
Type types[] = mg.getArgumentTypes();
int argCount = types.length;
String fieldName = "mtd_" + method.getName() + "$" + argCount;
addMethodField( fieldName, cg );
boolean returnsValue = ! mg.getReturnType().equals(Type.VOID);
mg.setAccessFlags(ACC_PUBLIC);
mg.setInstructionList(il);
InstructionHandle start = il.getStart();
// il.append( new BIPUSH ((byte)argCount) );
int loaded = createArgArray(il,factory,cp,mg.getArgumentTypes());
int argArray = loaded;
il.append( new ASTORE(argArray) );
il.append( new ALOAD(0) );
il.append(factory.createFieldAccess(cg.getClassName(),FIELD_NAME,new
ObjectType(INTERCEPTOR_CLASS),GETFIELD));
il.append( new ALOAD(0) );
il.append( factory.createGetStatic(cg.getClassName(),fieldName,
METHOD_OBJECT ) );
il.append( new ALOAD( argArray ) );
il.append( new INVOKEINTERFACE(before,4) );
int resutFromBefore = ++loaded ;
il.append( new ASTORE( resutFromBefore ) );
il.append( new ACONST_NULL() );
int resultFromSuper = ++loaded;
il.append( new ASTORE( resultFromSuper ) );
il.append( new ICONST(0) );
int superInvoked = ++loaded;
il.append( new ISTORE( superInvoked ) );
il.append( new ACONST_NULL() );
int error = ++loaded;
il.append( new ASTORE( error ) );
il.append( new ALOAD(0) );//this.handler
il.append(factory.createFieldAccess(cg.getClassName(),FIELD_NAME,new
ObjectType(INTERCEPTOR_CLASS),GETFIELD));
il.append( new ALOAD(0) );//this
il.append( factory.createGetStatic(cg.getClassName(),fieldName,
METHOD_OBJECT ) );
il.append( new ALOAD(argArray) );
il.append( new ALOAD(resutFromBefore) );
il.append( new INVOKEINTERFACE(invokeSuper,5) );
IFEQ ifInvoke = new IFEQ(null);
InstructionHandle condition = il.append( ifInvoke );
il.append( new ICONST(1));
InstructionHandle ehStart = il.append( new ISTORE( superInvoked ) );//
Ivoked = true
Instruction wrapper = newWrapper( mg.getReturnType(),cp );
if( wrapper != null ){
ehStart = il.append( wrapper );
il.append( new DUP() );
}
int pos = 1;
il.append(new ALOAD(0) );//this
for( int i = 0; i < argCount; i++ ){//load args to stack
pos = loadArg(il,types[i] , i, pos ) ;
}
//invokeSuper
il.append( new INVOKESPECIAL( cp.addMethodref(cg.getSuperclassName(),
method.getName(),method.getSignature() ) ) );
if( wrapper != null ){
il.append( initWrapper(mg.getReturnType(),cp) );
}
InstructionHandle ehEnd = il.append( new ASTORE(resultFromSuper) );
// GOTO gotoHandled = new GOTO(null);
// il.append( gotoHandled );
// InstructionHandle ehHandled = il.append( new ASTORE(error + 1) );
// il.append( new ALOAD(error + 1) );
// il.append( new ALOAD(error));
InstructionHandle endif = il.append( new ALOAD(0) );//this
ifInvoke.setTarget(endif);
// gotoHandled.setTarget(endif);
il.append(factory.createFieldAccess(cg.getClassName(),FIELD_NAME,new
ObjectType(INTERCEPTOR_CLASS),GETFIELD));
il.append( new ALOAD(0) );//this
il.append( factory.createGetStatic(cg.getClassName(),fieldName,METHOD_OBJECT
) );
il.append( new ALOAD(argArray) );
il.append( new ALOAD(resutFromBefore) );
il.append( new ILOAD(superInvoked) );
il.append( new ALOAD(resultFromSuper) );
il.append( new ALOAD(error) );
il.append( new INVOKEINTERFACE(after,8) );
InstructionHandle exitMethod =
generateReturnValue(il,factory,cp,mg.getReturnType(),++loaded);
// bug in BCEL ?
// mg.addExceptionHandler(ehStart,ehEnd,ehHandled,Type.THROWABLE);
mg.setMaxStack();
mg.setMaxLocals();
Method result = getMethod(mg);
if( DEBUG ){
System.err.println(mg.getMethod());
System.err.println(mg.getMethod().getCode());
System.err.flush();
}
return result;
}
}
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>