Получите java.lang.IllegalAccessError при доступе к закрытому полю внешнего класса через ASM Java Bytecode - PullRequest
2 голосов
/ 17 сентября 2010

в отражении, к приватному полю можно получить доступ через getDeclaredField () и setAccessible (true).Как получить доступ к закрытому полю внешнего класса через API байт-кода Objectweb ASM?Я настроен на получение приватного поля из чего-то вроде: через

Field current = sourceObject.getDeclaredField(privateFieldName);
Field.setAccessible(true);
Type sourceType = Type.getType(sourceObject.getClass());
mv.visitFieldInsn(Opcodes.GETFIELD,
                  sourceType.getInternalName(),
                  privateFieldname,
                  Type.getDescriptor(current.getType()));

Когда выполняется байт-код и для получения приватного поля я всегда получаю сообщение об ошибке «java.lang.IllegalAccessError»

Любая подсказка?Спасибо за связку,

Ответы [ 2 ]

2 голосов
/ 01 октября 2010

Вы не можете так делать. setAccessible(true) влияет только на текущую ссылку на поле при текущем выполнении вашей программы (то есть , это не влияет на выполнение результирующей модифицированной программы ).

Чтобы получить доступ к закрытому полю при запуске вашей модифицированной программы, вам, в основном, нужно встроить соответствующие шаги отражения в программу.

Для доступа к закрытому полю YourClass.thePrivatefield некоторого объекта, хранящегося в локальной переменной varId, вы делаете что-то вроде

// Get hold of the field-reference
mv.visitLdcInsn(Type.getType("LYourClass;"));
mv.visitLdcInsn("thePrivateField");
mv.visitMethodInsn(INVOKEVIRTUAL,
                   "java/lang/Class",
                   "getDeclaredField",
                   "(Ljava/lang/String;)Ljava/lang/reflect/Field;");

// Duplicate the reference
mv.visitInsn(DUP);

// Call setAccessible(true) using the first reference.
mv.visitInsn(ICONST_1);
mv.visitMethodInsn(INVOKEVIRTUAL,
                   "java/lang/reflect/Field",
                   "setAccessible",
                   "(Z)V");

// Call get(yourObject) using the second reference to the field.
mv.visitInsn(ALOAD, varId);
mv.visitMethodInsn(INVOKEVIRTUAL,
                   "java/lang/reflect/Field",
                   "get",
                   "(Ljava/lang/Object;)Ljava/lang/Object;");

Если поле, которое вы пытаетесь сделать доступным, является частью базы cobe, которую вы переписываете, вы, очевидно, можете также сделать это поле общедоступным, используя ACC_PUBLIC вместо ACC_PRIVATE.

0 голосов
/ 16 марта 2011

Проблема в том, что вы не можете легально получить доступ к этим переменным. Это связано с тем, что JVM определила свои правила доступа еще до того, как в Java появились внутренние классы, поэтому javac создает синтетические средства доступа для полей, к которым он не имеет легального доступа в JVM, но может в Java. Например,

class Sample {
    private int i = 0;
    class Inner {
        int foo = i;
    }
}

Тогда мы можем использовать javap для декомпиляции сгенерированных классов.

fowles@morbo:/tmp$ javap -private Sample
Compiled from "Sample.java"
class Sample extends java.lang.Object{
    private int i;
    Sample();
    static int access$000(Sample);
}

fowles@morbo:/tmp$ javap -c Sample.Inner
Compiled from "Sample.java"
class Sample$Inner extends java.lang.Object{
int foo;

final Sample this$0;

Sample$Inner(Sample);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #1; //Field this$0:LSample;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   aload_0
   10:  aload_0
   11:  getfield    #1; //Field this$0:LSample;
   14:  invokestatic    #3; //Method Sample.access$000:(LSample;)I
   17:  putfield    #4; //Field foo:I
   20:  return

}

Обратите внимание на метод access$000(Sample), который был сгенерирован в Sample и использован из Sample.Inner. К сожалению, вы можете либо

  1. Сделать поле доступным
  2. Использовать отражение
  3. Генерация синтетических аксессоров
...