2017-08-25 429 views
0

我是javassist的新手,開始惹惱它,並得到一些工作。但是,還有很多其他的東西似乎並不奏效。用javassist創建空的構造函數(java)不能上班

我做了注入代碼的方法到類 -

public static void editMethodAddEvent(CtClass target, MethodInfo method, CtClass eventClass, int start, int[] bytes, int stacksize, String constructorParameters) throws BadBytecode, NotFoundException, CannotCompileException { 
    target.defrost(); 
    CodeAttribute codeAttribute = method.getCodeAttribute(); 
    CodeIterator iterator = codeAttribute.iterator(); 
    int classID = method.getConstPool().addClassInfo(eventClass); 
    int constrnatID = method.getConstPool().addNameAndTypeInfo("<init>",constructorParameters); 
    int constructID = method.getConstPool().addMethodrefInfo(classID,constrnatID); 
    int callnatID = method.getConstPool().addNameAndTypeInfo("call","()V"); 
    int callID = method.getConstPool().addMethodrefInfo(classID,callnatID); 
    iterator.insertGap(start,bytes.length); 

    for (int i = 0; i < bytes.length; i++) { 
     int byteCode = bytes[i]; 
     if (byteCode >= 0) { 
      iterator.writeByte(byteCode, start+i); 
     } else if (byteCode == -1) { 
      iterator.writeByte(classID,start+i); 
     } else if (byteCode == -2) { 
      iterator.writeByte(constructID, start+i); 
     } else if (byteCode == -3) { 
      iterator.writeByte(callID, start+i); 
     } 
    } 

    if(stacksize > codeAttribute.getMaxStack()) 
     codeAttribute.setMaxStack(stacksize); 
    target.toClass(); 
} 

我使用此代碼的字節代碼添加到一個方法,觸發事件是運行代碼時。

當我在最後編譯類(target.toClass())時,它沒有錯誤(並且工作正常)。

但是,當我添加一個方法(在這種情況下是空的構造函數),使用下面的代碼,它的錯誤。

public static Class addEmptyConstructor(Class clazz) throws NotFoundException, CannotCompileException { 
    CtClass ctClass = ClassPool.getDefault().getCtClass(clazz.getName()); 
    ctClass.defrost(); 
    ClassFile classFile = ctClass.getClassFile(); 
    MethodInfo newMethod = new MethodInfo(classFile.getConstPool(), "<init>", "()V"); 
    newMethod.setCodeAttribute(new CodeAttribute(classFile.getConstPool(),1,1,new byte[]{0,0,0,0,0},new ExceptionTable(classFile.getConstPool()))); 
    CodeIterator iterator = newMethod.getCodeAttribute().iterator(); 
    iterator.writeByte(42, 0); 
    iterator.writeByte(183,1); 
    iterator.writeByte(0,2); 
    iterator.writeByte(1,3); 
    iterator.writeByte(177,4); 
    classFile.addMethod(newMethod); 
    return ClassPool.getDefault().toClass(ctClass); 
} 

給出的錯誤是:

javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "mod/TestClass" 
     at javassist.util.proxy.DefineClassHelper.toClass2(DefineClassHelper.java:140) 
     at javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:95) 
     at javassist.ClassPool.toClass(ClassPool.java:1143) 
     at javassist.ClassPool.toClass(ClassPool.java:1106) 
     at javassist.ClassPool.toClass(ClassPool.java:1064) 
     at mod.edit.MethodEdit.addEmptyConstructor(MethodEdit.java:113) 
     <other nonrelevant stuff...> 
    Caused by: java.lang.LinkageError: loader (instance of 
    sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for 
    name: "haven/mod/TestClass" 
     at java.lang.ClassLoader.defineClass1(Native Method) 
     at java.lang.ClassLoader.defineClass(Unknown Source) 
     at java.lang.ClassLoader.defineClass(Unknown Source) 
     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
     at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) 
     at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 
     at java.lang.reflect.Method.invoke(Unknown Source) 
     at javassist.util.proxy.DefineClassHelper.toClass3(DefineClassHelper.java:152) 
     at javassist.util.proxy.DefineClassHelper.toClass2(DefineClassHelper.java:134) 
     ... 10 more 

