Repository navigation
GROOVY-12717: SC: report new of a protected constructor of another package and a protected method through a superclass receiver - #3251
Conversation
…ckage instead of failing verification
Outside its package, a protected constructor can only be called by
super(...) or by creating an anonymous inner class (JLS 6.6.2.2). The type
checker accepts new A(...) of such a constructor in a subclass of A, as it
accepts other protected members there, and type-checked code creates the
object through the meta-class. Under static compilation, the call was
compiled to NEW and INVOKESPECIAL, and the class failed verification when
it was loaded: "VerifyError: Bad access to protected <init> method". The
same happened for a map-style construction such as new A([:]), which calls
the protected no-argument constructor.
The static compiler now reports "Cannot call protected constructor for A
from class B", as it reports a protected method called on a receiver that
is not the subclass (GROOVY-7325). super(...), this(...) and
new A(...) {} still call the constructor directly. A constructor reference
A::new to such a constructor still selects it at run time.
… through a superclass receiver Outside its package, a protected instance method can be used only through a receiver of the subclass (JLS 6.6.2.1, JVMS 4.10.1.8). The static compiler meant to report such a call (GROOVY-7325), but its check compared the types the wrong way round: it tested whether the current class is a subtype of the receiver type instead of the reverse. So a.m() with a receiver of the superclass type, in a subclass in another package, was compiled to a direct call and the class failed verification: "VerifyError: Bad access to protected data in invokevirtual". This also hit a protected getter or setter used as a property (a.name, a.name = value). A method reference such as a::m or A::m to such a method failed with BootstrapMethodError. The check now requires the receiver to be of the current class (or this, super or a static method), so such a call or method reference is reported as "Cannot access method", as GROOVY-7325 intended and as javac does. Calls through this, super or a receiver of the subclass type are still direct.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #3251 +/- ##
==================================================
+ Coverage 74.4524% 74.4627% +0.0103%
- Complexity 44845 44862 +17
==================================================
Files 1702 1702
Lines 151995 152013 +18
Branches 29122 29131 +9
==================================================
+ Hits 113164 113193 +29
+ Misses 28422 28412 -10
+ Partials 10409 10408 -1
🚀 New features to boost your workflow:
|
daniellansun
left a comment
There was a problem hiding this comment.
LGTM for GROOVY-12717.
Outside its package, a protected constructor may only be invoked via super(...) or an anonymous subclass (JLS 6.6.2.2), and a protected instance method needs a receiver of the calling subclass (JVMS 4.10.1.8). Reporting those at SC generation time — instead of a VerifyError / BootstrapMethodError at load — is the right UX, and leaving @TypeChecked on the meta-class path is consistent with dynamic Groovy.
Notable pieces: StaticInvocationWriter constructor accessibility error; protected instance-method receiver check excluding super / class-with-super; method-reference validation for protected cross-package receivers; map-style construction routed through the invocation writer so the same check applies. Tests cover ObjectInputStream/ObjectOutputStream, cross-package Groovy types, property accessors, bound and unbound references, and the allowed subclass-receiver / AIC / super() cases.
CI fully green. Companion to the GROOVY-12568 runtime-package work (#3097 and siblings); land with that family in mind. Soft adjacency to #3253 (indy refused constructor → meta-class) — complementary layers.
The tests were marked with GROOVY-12568, where they came from before the change moved to its own issue.
…package # Conflicts: # src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java # src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java # src/main/java/org/codehaus/groovy/transform/sc/transformers/ConstructorCallTransformer.java
🚨 All tests passed but jobs failed 🚨Failed Jobs without Test Failures🏷️ Commit: 0b62c0e Rerun ControlsClick the checkbox to trigger a rerun:
Learn more about TestLens at testlens.app/docs. |
Under
@CompileStatic, this compiles and then fails when the class is loaded:5.0.8 and 6.0.0 fail the same way. Neither has to do with class loaders: these are Java's rules for a protected member outside its package.
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, onlysuper(...)andnew A(...) {}can call it. That includes a map-style construction, which calls the protected no-argument constructor. The error is raised while the class is generated, so@TypeCheckedcode is not checked for it and works through the meta-class.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.Calls through
this,superor a receiver of the subclass type are still direct, andA::newstill selects such a constructor at run time.An attribute assignment
a.@f = vthrough a superclass receiver still fails verification inputfield. It is not changed here.This and #3097 (GROOVY-12568) touch the same lines of
StaticInvocationWriter,StaticTypesMethodReferenceExpressionWriterandConstructorCallTransformer; whichever lands second keeps both. Once this is in, a map-style construction reaches the check of #3097 throughwriteInvokeConstructor, so its own call inConstructorCallTransformeris not needed.Compatibility
No working code is affected: this code failed verification or at link time. It now fails at compile time, under
@CompileStaticonly.Tests
Groovy7325Bug.testProtectedMethodOfAnotherPackageThroughReceiverOfSuperclass,StaticCompileConstructorsTest.testProtectedConstructorOfAnotherPackageandtestMapStyleConstructionWithProtectedConstructorOfAnotherPackagefail without the change.Groovy7325Bug.testProtectedMembersOfAnotherPackageThroughReceiverOfSubclassand theConstructorsSTCTestadditions pin what keeps working. 5,684 tests inorg.codehaus.groovy.classgen.asm.sc,groovy.transform.stcandglspass. The Checkstyle and CodeNarc gates pass.