[ 
https://issues.apache.org/jira/browse/GROOVY-12568?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18134521#comment-18134521
 ] 

ASF GitHub Bot commented on GROOVY-12568:
-----------------------------------------

codeconsole opened a new pull request, #3097:
URL: https://github.com/apache/groovy/pull/3097

   The JVM allows access to a protected or package-private member only within 
the same run-time package, which is the package name and the class loader. A 
`GroovyClassLoader` defines each compilation in memory with a class loader of 
its own (a `GroovyShell` script, `parseClass`, a script run with `groovy`, 
JSR-223), so a class from an earlier parse, or a precompiled class used by such 
a script, is in another run-time package even with the same package name. 
Statically compiled code accessed such members directly and failed with 
`IllegalAccessError`:
   
   ```groovy
   // first parse
   package p
   class A { @PackageScope A(String s) {}; @PackageScope String name() { 'a' }; 
@PackageScope String tag = 't' }
   
   // second parse with the same GroovyShell
   package p
   @CompileStatic List make() { def a = new A('x'); [a.name(), a.tag] }
   ```
   
   For a compilation defined in memory, statically compiled code now reaches 
such a constructor, method or field through the meta-class, as dynamic code 
does. This covers `new` and map-style construction, instance and static 
methods, getters and setters used as properties, method references such as 
`a::name` (through a synthetic method), and field reads and writes including 
`.@`, `+=` and `++`. In dynamic code, `super.f`, `super.@f` and `this.@f` of 
such a field and `super.m()` of such a method work too (no `super$` method is 
generated for a package-private method of another run-time package). Public 
members, classes of the same compilation, and protected members used from a 
subclass through `this`, `super` or a receiver of the subclass type are still 
accessed directly. Code compiled to class files (groovyc, Gradle, Maven, Ant, 
joint compilation with Java) keeps direct access to the members of the other 
classes of its package, as javac does, and a full and an incremental build give 
the same bytecode. The rule is `AsmClassGenerator.isInSameRuntimePackage`, 
which #3022 introduces for constructor references; this applies it to the rest.
   
   Protected members of another package, in a subclass, under static 
compilation:
   
   - `new A(...)` of a protected constructor of another package is reported: 
"Cannot call protected constructor for p.A from class q.B". As in Java, only 
`super(...)` and `new A(...) {}` can call it. It used to compile and then fail 
verification. The error is raised while the class is generated, so 
`@TypeChecked` code is not checked for it and works through the meta-class.
   - A protected method of another package called through a receiver of the 
superclass type (`a.m()`, `a.name`, `a::m`, `A::m`) is reported as "Cannot 
access method", as GROOVY-7325 intended: its check compared the types the wrong 
way round. These used to compile and then fail with `VerifyError`. `a.@field = 
value` through such a receiver now goes through the meta-class, like the read.
   
   With invokedynamic, a dynamic `new` of a constructor that the JVM refuses, 
such as `new ObjectInputStream()` in a subclass, now fails with "could not 
access constructor", as it does without invokedynamic, instead of 
`GroovyBugError`.
   
   COMPATIBILITY.md has an entry for the meta-class semantics of these accesses 
and the two new compile errors.
   
   This builds on #3022 (constructor references), which adds 
`isInSameRuntimePackage`, the `GroovyClassLoader` marker and 
`StaticInvocationWriter.isDirectlyAccessible`; its commits are included here 
until it is merged.
   
   ## Tests
   
   All marked `// GROOVY-12568`, each failing without the change it covers:
   
   - `StaticCompileRuntimePackageTest`: constructors, map-style construction 
(literal and `Map` variable), methods, properties, method references (also in 
static and default methods of an interface) and fields of a class of another 
class loader, with and without invokedynamic, compared with the same code 
compiled dynamically; bytecode tests pin that classes of the same compilation, 
public members, class files, incremental builds and joint compilation keep 
direct access;
   - `bugs.Groovy12568`: dynamic `super.m()` of a package-private method of 
another class loader, and invokedynamic `new` of a protected constructor of 
another package or of the JDK;
   - `ConstructorsSTCTest` and `StaticCompileConstructorsTest`: `new` of a 
protected constructor of another package, with `super(...)` and anonymous 
classes still direct;
   - `Groovy7325Bug`: protected methods of another package through a receiver 
of the superclass and of the subclass.
   
   The `groovy.*`, `gls.*`, `bugs.*`, `org.codehaus.groovy.classgen` and 
`org.codehaus.groovy.transform` packages pass (13,458 tests), apart from 
`bugs.Groovy12363`, which also fails on master when it runs after 
`bugs.Groovy12142` in the same JVM (GROOVY-12580, #3096, fixes it), and 
`bugs.Groovy9815`, whose `@Grab` download failed once and passes when rerun. 
The Checkstyle and CodeNarc gates pass.
   




> SC: protected and package-private members of a class defined by another 
> GroovyClassLoader compilation fail with IllegalAccessError
> ----------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: GROOVY-12568
>                 URL: https://issues.apache.org/jira/browse/GROOVY-12568
>             Project: Groovy
>          Issue Type: Bug
>          Components: Static compilation
>    Affects Versions: 6.0.0
>            Reporter: Scott Murphy Heiberg
>            Priority: Major
>
> The JVM allows access to a protected or package-private member only within 
> the same run-time package, which is the package name and the class loader. A 
> {{GroovyClassLoader}} defines each compilation in memory with a class loader 
> of its own: a {{GroovyShell}} script, {{parseClass}}, a script run with 
> {{groovy}}, JSR-223. So a class of an earlier parse, or a precompiled class 
> used by such a script, is in another run-time package even with the same 
> package name. Statically compiled code calls such members directly and fails; 
> dynamic code goes through the meta-class and works.
> {code:groovy}
> def shell = new GroovyShell()
> shell.evaluate '''package p
>     class A {
>         @groovy.transform.PackageScope A(String s) { }
>         @groovy.transform.PackageScope String name() { 'a' }
>         @groovy.transform.PackageScope String tag = 't'
>     }
>     true
> '''
> shell.evaluate '''package p
>     @groovy.transform.CompileStatic
>     List make() { def a = new A('x'); [a.name(), a.tag] }
>     make()
> ''' // IllegalAccessError; without @CompileStatic: ['a', 't']
> {code}
> The same happens for map-style construction, instance and static methods, 
> getters and setters used as properties, method references such as 
> {{a::name}}, and field reads and writes including {{.@}}, {{+=}} and {{++}}. 
> In dynamic code, {{super.f}}, {{super.@f}} and {{this.@f}} of such a field, 
> and {{super.m()}} of such a method, fail the same way, because the {{super$}} 
> helper and the field instructions are generated directly.
> Related, in a subclass in another package, under static compilation:
> * {{new A(...)}} of a protected constructor of another package compiles and 
> then fails verification. As in Java, only {{super(...)}} and {{new A(...) 
> {}}} can call it.
> * a protected method of another package called through a receiver of the 
> superclass type ({{a.m()}}, {{a.name}}, {{a::m}}, {{A::m}}) compiles and then 
> fails with {{VerifyError}}: the GROOVY-7325 check compared the types the 
> wrong way round. {{a.@field = value}} through such a receiver fails 
> verification too.
> * with invokedynamic, a dynamic {{new ObjectInputStream()}} in a subclass 
> fails with {{GroovyBugError}} ("member is protected") instead of "could not 
> access constructor".
> Expected: such members are reached through the meta-class, as in dynamic 
> code; code compiled to class files (groovyc, Gradle, Maven, joint 
> compilation) keeps direct access, as javac does; the two verification 
> failures are compile errors.



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

Reply via email to