beanshell / beanshell

Beanshell scripting language
Apache License 2.0
815 stars 183 forks source link

IllegalAccessException for public methods on non-public cls #335

Closed sourceforge-issue-exporter closed 1 year ago

sourceforge-issue-exporter commented 19 years ago

When calling a public method on a package scope inner class, which extends a public superclass you get an IllegalAccessException. This is caused by one of the top rated Sun Java bugs ( http://bugs.sun.com/bugdatabase/view\_bug.do? bug_id=4071957 ), but can be fixed in Beanshell with a one line fix.

It appears in the current CVS version of Beanshell (2.0b2). You might want to add your bug votes to this bug on Sun's bug DB if you have any spare.

To reproduce: -------------

1) Compile this code

package foo;

public abstract class MyParent {
    public static final MyParent INSTANCE = new MyClass();
    public abstract int getValue(Number number);

    private static class MyClass extends MyParent {
        public int getValue(Number number) {
            return 1;
        }
    }
}

2) Start the beanshell console.

3) Add the output from the above compilation to the classpath.

4) Type foo.MyParent.INSTANCE.getValue(new Integer (1));

To fix (one line): --------------- Modify

if ( method != null && !Modifier.isPublic( method.getModifiers() ) ) { try { ReflectManager.RMSetAccessible( method ); } catch ( UtilEvalError e ) { /*ignore*/ } }

To (you are just adding a test for a non-public class)

if ( method != null && (!Modifier.isPublic( method.getModifiers() ) ||
Modifier.isPublic(method.getDeclaringClass().getModifiers ())) ) { try { ReflectManager.RMSetAccessible( method ); } catch ( UtilEvalError e ) { /*ignore*/ } }

Reported by: nfortescue

Original Ticket: "beanshell/bugs/197":https://sourceforge.net/p/beanshell/bugs/197

sourceforge-issue-exporter commented 18 years ago

Logged In: NO

if the method is retrieved like:

MyParent.class.getMethod("...")

all will work fine, while the following will not work without setAccessible(true):

myParentInstance.getClass().getMethod("...")

please observer the following sample, with a slight'ly modified MyParent:

----- file: foo/ReflectTest

package foo;

public class ReflectTest { public static void main(String[] args) { MyParent myParent = MyParent.getInstance();

// the following fails to work correctly
//Method method = myParent.getClass().getMethod("getValue", new Class[] {Number.class});

// this one works ok Method method = MyParent.class.getMethod("getValue", new Class[] {Number.class});

System.out.println(method.invoke(myParent, new Object[] {new Long(123)})); } }

----- file: bar/MyParent

package bar;

public abstract class MyParent { public static MyParent getInstance() { return new MyClass(); }

public abstract int getValue(Number number);

private static class MyClass extends MyParent { public int getValue(Number number) { return 1; } } }

-----

Guven Demir guven dot demir at telenity dot com dot tr

Original comment by: nobody

nickl- commented 1 year ago

Cannot even evaluate the class definition:

bsh % public abstract class MyParent {
    public static final MyParent INSTANCE = new MyClass();
    public abstract int getValue(Number number);

    private static class MyClass extends MyParent {
        public int getValue(Number number) {
            return 1;
        }
    }
}
// Error: Evaluation Error: Class: MyParent not found in namespace : at Line: 5 : in file: <unknown file> : MyParent

Line 5 is at the inner class definition. source file: MyParent.java.txt

This compiles with javac so we should at least be able to evaluate the class.

nickl- commented 1 year ago

This is not a problem:

BeanShell 3.0.0-SNAPSHOT.2151
bsh % public abstract class MyParent {}
--> $0 = class MyParent :Class

And neither is:

BeanShell 3.0.0-SNAPSHOT.2151
bsh % public abstract class MyParent {
    public static final MyParent INSTANCE = new MyClass();
}
--> $0 = class MyParent :Class
nickl- commented 1 year ago

And once parent exists the inner class succeeds:

