5

मैं .class फ़ाइल में स्थिर अंतिम क्षेत्र को जोड़ने के लिए एएसएम का उपयोग कर चाहते हैं, और स्रोत फ़ाइलएएसएम का उपयोग कर प्रारंभकर्ता के साथ स्थिर अंतिम फ़ील्ड को कैसे जोड़ा जाए?

public class Example { 

    public Example(int code) { 
     this.code = code; 
    } 

    public int getCode() { 
     return code; 
    } 

    private final int code; 

} 

और उत्पन्न वर्ग जो इस तरह होना चाहिए decompiled है:

public class Example { 

    public static final Example FIRST = new Example(1); 

    public static final Example SECOND = new Example(2); 

    public Example(int code) { 
     this.code = code; 
    } 

    public int getCode() { 
     return code; 
    } 

    private final int code; 

} 

और एक निष्कर्ष के रूप में , मैं एएसएम का उपयोग कर .class फ़ाइल में FIRST और SECOND स्थिरांक जोड़ना चाहता हूं, मैं कैसे कर सकता हूं?

+0

क्या यह जावा है? क्या मेनन-असेंबली-प्लगइन से संबंधित प्रश्न है? फिर इसे इस तरह टैग करें। –

उत्तर

17

इस उत्तर से पता चलता है कि यह कैसे एएसएम के आगंतुक एपीआई का उपयोग किया जा सकता है (ASM homepage पर एएसएम 4.0 एक जावा बाईटकोड इंजीनियरिंग पुस्तकालय की धारा 2.2 देखें), क्योंकि यह मेरे लिए सबसे परिचित एपीआई। एएसएम में एक ऑब्जेक्ट मॉडल एपीआई भी है (उसी दस्तावेज़ में भाग II देखें) संस्करण जो आमतौर पर इस मामले में उपयोग करना आसान हो सकता है। ऑब्जेक्ट मॉडल काफी हद तक धीमा है क्योंकि यह स्मृति में पूरी कक्षा फ़ाइल का एक पेड़ बनाता है, लेकिन यदि प्रदर्शन की हिट को बदलने की आवश्यकता वाले वर्गों की केवल थोड़ी सी मात्रा नगण्य होनी चाहिए।

static final फ़ील्ड बनाते समय जिनके मान स्थिरांक (संख्याओं की तरह) नहीं हैं, उनका प्रारंभिक वास्तव में "static initializer block" पर जाता है। ,

public class Example { 

    public static final Example FIRST; 

    public static final Example SECOND; 

    static { 
    FIRST = new Example(1); 
    SECOND = new Example(2); 
    } 

    ... 
} 

एक जावा फ़ाइल में आप कई तरह के स्थिर {...} ब्लॉक की अनुमति दी जाती है, जबकि कक्षा में फ़ाइलों को केवल वहाँ कर सकते हैं: इस प्रकार, अपने दूसरे (तब्दील) कोड सूची निम्नलिखित जावा कोड के बराबर है एक बने। इस संकल्प को पूरा करने के लिए जावा कंपाइलर स्वचालित रूप से एकाधिक स्थिर ब्लॉक को विलय करता है। बाइटकोड में हेरफेर करते समय इसका मतलब है कि यदि पहले से कोई स्थिर ब्लॉक नहीं है तो हम एक नया बनाते हैं, जबकि यदि पहले से ही एक स्थिर ब्लॉक मौजूद है तो हमें मौजूदा कोड की शुरुआत में हमारे कोड को प्रीपेड करने की आवश्यकता है (प्रीपेडिंग संलग्न करने से आसान है)।

एएसएम के साथ, स्थिर ब्लॉक विशेष नाम <clinit> के साथ एक स्थिर विधि की तरह दिखता है, जैसे रचनाकार विशेष नाम <init> के साथ विधियों की तरह दिखते हैं।

विज़िटर एपीआई का उपयोग करते समय, यह जानने का तरीका कि किसी विधि को पहले से परिभाषित किया गया है, सभी विज़िट मोड() कॉल को सुनना और प्रत्येक कॉल में विधि का नाम देखना है। सभी विधियों का दौरा करने के बाद visitEnd() विधि कहा जाता है, इसलिए यदि कोई विधि नहीं देखी गई है, तो हम जानते हैं कि हमें एक नई विधि बनाने की आवश्यकता है।

मान लिया जाये कि हम बाइट [] प्रारूप में एक orignal वर्ग है, अनुरोध किया परिवर्तन इस तरह किया जा सकता है:

import org.objectweb.asm.*; 
import static org.objectweb.asm.Opcodes.*; 

public static byte[] transform(byte[] origClassData) throws Exception { 
    ClassReader cr = new ClassReader(origClassData); 
    final ClassWriter cw = new ClassWriter(cr, Opcodes.ASM4); 

    // add the static final fields 
    cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "FIRST", "LExample;", null, null).visitEnd(); 
    cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "SECOND", "LExample;", null, null).visitEnd(); 

    // wrap the ClassWriter with a ClassVisitor that adds the static block to 
    // initialize the above fields 
    ClassVisitor cv = new ClassVisitor(ASM4, cw) { 
    boolean visitedStaticBlock = false; 

    class StaticBlockMethodVisitor extends MethodVisitor { 
     StaticBlockMethodVisitor(MethodVisitor mv) { 
     super(ASM4, mv); 
     } 
     public void visitCode() { 
     super.visitCode(); 

     // here we do what the static block in the java code 
     // above does i.e. initialize the FIRST and SECOND 
     // fields 

     // create first instance 
     super.visitTypeInsn(NEW, "Example"); 
     super.visitInsn(DUP); 
     super.visitInsn(ICONST_1); // pass argument 1 to constructor 
     super.visitMethodInsn(INVOKESPECIAL, "Example", "<init>", "(I)V"); 
     // store it in the field 
     super.visitFieldInsn(PUTSTATIC, "Example", "FIRST", "LExample;"); 

     // create second instance 
     super.visitTypeInsn(NEW, "Example"); 
     super.visitInsn(DUP); 
     super.visitInsn(ICONST_2); // pass argument 2 to constructor 
     super.visitMethodInsn(INVOKESPECIAL, "Example", "<init>", "(I)V"); 
     super.visitFieldInsn(PUTSTATIC, "Example", "SECOND", "LExample;"); 

     // NOTE: remember not to put a RETURN instruction 
     // here, since execution should continue 
     } 

     public void visitMaxs(int maxStack, int maxLocals) { 
     // The values 3 and 0 come from the fact that our instance 
     // creation uses 3 stack slots to construct the instances 
     // above and 0 local variables. 
     final int ourMaxStack = 3; 
     final int ourMaxLocals = 0; 

     // now, instead of just passing original or our own 
     // visitMaxs numbers to super, we instead calculate 
     // the maximum values for both. 
     super.visitMaxs(Math.max(ourMaxStack, maxStack), Math.max(ourMaxLocals, maxLocals)); 
     } 
    } 

    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 
     if (cv == null) { 
     return null; 
     } 
     MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 
     if ("<clinit>".equals(name) && !visitedStaticBlock) { 
     visitedStaticBlock = true; 
     return new StaticBlockMethodVisitor(mv); 
     } else { 
     return mv; 
     } 
    } 

    public void visitEnd() { 
     // All methods visited. If static block was not 
     // encountered, add a new one. 
     if (!visitedStaticBlock) { 
     // Create an empty static block and let our method 
     // visitor modify it the same way it modifies an 
     // existing static block 
     MethodVisitor mv = super.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); 
     mv = new StaticBlockMethodVisitor(mv); 
     mv.visitCode(); 
     mv.visitInsn(RETURN); 
     mv.visitMaxs(0, 0); 
     mv.visitEnd(); 
     } 
     super.visitEnd(); 
    } 
    }; 

    // feed the original class to the wrapped ClassVisitor 
    cr.accept(cv, 0); 

    // produce the modified class 
    byte[] newClassData = cw.toByteArray(); 
    return newClassData; 
} 

के बाद से अपने प्रश्न वास्तव में अपने अंतिम लक्ष्य क्या है, इसकी आगे संकेत नहीं दिया, मैं अपने उदाहरण वर्ग मामले के लिए काम करने के लिए हार्ड-कोडेड एक मूल उदाहरण के साथ जाने का फैसला किया। क्या आप कक्षा के रूपांतरित होने के उदाहरण बनाना चाहते हैं, आपको कक्षा के पूर्ण वर्ग नाम का उपयोग करने के लिए उपरोक्त "उदाहरण" वाले सभी तारों को बदलना होगा, वास्तव में इसके बदले में परिवर्तित किया जा रहा है। या यदि आप विशेष रूप से प्रत्येक रूपांतरित वर्ग में उदाहरण वर्ग के दो उदाहरण चाहते हैं, तो उपर्युक्त उदाहरण इस प्रकार काम करता है।

+2

इच्छा है कि मैं यह जवाब 10 अपवॉट दे सकता हूं। एएसएम के साथ विधियों को जोड़ना/निकालना आसान है। यह उत्तर उन्हें संशोधित करने के लिए महत्वपूर्ण तकनीक दिखाता है। –

संबंधित मुद्दे

 संबंधित मुद्दे