Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advice checks fail when subclass tries to access protected field of superclass #1224

Closed
yrodiere opened this issue Mar 24, 2022 · 4 comments
Closed
Assignees
Labels
Milestone

Comments

@yrodiere
Copy link
Contributor

yrodiere commented Mar 24, 2022

Description of the problem

Assuming I have a class with a protected field, and that class is extended by another in a different package:

package org.hibernate.bytebuddy.playground.differentpackage;

public class MySuperClass {

	protected int myField;

	public void setMyField(int myField) {
		this.myField = myField;
	}
}
package org.hibernate.bytebuddy.playground;

import org.hibernate.bytebuddy.playground.differentpackage.MySuperClass;

public class MyClass extends MySuperClass {

	public int myMethod() {
		return 0;
	}

}

If I create an advice that reads myField, and apply it to the subclass MyClass:

public class MyAdvice {
	@Retention(RetentionPolicy.RUNTIME)
	@interface FieldValue {

	}

	@Advice.OnMethodExit
	public static void exit(@FieldValue Object fieldValue,
			@Advice.Return(readOnly = false, typing = Assigner.Typing.DYNAMIC) Object returned)
			throws Throwable {
		returned = fieldValue;
	}
}
		DynamicType.Unloaded<MyClass> unloadedTypeWithAdvice = new ByteBuddy()
				.subclass( MyClass.class )
				.name( MyClass.class.getName() + "_withAdvice" )
				.method( named( "myMethod" ) )
				.intercept( Advice.withCustomMapping()
						.bind( MyAdvice.FieldValue.class, MySuperClass.class.getDeclaredField( "myField" ) )
						.to( MyAdvice.class ) )
				.make();

Then applying the advice will fail with the following error:

java.lang.IllegalStateException: Cannot access protected int org.hibernate.bytebuddy.playground.differentpackage.MySuperClass.myField from class org.hibernate.bytebuddy.playground.MyClass_withAdvice

	at net.bytebuddy.asm.Advice$OffsetMapping$ForField$Resolved.resolve(Advice.java:2558)
	at net.bytebuddy.asm.Advice$OffsetMapping$ForField.resolve(Advice.java:2305)
	at net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodExit.doApply(Advice.java:8932)
	at net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodExit.apply(Advice.java:8890)
	at net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$AdviceMethodInliner.visitMethod(Advice.java:8312)
	at net.bytebuddy.jar.asm.ClassReader.readMethod(ClassReader.java:1353)
	at net.bytebuddy.jar.asm.ClassReader.accept(ClassReader.java:744)
	at net.bytebuddy.jar.asm.ClassReader.accept(ClassReader.java:424)
	at net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$AdviceMethodInliner.apply(Advice.java:8306)
	at net.bytebuddy.asm.Advice$AdviceVisitor$WithExitAdvice.onUserEnd(Advice.java:10797)
	at net.bytebuddy.asm.Advice$AdviceVisitor.visitMaxs(Advice.java:10576)
	at net.bytebuddy.asm.Advice$Appender$EmulatingMethodVisitor.resolve(Advice.java:11148)
	at net.bytebuddy.asm.Advice$Appender.apply(Advice.java:11100)
	at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:709)
	at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:694)
	at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:601)
	at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:5857)
	at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:2192)
	at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:232)
	at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:204)
	at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3659)
	at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:3897)
	at org.hibernate.bytebuddy.playground.MyTest.test(MyTest.java:31)

Expected behavior

I would expect this advice to work fine. The error message is wrong: class org.hibernate.bytebuddy.playground.MyClass_withAdvice definitely can access protected int org.hibernate.bytebuddy.playground.differentpackage.MySuperClass.myField, because MyClass_withAdvice extends MySuperClass, the field has protected access, and we know we're accessing a field of the same instance.

Reproducer

See https://github.com/yrodiere/bytebuddy-playground/tree/protected-field . Just run with ./mvnw clean test.

Possible cause

I think net.bytebuddy.asm.Advice$OffsetMapping$ForField$Resolved.resolve should probably call isVisibleTo rather than isAccessibleTo, because we know we're accessing a field of the same instance.

Unless net.bytebuddy.asm.Advice$OffsetMapping$ForField$Resolved.resolve can be used in cases where the field being accessed is located on another instance, in which case... I really don't know what is going on :)

See also

Downstream reports:

May be related to #976.

@raphw
Copy link
Owner

raphw commented Mar 24, 2022

Yes, you are completely right about this. I fixed this on master. I am planning a release in the next week which comes with some additional Graal support. It will include this fix.

@yrodiere
Copy link
Contributor Author

That's great, thanks a lot for the (very) quick fix @raphw!

@Sanne
Copy link
Contributor

Sanne commented Apr 13, 2022

It seems this issue was fixed? We'll check... thanks @raphw !

@yrodiere
Copy link
Contributor Author

yrodiere commented Apr 13, 2022

I confirm this issue was fixed in 1.12.9 and can be closed. Thanks @raphw!

EDIT: Relevant commit: 994f4f0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants