byte-buddy icon indicating copy to clipboard operation
byte-buddy copied to clipboard

What are the differences between the three test methods ?

Open ooooo-youwillsee opened this issue 1 year ago • 4 comments

1. It work fine.

	@Test
	public void test() {
		Class<?> clazz = new ByteBuddy()
				.subclass(A.class)
				.method(not(isDeclaredBy(Object.class)))
				.intercept(MethodDelegation.to(new AInterceptor()))
				.implement(B.class)
				.intercept(FixedValue.value(new A()))
				.make()
				.load(Thread.currentThread().getContextClassLoader())
				.getLoaded();
		
		assertTrue(B.class.isAssignableFrom(clazz));
	}
	
	public static class A {
		
		public String sayHello() {
			return "Hello";
		}
	}
	
	public interface B {
		
		A getA();
	}
	
	public static class AInterceptor {
		
		@RuntimeType
		public Object invoke(@RuntimeType @AllArguments Object[] args,
		                     @Origin Method method,
		                     @SuperCall Callable<Object> callable) throws Exception {
			
			return callable.call();
		}
	}

2 .it gets wrong.

	@Test
	public void test() {
		Class<?> clazz = new ByteBuddy()
				.subclass(A.class)
        // change start 
				.implement(B.class)
				.intercept(FixedValue.value(new A()))
				.method(not(isDeclaredBy(Object.class)))
				.intercept(MethodDelegation.to(new AInterceptor()))
        // change end
				.make()
				.load(Thread.currentThread().getContextClassLoader())
				.getLoaded();
		
		assertTrue(B.class.isAssignableFrom(clazz));
	}
	
	public static class A {
		
		public String sayHello() {
			return "Hello";
		}
	}
	
	public interface B {
    
		A getA();
	}
	
	public static class AInterceptor {
		
		@RuntimeType
		public Object invoke(@RuntimeType @AllArguments Object[] args,
		                     @Origin Method method,
		                     @SuperCall Callable<Object> callable) throws Exception {
			
			return callable.call();
		}
	}

3 . but it works fine.

	@Test
	public void test() {
		Class<?> clazz = new ByteBuddy()
				.subclass(A.class)
        // change start 
				.implement(B.class)
				.intercept(FixedValue.value(new A()))
				.method(not(isDeclaredBy(Object.class)))
				.intercept(MethodDelegation.to(new AInterceptor()))
        // change end
				.make()
				.load(Thread.currentThread().getContextClassLoader())
				.getLoaded();
		
		assertTrue(B.class.isAssignableFrom(clazz));
	}
	
	public static class A {
		
		public String sayHello() {
			return "Hello";
		}
	}
	
	public interface B {
		
    // change method return type
		String getA();
	}
	
	public static class AInterceptor {
		
		@RuntimeType
		public Object invoke(@RuntimeType @AllArguments Object[] args,
		                     @Origin Method method,
		                     @SuperCall Callable<Object> callable) throws Exception {
			
			return callable.call();
		}
	}

ooooo-youwillsee avatar Apr 12 '23 10:04 ooooo-youwillsee

because you are trying to delegatete a method getA which does not exist in A.class. Try:

  1. define a method getA() in A.class.
  2. or remove the @SuperCall in AInterceptor.
  3. or change the method matcher to .method(not(isDeclaredBy(Object.class)).and(not(isDeclaredBy(B.class))))
  4. or let the .implement call after .method.interceptor

dogourd avatar Apr 13 '23 02:04 dogourd

I wonder why it works fine after I change return type in case 2

ooooo-youwillsee avatar Apr 13 '23 02:04 ooooo-youwillsee

Byte Buddy does not require target methods to be named equally to a source method. the method String getA() can be delegated to AInterceptor.toString(). You can get an Unloaded after the make() method returns, this allows you to step in and call saveIn to see the generated class information. You can also wait and see what rafael thinks about this issue.

dogourd avatar Apr 13 '23 03:04 dogourd

SuperCall should not work with an abstract method.

Note that the interceptor that you define last will superseed all previous interceptors. So methods not declared by Object will override the previous interface matcher.

raphw avatar Apr 13 '23 23:04 raphw