установка пути к классу для нового введенного класса - PullRequest
5 голосов
/ 21 ноября 2010

Контекст: Новый класс, скажем, Bar, вводится в JVM во время выполнения. Этот класс принадлежит пакету скажем com.foo. Ссылка на этот класс вводится в другой класс, принадлежащий тому же пакету. Новый класс может иметь другое имя при каждой загрузке, поэтому его нельзя указывать как часть какого-либо файла конфигурации, например не может быть указан в build.xml для включения как часть файла jar.

Проблема: Во время загрузки класса jvm выдает ошибку - результат Java 1. Хотя я не могу окончательно определить основную причину, похоже, что новый внедренный класс не найден загрузчиком классов. Сервер был запущен в подробном режиме, который показывает список классов, загруженных JVM, и этот вновь внедренный класс считается загруженным.

Вопрос: Только что введенный класс уже находится в пути к классам? Если нет, то как его установить?

[Правка] - добавление кода к вопросу.

Сегмент кода - 1: этот сегмент кода ниже вызывается из метода PreMain - агент JVM вызывает метод Premain и вводит ссылку на инструментарий во время выполнения. Метод Premain вставляет 1 новый класс - Bar - и 1 ссылку на этот новый класс из метода - returnABool () - в существующий класс - ExistingClass.

public static void premain(String agentArgs, Instrumentation inst) {

        // 1. Create and load the new class - Bar
        String className = "Bar";
        byte [] b = getBytesForNewClass();
        //override classDefine (as it is protected) and define the class.
        Class clazz = null;
        try {
          ClassLoader loader = ClassLoader.getSystemClassLoader();
          Class cls = Class.forName("java.lang.ClassLoader");
          java.lang.reflect.Method method =
            cls.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class });
          // protected method invocation
          method.setAccessible(true);
          try {
            Object[] args = new Object[] { className, b, new Integer(0), new Integer(b.length)};
            clazz = (Class) method.invoke(loader, args);
          } finally {
            method.setAccessible(false);
          }
        } catch (Exception e) {
         System.err.println(
            "AllocationInstrumenter was unable to create new class" + e.getMessage());
         e.printStackTrace();
        }

        // 2. Inject some lines of code into the returnsABool method in ExistingClass class that references Bar
        inst.addTransformer(new CustomInstrumenter(), true);

        // end of premain method
}

Сегмент кода 2: метод returnABool () должен быть введен байтами с комментариями линии показаны ниже. Код для байтового ввода этого также вызывается из метода PreMain.

public class ExistingClass{

    public static boolean returnsABool() {
     // Code within comments is byte-injected, again as part of the pre-main method

     /*
     String str = Bar.get();
     if (str != "someValue") {
      return true;
     }
     */

        return false;
    }
}

Внедрение байтового кода для ExistingClass - выполняется с использованием библиотеки asm

{  
    MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);  
    mv.visitCode();  
    Label l0 = new Label();  
    mv.visitLabel(l0);   
    mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/Bar", "get", "()Ljava/lang/String;");        
    mv.visitLdcInsn("some constant here");   
    Label l1 = new Label();   
    mv.visitJumpInsn(Opcodes.IF_ACMPNE, l1);   
    mv.visitInsn(Opcodes.ICONST_0); Label l2 = new Label();   
    mv.visitJumpInsn(Opcodes.GOTO, l2);   
    mv.visitLabel(l1);   
    mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);   
    mv.visitInsn(Opcodes.ICONST_1); 
    mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {Opcodes.INTEGER});  
    mv.visitInsn(Opcodes.IRETURN);   
    mv.visitMaxs(2, 0);   
    mv.visitEnd();   
}

1 Ответ

1 голос
/ 23 ноября 2010

Я подозреваю, что у вас что-то не так с генерацией байт-кода, у меня работает следующий код ASM:

        mv.visitCode();
        Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/Bar", "get", "()Ljava/lang/String;");
        Label l1 = new Label();
        mv.visitLdcInsn("some constant here");
        mv.visitJumpInsn(Opcodes.IF_ACMPEQ, l1);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        mv.visitInsn(Opcodes.ICONST_1);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] {Opcodes.INTEGER});
        mv.visitInsn(Opcodes.IRETURN);
        mv.visitLabel(l1);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        mv.visitInsn(Opcodes.ICONST_0);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] {Opcodes.INTEGER});
        mv.visitInsn(Opcodes.IRETURN);
        mv.visitMaxs(2, 1);
        mv.visitEnd();

Также обратите внимание, что:

  • как вы сравниваетеСтроки, скорее всего, приведут к проблемам, вы должны использовать str.equals(str2)
  • , вы заменяете весь метод , вместо того, чтобы вводить свой собственный код в начале (ваши комментарии, кажется, указывают, что выхочу ввести вместо замены)
...