BeanShell 3.0.0-SNAPSHOT.2151
public abstract class MyParent {}
--> $0 = class MyParent :Class
public abstract class MyParent {
    private static class MyClass extends MyParent {
        public int getValue(Number number) {
            return 1;
        }
    }
}
--> $1 = class MyParent :Class
nickl- commented 1 year ago
BeanShell 3.0.0-SNAPSHOT.2151
debug();
// Debug: On
--> void
public abstract class MyParent {
    private static class MyClass extends MyParent {
        public int getValue(Number number) {
            return 1;
        }
    }
}
>ClassDeclaration: MyParent
> Block: static=false, synchronized=false
>  ClassDeclaration: MyClass
>   AmbiguousName: MyParent
>   Block: static=false, synchronized=false
>    MethodDeclaration: getValue
>     ReturnType: void=false
>      Type
>       PrimitiveType: int
>     FormalParameters
>      FormalParameter: number, final=false, varargs=false
>       Type
>        AmbiguousName: Number
>     Block: static=false, synchronized=false
>      ReturnStatement: "return"
>       Assignment
>        PrimaryExpression
>         Literal: 1
// Debug: Trying to load class: MyParent
// Debug: absoluteNonClass list hit: MyParent
// Debug: getClass(): MyParent not found in NameSpace: MyParent (bsh.NameSpace@4d591d15) (class) 
// Debug: Trying to load class: java.lang.MyParent
// Debug: Trying to load class: java.io.MyParent
// Debug: Trying to load class: java.util.MyParent
// Debug: Trying to load class: java.util.regex.MyParent
// Debug: Trying to load class: java.util.stream.MyParent
// Debug: Trying to load class: java.util.function.MyParent
// Debug: Trying to load class: java.net.MyParent
// Debug: absoluteNonClass list hit: MyParent
// Debug: absoluteNonClass list hit: MyParent
// Debug: getClass(): MyParent not found in NameSpace: global (bsh.NameSpace@65ae6ba4)
// Debug: trying class: MyParent
// Debug: absoluteNonClass list hit: MyParent
// Debug: absoluteNonClass list hit: MyParent
// Debug: getClass(): MyParent not found in NameSpace: MyParent (bsh.NameSpace@4d591d15) (class) 
// Debug: absoluteNonClass list hit: java.lang.MyParent
// Debug: absoluteNonClass list hit: java.io.MyParent
// Debug: absoluteNonClass list hit: java.util.MyParent
// Debug: absoluteNonClass list hit: java.util.regex.MyParent
// Debug: absoluteNonClass list hit: java.util.stream.MyParent
// Debug: absoluteNonClass list hit: java.util.function.MyParent
// Debug: absoluteNonClass list hit: java.net.MyParent
// Debug: absoluteNonClass list hit: MyParent
// Debug: absoluteNonClass list hit: MyParent
// Debug: getClass(): MyParent not found in NameSpace: global (bsh.NameSpace@65ae6ba4)
// Debug: not a class, trying var prefix MyParent
// Debug: Get method: getMyParent NameSpace: MyParent (bsh.NameSpace@4d591d15) (class)
// Debug: Get method: getMyParent NameSpace: global (bsh.NameSpace@65ae6ba4)
// Debug: Get method: isMyParent NameSpace: MyParent (bsh.NameSpace@4d591d15) (class)
// Debug: Get method: isMyParent NameSpace: global (bsh.NameSpace@65ae6ba4)
// Error: Evaluation Error: Class: MyParent not found in namespace : at Line: 5 : in file: <unknown file> : MyParent

bsh.EvalError: Class: MyParent not found in namespace : at Line: 5 : in file: <unknown file> : MyParent

at bsh.BSHAmbiguousName.toClass(BSHAmbiguousName.java:66)
at bsh.BSHClassDeclaration.generateClass(BSHClassDeclaration.java:78)
at bsh.BSHClassDeclaration.eval(BSHClassDeclaration.java:63)
at bsh.BSHBlock.evalBlock(BSHBlock.java:132)
at bsh.ClassGenerator.generateClassImpl(ClassGenerator.java:89)
at bsh.ClassGenerator.generateClass(ClassGenerator.java:55)
at bsh.BSHClassDeclaration.generateClass(BSHClassDeclaration.java:104)
at bsh.BSHClassDeclaration.eval(BSHClassDeclaration.java:63)
at bsh.Interpreter.run(Interpreter.java:509)
at bsh.Interpreter.main(Interpreter.java:452)
Caused by: java.lang.ClassNotFoundException: Class: MyParent not found in namespace
at bsh.Name.toClass(Name.java:642)
at bsh.BSHAmbiguousName.toClass(BSHAmbiguousName.java:64)
... 9 more
nickl- commented 1 year ago

Here we start with Generate class where before the first instinct was to try and load it. Chicken or egg problem.

BeanShell 3.0.0-SNAPSHOT.2151
debug();
// Debug: On
--> void
public abstract class MyParent {}
>ClassDeclaration: MyParent
> Block: static=false, synchronized=false
// Debug: Generate class CLASS MyParent cons:0 meths:0 vars:0
// Debug: Trying to load class: MyParent
// Debug: Define MyParent as class MyParent
// Debug: NameSpace: MyParent (bsh.NameSpace@6833ce2c) (class)  (class static)
--> $0 = class MyParent :Class
nickl- commented 1 year ago

While generating the class the block is evaluated, specifically:

// Evaluate any inner class class definitions in the block
// effectively recursively call this method for contained classes first

https://github.com/beanshell/beanshell/blob/270dd71ab97ce20096275294504f40b43ed1a5a7/src/main/java/bsh/ClassGenerator.java#L68-L90

So this means we cannot have eggs from the same chicken.

nickl- commented 1 year ago

I went on a tangent and missed the original instruction:

1) Compile this code

public abstract class MyParent {
    public static final MyParent INSTANCE = new MyClass();
    public abstract int getValue(Number number);

    private static class MyClass extends MyParent {
        public int getValue(Number number) {
            return 1;
        }
    }
}

2) Start the beanshell console. 3) Add the output from the above compilation to the classpath. 4) Type MyParent.INSTANCE.getValue(new Integer(1));

Result:

BeanShell 3.0.0-SNAPSHOT.3578
bsh % MyParent.class;
--> $0 = class MyParent :Class
bsh % MyParent.INSTANCE;
--> $1 = MyParent$MyClass@41a4555e :MyParent$MyClass
bsh % MyParent.INSTANCE.getValue(1);
--> $2 = 1I :int
bsh % MyParent.INSTANCE.getValue(new Integer(1));
--> $3 = 1I :int

Works as expected...

nickl- commented 1 year ago

The issue that BeanShell cannot interpret the valid java source from this example has been moved to #694

Closed as the issue reported by OP is resolved.