我的目標是創建一個空的構造函數,這樣我可以不投入參數創建任何對象的實例。具體來說,我有很多種事件,有參數。我不想在編譯時訪問這個構造函數。因此,我嘗試在運行時使用javassist添加構造函數,並拒絕編譯。如果我簡單地編輯一個方法,它不會大驚小怪,但如果我添加一個方法,它似乎拒絕做我想要的。我該如何解決這個問題?我一直在嘗試超過16個小時,研究,測試不同的代碼,並且無法獲得任何工作。請幫忙!!!!

+0

你確定你沒有添加一個空構造函數給已經有一個空的構造函數的類嗎? – rakwaht

+0

是的,我現在只是在某些地方調用此方法進行測試。通過字節碼或簡化的CtMethod或CtConstructor方法添加任何方法都會出現問題。感謝您試圖幫助! – PoisonedPorkchop

+0

我有一個在TestClass中有參數和冗餘代碼的構造函數,所以編譯器不會創建默認的。 – PoisonedPorkchop

回答

0

我似乎已經找到了問題的根源。我傳遞了一個對我的方法的類引用。從類我做class.getName(),放入classPool.get(需要字符串)的方法。似乎通過創建類的實例,類加載器無法重新加載,因爲類的實例存在。從我有限的測試,似乎,即使我通過外部調用的方法:

MethodEdit.addEmptyConstructor(TestClass.class.getName()) 

,簡單地通過調用TestClass.class(創建一個類的實例),代碼不能重裝(不知道這是否是有效的,需要更多的測試)。我認爲這就是爲什麼我的其他方法將代碼注入方法的原因,因爲我通過直接名稱獲取CtClass和CtMethod,而不是通過class.getName()獲取它們。雖然我可以簡單地手動調用該方法(「mod.TestClass」作爲參數,而不是TestClass.class),但如果解決方案可用,但仍然可以使用類文件並且不會出錯,請告訴我!在此之前,我將手動鍵入文件名稱的字符串。

+0

如果我在上面的假設中錯了,請糾正我。 – PoisonedPorkchop

0

也許你的問題可能是你正在創建一個新的方法而不是一個構造函數。您是否嘗試在CtNewConstructor處尋找here

我想你應該像我的代碼片段嘗試: 你是不是要加一個構造的人誰已經有一個構造函數首先檢查(我只是刨光它一個布爾值,你應該檢查怎麼辦它由你自己),你也應該檢查這個類是不是一個接口。

然後,只需創建一個新的構造函數,我就可以將其鏈接到您的類並將其添加到您的類中。這裏有一個例子的小代碼片段

if (!hasDefaultConstructor && !ctClass.isInterface()) { 
     CtConstructor defaultConstructor = CtNewConstructor.make("public " + ctClass.getSimpleName() + "() {}", ctClass); 
     ctClass.addConstructor(defaultConstructor); 
} 
+0

是的,我嘗試了這種方法以及字節碼操作。只要我做ctClass.toClass()(編譯更改),它會吐出錯誤,因爲我添加了方法或構造函數。 – PoisonedPorkchop

+0

我在深究你的錯誤:看來問題在於你加載了你正試圖修改兩次的類。您是否使用代理/變換器爲Javassist創建?你能解釋我如何以及何時拿起你正在編輯的CtClass? – rakwaht

+0

如上所示, public static Class addEmptyConstructor(Class clazz)throws NotFoundException,CannotCompileException CtClass = ClassPool.getDefault()。getCtClass(clazz.getName()); ctClass被創建一次,(因爲此方法只被調用一次)。它是通過clazz參數創建的。至於儀表,澄清?不太確定你的意思。 – PoisonedPorkchop