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

[Question] Inner static class loading / method intercept possible? #199

Closed
originx opened this issue Oct 16, 2016 · 4 comments
Closed

[Question] Inner static class loading / method intercept possible? #199

originx opened this issue Oct 16, 2016 · 4 comments
Assignees
Labels
Milestone

Comments

@originx
Copy link

originx commented Oct 16, 2016

Hello, I am trying to adapt a library (port it) from cglib to ByteBuddy so I can run it on Android Dalvik/dex, and I am having issues with method delegation and IllegalAccessException.
ByteBuddy seems to fail with IllegalStateException in case of a inner static class declaration with following error:

java.lang.IllegalAccessException: Class com.tngtech.jgiven.impl.StandaloneScenarioExecutor can not access a member of class com.tngtech.jgiven.report.text.PlainTextReporterTest$FormattedSteps$ByteBuddy$7UgEFvgC with modifiers ""
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Constructor.newInstance(Constructor.java:413)

Note if I change the class modifier to public it works fine:

static class FormattedSteps { <-- **fails**
public  static class FormattedSteps { <-- **works**

This is a snippet of interception setup:

 return new  ByteBuddy()  
  .subclass(stepsClass)
  .method(any())
  .intercept(MethodDelegation.to(methodInterceptor))
  .make()
  .load(getClass().getClassLoader())
  .getLoaded()
  .newInstance();

Then a snippet from the MethodInterceptor.class

 @RuntimeType
 @BindingPriority(BindingPriority.DEFAULT * 3)
 public Object interceptSuper( 
        @SuperCall final Callable<?> zuper,
        @This final Object receiver,
        @Origin Method method, 
        @AllArguments final Object[] parameters)
        throws Throwable {
      Invoker invoker = new Invoker() {
        @Override
        public Object proceed() throws Throwable {
          return zuper.call();
        }
      };
      return doIntercept( receiver  , method, parameters, invoker );
    }

    @RuntimeType
    @BindingPriority(BindingPriority.DEFAULT * 2)
    public Object interceptDefault(
        @DefaultCall final Callable<?> zuper,
        @This final Object receiver,
        @Origin Method method, 
        @AllArguments final Object[] parameters)
        throws Throwable {
      Invoker invoker = new Invoker() {
        @Override
        public Object proceed() throws Throwable {
          return zuper.call();
        }
      };
      return doIntercept( receiver  , method, parameters, invoker );
    }

    @RuntimeType
    public Object intercept( 
      @This final Object receiver,
      @Origin final Method method, 
      @AllArguments final Object[] parameters) throws Throwable {
    // this intercepted method does not have a non-abstract super method
    Invoker invoker = new Invoker() {
      @Override
      public Object proceed() throws Throwable {
        return null;
      }
    };
    return doIntercept( receiver  , method, parameters, invoker );
  }

Am I doing something wrong here, code works fine with cglib so I presume I misconfigured the interceptor?

@originx originx changed the title [Question] [Question] Inner static class loading / method intercept Oct 16, 2016
@originx originx changed the title [Question] Inner static class loading / method intercept [Question] Inner static class loading / method intercept possible? Oct 16, 2016
@raphw raphw added the question label Oct 16, 2016
@raphw raphw self-assigned this Oct 16, 2016
@raphw raphw modified the milestones: 1.4.3, 1.4.32 Oct 16, 2016
@raphw
Copy link
Owner

raphw commented Oct 16, 2016

I assume that getClass().getClassLoader() is not the same as the class loader of your stepsClass. If a class is package-private (all inner non-public and non-protected classes are), it is defined to be package-private. A package-private class can only be subclasses if the subclass is also within the same package.

A package at runtime needs to be defined by the same class loader in order to be equal at runtime. This is a limitation enforced by the JVM. (This should also not work with cglib, so to speak.)

@originx
Copy link
Author

originx commented Oct 17, 2016

Thank you for the fast reply I will look into it.
I am doing the migration on a fork of JGiven library:
https://github.com/originx/JGiven

You can reproduce the issue if you run ./gradlew :jgiven-core:test

Although I am unsure it is run in a different class loader I have to check it.

@raphw
Copy link
Owner

raphw commented Oct 17, 2016

I see now what is going on. Byte Buddy mimics the constructors of its super class but it does not change their visibility. Therefore, you would need to set the constructor to be accessible before creating an instance. You can change that by using ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING upon subclassing.

PS: I changed this to be the default starting from the next version.

@raphw raphw closed this as completed Oct 17, 2016
@originx
Copy link
Author

originx commented Oct 17, 2016

Really nice, never saw a library dev so responsive. Kudos and thank you for quick assistance, works fine now.

originx pushed a commit to originx/JGiven that referenced this issue Oct 17, 2016
…y can access super constructors and set their visibility for reflection "raphw/byte-buddy#199 (comment)"
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

2 participants