[ 
https://issues.apache.org/jira/browse/GROOVY-12484?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Paul King updated GROOVY-12484:
-------------------------------
    Description: 
h3. Summary

GROOVY-12310 (fixed via GROOVY-12314 in 6.0.0) made the type checker reject 
*field* access on a type that is not accessible from the calling class, e.g. a 
public enum constant of a package-private nested enum. The same check is 
missing for method calls and constructor calls: {{@CompileStatic}} code that 
calls a public static method, or a public constructor, of a package-private 
type compiles and then fails at runtime with {{IllegalAccessError}}.

Java rejects all of these at compile time ("X is not public in Y; cannot be 
accessed from outside package").

h3. Reproducer

Compile with javac:
{code:java}
package a;
public class A {
    enum Keys { X, Y }
    static class Impl {
        public Impl() {}
        public static String hello() { return "hello"; }
    }
}
{code}
{code:java}
package a;
class B {
    public static String greet() { return "greet"; }
}
{code}

Then, from another package:
{code:groovy}
import a.A

@groovy.transform.CompileStatic
class Baz {
    def run1() { A.Keys.values() }
    def run2() { A.Keys.valueOf('X') }
    def run3() { A.Impl.hello() }
    def run4() { new A.Impl() }
    def run5() { a.B.greet() }
}
def b = new Baz()
['run1', 'run2', 'run3', 'run4', 'run5'].each { m ->
    try { println "$m: ${b."$m"()}" } catch (Throwable t) { println "$m: $t" }
}
{code}

h3. Actual

All five compile. At runtime each throws:
{noformat}
java.lang.IllegalAccessError: failed to access class a.A$Keys from class Baz
java.lang.IllegalAccessError: failed to access class a.A$Impl from class Baz
java.lang.IllegalAccessError: failed to access class a.B from class Baz
{noformat}

For comparison, {{A.Keys.X}} is rejected at compile time since 6.0.0 ("Cannot 
access f from class: Baz"), and a package-private *constructor* is already 
rejected ("Cannot
find matching constructor"), because member modifiers are checked. Only the 
declaring t.

h3. Expected                                                                    
                                                                                
                              A static type checking error for each call, 
consistent with the field case, e.g. "Cannf class: a.A$Keys from class: Baz" / 
"Cannot access constructor ...". Without{{@CompileStatic}} the calls should 
keep working as they do today.

h3. Notes

* {{StaticTypeCheckingVisitor.isFieldAccessible}} applies {{hasAccessToClass}} 
(JLS 6.ssible type is out of reach regardless of its own modifiers). Method 
selection and
constructor selection only apply {{hasAccessToMember}} to the member's 
modifiers, so te.
* The class literal ({{A.Keys}}) and subscript access ({{A.Keys['X']}}) are 
deliberate12310's tests and remain the escape hatch; they go through 
{{Class.forName}} and the
MOP, which perform no access check.
* Two things to decide: whether to also cover instance method calls on a vale 
inaccessible class (e.g. a value returned as A.Impl from a public API — javac 
rejects naming the type at all, so this follows from the same rule), and the 
exact message wording to match the existing "Cannot access field" one.

  was:
h3. Summary

GROOVY-12310 (fixed via GROOVY-12314 in 6.0.0) made the type checker reject 
*field* access on a type that is not accessible from the calling class, e.g. a 
public enum constant of a package-private nested enum. The same check is 
missing for method calls and constructor calls: {{@CompileStatic}} code that 
calls a public static method, or a public constructor, of a package-private 
type compiles and then fails at runtime with {{IllegalAccessError}}.

Java rejects all of these at compile time ("X is not public in Y; cannot be 
accessed from outside package").

h3. Reproducer

Compile with javac:
{code:java}
package a;
public class A {
    enum Keys { X, Y }
    static class Impl {
        public Impl() {}
        public static String hello() { return "hello"; }
    }
}
{code}
{code:java}
package a;
class B {
    public static String greet() { return "greet"; }
}
{code}

Then, from another package:
{code:groovy}
import a.A

@groovy.transform.CompileStatic
class Baz {
    def run1() { A.Keys.values() }
    def run2() { A.Keys.valueOf('X') }
    def run3() { A.Impl.hello() }
    def run4() { new A.Impl() }
    def run5() { a.B.greet() }
}
def b = new Baz()
['run1', 'run2', 'run3', 'run4', 'run5'].each { m ->
    try { println "$m: ${b."$m"()}" } catch (Throwable t) { println "$m: $t" }
}
{code}

h3. Actual

All five compile. At runtime each throws:
{noformat}
java.lang.IllegalAccessError: failed to access class a.A$Keys from class Baz
java.lang.IllegalAccessError: failed to access class a.A$Impl from class Baz
java.lang.IllegalAccessError: failed to access class a.B from class Baz
{noformat}

For comparison, {{A.Keys.X}} is rejected at compile time since 6.0.0 ("Cannot 
access f from class: Baz"), and a package-private *constructor* is already 
rejected ("Cannot
find matching constructor"), because member modifiers are checked. Only the 
declaring t.

h3. Expected                                                                    
                                                                                
                              A static type checking error for each call, 
consistent with the field case, e.g. "Cannf class: a.A$Keys from class: Baz" / 
"Cannot access constructor ...". Without{{@CompileStatic}} the calls should 
keep working as they do today.

h3. Notes

* {{StaticTypeCheckingVisitor.isFieldAccessible}} applies {{hasAccessToClass}} 
(JLS 6.ssible type is out of reach regardless of its own modifiers). Method 
selection and
constructor selection only apply {{hasAccessToMember}} to the member's 
modifiers, so te.
* The class literal ({{A.Keys}}) and subscript access ({{A.Keys['X']}}) are 
deliberate12310's tests and remain the escape hatch; they go through 
{{Class.forName}} and the
MOP, which perform no access check.
* Two things to decide: whether to also cover instance method calls on a vale 
inaccessible class (e.g. a value returned as A.Impl from a public API — javac 
rejects
naming the type at all, so this follows from the same rule), and the exact 
message wor "Cannot access field" one.


> STC: method and constructor calls on an inaccessible type compile and fail at 
> runtime with IllegalAccessError
> -------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12484
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12484
>             Project: Groovy
>          Issue Type: Bug
>          Components: Static Type Checker
>            Reporter: Paul King
>            Priority: Major
>
> h3. Summary
> GROOVY-12310 (fixed via GROOVY-12314 in 6.0.0) made the type checker reject 
> *field* access on a type that is not accessible from the calling class, e.g. 
> a public enum constant of a package-private nested enum. The same check is 
> missing for method calls and constructor calls: {{@CompileStatic}} code that 
> calls a public static method, or a public constructor, of a package-private 
> type compiles and then fails at runtime with {{IllegalAccessError}}.
> Java rejects all of these at compile time ("X is not public in Y; cannot be 
> accessed from outside package").
> h3. Reproducer
> Compile with javac:
> {code:java}
> package a;
> public class A {
>     enum Keys { X, Y }
>     static class Impl {
>         public Impl() {}
>         public static String hello() { return "hello"; }
>     }
> }
> {code}
> {code:java}
> package a;
> class B {
>     public static String greet() { return "greet"; }
> }
> {code}
> Then, from another package:
> {code:groovy}
> import a.A
> @groovy.transform.CompileStatic
> class Baz {
>     def run1() { A.Keys.values() }
>     def run2() { A.Keys.valueOf('X') }
>     def run3() { A.Impl.hello() }
>     def run4() { new A.Impl() }
>     def run5() { a.B.greet() }
> }
> def b = new Baz()
> ['run1', 'run2', 'run3', 'run4', 'run5'].each { m ->
>     try { println "$m: ${b."$m"()}" } catch (Throwable t) { println "$m: $t" }
> }
> {code}
> h3. Actual
> All five compile. At runtime each throws:
> {noformat}
> java.lang.IllegalAccessError: failed to access class a.A$Keys from class Baz
> java.lang.IllegalAccessError: failed to access class a.A$Impl from class Baz
> java.lang.IllegalAccessError: failed to access class a.B from class Baz
> {noformat}
> For comparison, {{A.Keys.X}} is rejected at compile time since 6.0.0 ("Cannot 
> access f from class: Baz"), and a package-private *constructor* is already 
> rejected ("Cannot
> find matching constructor"), because member modifiers are checked. Only the 
> declaring t.
> h3. Expected                                                                  
>                                                                               
>                                   A static type checking error for each call, 
> consistent with the field case, e.g. "Cannf class: a.A$Keys from class: Baz" 
> / "Cannot access constructor ...". Without{{@CompileStatic}} the calls should 
> keep working as they do today.
> h3. Notes
> * {{StaticTypeCheckingVisitor.isFieldAccessible}} applies 
> {{hasAccessToClass}} (JLS 6.ssible type is out of reach regardless of its own 
> modifiers). Method selection and
> constructor selection only apply {{hasAccessToMember}} to the member's 
> modifiers, so te.
> * The class literal ({{A.Keys}}) and subscript access ({{A.Keys['X']}}) are 
> deliberate12310's tests and remain the escape hatch; they go through 
> {{Class.forName}} and the
> MOP, which perform no access check.
> * Two things to decide: whether to also cover instance method calls on a vale 
> inaccessible class (e.g. a value returned as A.Impl from a public API — javac 
> rejects naming the type at all, so this follows from the same rule), and the 
> exact message wording to match the existing "Cannot access field" one.



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to