Paul King created GROOVY-12484:
----------------------------------
Summary: 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
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.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)