[
https://issues.apache.org/jira/browse/GROOVY-12569?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18135274#comment-18135274
]
ASF GitHub Bot commented on GROOVY-12569:
-----------------------------------------
codeconsole commented on PR #3098:
URL: https://github.com/apache/groovy/pull/3098#issuecomment-6527469623
@daniellansun
1. CI: the push of these commits starts a new run.
2. Coverage. Codecov's per-line data for b0aa14e21d: 34 of the 53 lines are
this PR's; the other 19 are #3097's, which Codecov counts here because the PR
is compared with master. 62754d72c9 adds tests for the branches that can be
reached, and 45f47d8a27 removes the ones that cannot. Line numbers are from
b0aa14e21d.
New tests in `StaticCompileRuntimePackageTest` use a fixture with the
public `Open`, the package-private `Shut`, `Shut2 extends Shut`, `Plain` and
the interface `Secret`, and the public `Pub extends Shut`.
`testPackagePrivateClassesOfAnotherClassLoaderAreUsedThroughPublicClassesAsInDynamicCode`
checks that dynamic and `@CompileStatic` code give the same results, and
`testPackagePrivateClassesOfAnotherClassLoaderAreNotNamedInBytecodeOfUsesThroughPublicClasses`
that no instruction names `p/Shut`, `p/Plain` or `p/Secret`:
- `AsmClassGenerator.getDirectlyAccessibleSuperClass` 1387/1388/1390:
`new Shut2()`, whose superclass `Shut` is hidden too, steps to `Open`;
`Open.secret()` returns the interface `Secret`, which has no superclass, so
`Object`.
- `StaticInvocationWriter.findDirectlyAccessibleDeclaration` 273/275:
through `Shut2` the lookup passes the hidden `Shut`; `s.prot()` finds the
protected `Open.prot()`, so the call is made at run time. 442/445/446:
`s.take(s)` finds the public `Open.take(Shut)`, whose parameter type is hidden,
so at run time; `s.take('x')` stays `INVOKEVIRTUAL p/Open.take`. 773:
`list*.@pf`.
- `StaticTypesCallSiteWriter` 555/920: `Shut.SF` and `Shut.@SF = 'w'`
give `GETSTATIC`/`PUTSTATIC p/Open.SF`; 911: `s.@pf = 'q'` gives `PUTFIELD
p/Open.pf`; 899/900: `s.@peer = s` sets a field typed `Shut`, so at run time.
- `StaticTypesMethodReferenceExpressionWriter` 159/162/166/167/165/168:
`Open::toString` as `Function<Shut, String>`, `Open::make` as `Supplier<Shut>`
and `Supplier<Object>`, `Open::take` with the `take(Shut)` overload, and
`Pub::own`, which the hidden `Shut` declares.
- The `!method.isStatic()` side of 275 needs a Java class, since Groovy
rejects an instance method named like a static interface method: in
`testPackagePrivateJavaClassOfAnotherClassLoaderWithInstanceMethodNamedAsStaticMethodOfInterface`,
`Q implements Function` declares an instance `identity()`; without the check,
`q.identity()` compiles to the static `Function.identity()`.
Removed in 45f47d8a27:
- `StaticInvocationWriter.writeInvokeConstructor` 141: the
`DYNAMIC_RESOLUTION` branch and the marker `ConstructorCallTransformer` set for
it. The map-style call already reaches the
`!isClassDirectlyAccessible(call.getType())` branch below, since its
`DIRECT_METHOD_CALL_TARGET` is the generated map constructor;
`testMapStyleConstructionOfPackagePrivateClassOfAnotherClassLoader` still
passes.
- 241: `BytecodeHelper.doCast` already skips `Object`. 254: no caller
passes `null`.
- 490: `written` is always true there, and `getStackLength() > 0` tested
the wrong thing, since the stack can hold other operands; it now compares the
depth before and after the call, which also covers an elided `void` result such
as `h.run()`.
- `StaticTypesCallSiteWriter.makeGetField` 601: the boxed type of a safe
primitive read goes into `valueType`, so the ternary is gone; a new `s?.n`
reads a public `int` field.
- `MethodPointerExpressionWriter` 87 and
`StaticTypesMethodReferenceExpressionWriter.inferredType` 177–182: the
fallbacks to the accessed variable's inferred or origin type never found a type
(178 and 182 never ran in CI); a receiver of a hidden class always carries
`INFERRED_TYPE` itself.
- `StaticTypesTypeChooser` 58: `getTarget` of a `VariableExpression` is
always a `Variable`.
Partial on purpose:
- `StaticTypesTypeChooser.resolveType` 43: the `type != null && current
!= null` checks stay (a44e7610b2). No caller in the compiler passes `null`, but
the method is public and a type checking extension can call it.
- `AsmClassGenerator` 1317: the missing branch is the existing `field ==
null` guard of the public `isFieldDirectlyAccessible`; all three callers pass a
field. The new `isClassDirectlyAccessible` disjunct is covered both ways:
without it, 4 tests fail.
- `StaticTypesCallSiteWriter` 258: `isFieldOfAnotherRuntimePackage` runs
only after `makeGetField` refused a direct read, which never happens for a
public field of a class the bytecode can name or for a field of the same
run-time package.
With JaCoCo over the `sc`, `stc` and `classgen.asm` test packages (4,808
tests), every line this PR adds is covered apart from those. The three new
tests fail without the change (`IllegalAccessError` for `p.Shut2` and for the
Java `p.Q`, and a `CHECKCAST p/Shut`). The Checkstyle and CodeNarc gates pass.
> SC: a package-private class of another GroovyClassLoader compilation fails
> with IllegalAccessError where dynamic code works
> ---------------------------------------------------------------------------------------------------------------------------
>
> Key: GROOVY-12569
> URL: https://issues.apache.org/jira/browse/GROOVY-12569
> Project: Groovy
> Issue Type: Bug
> Components: Static compilation
> Affects Versions: 6.0.0
> Reporter: Scott Murphy Heiberg
> Priority: Major
>
> A class that is not public is accessible only within its run-time package
> (JVMS 5.4.4), and the JVM checks that wherever an instruction names the
> class. Dynamic code uses a package-private class of an earlier
> {{GroovyShell}} parse, or of a library used by a script, without naming it.
> Statically compiled code names it and fails:
> {code:groovy}
> def shell = new GroovyShell()
> shell.evaluate '''package p
> @groovy.transform.PackageScope class Hidden implements Runnable {
> static String sm() { 'sm' }
> String name
> String own() { 'own' }
> void run() { }
> }
> true
> '''
> shell.evaluate '''package p
> @groovy.transform.CompileStatic
> def test() {
> def h = new Hidden(name: 'x')
> h.run()
> Runnable r = h
> [Hidden.sm(), h.own(), h.name]
> }
> test()
> ''' // each line fails with IllegalAccessError; without @CompileStatic it
> works
> {code}
> Expected: as in dynamic code, {{new}} and static members go through the
> meta-class, a method is called through a public superclass or interface that
> declares it (else the meta-class), and a value of the class is held as its
> nearest public superclass. A variable declared with the class, a cast to it
> and {{instanceof}} still fail, as in dynamic code. Classes compiled to class
> files are used directly.
> PR: https://github.com/apache/groovy/pull/3098
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)