RFR: 8015831: Add lint check for calling overridable methods from a constructor [v7]
Maurizio Cimadamore
mcimadamore at openjdk.org
Thu Jan 12 16:46:31 UTC 2023
On Thu, 12 Jan 2023 16:20:12 GMT, Archie L. Cobbs <duke at openjdk.org> wrote:
>> src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java line 218:
>>
>>> 216: new TreeScanner() {
>>> 217:
>>> 218: private Lint lint = ThisEscapeAnalyzer.this.lint;
>>
>> On a first look I'm not sure about the granularity of suppression here. I believe that suppressing at the class level, or at the constructor level is enough. Allowing to further annotate var declarations and non-constructor methods, while doable, might actually be counter productive - in the sense that the annotation does not occur in the constructor (where you'd want to see it) but in some other method. I think the fact that a constructor is escaping (willingly) should be a visible thing. Something to consider.
>
> Two things...
>
> We need to support annotations on field declarations because their initializers are effectively mini-constructors. But we don't need it on local variables, parameters, etc. - too confusing.
>
> For annotations on methods, yes it's debatable. It can be handy if you have e.g. an `init()` method that all of your constructors invoke. However, I agree it's also confusing, so I will remove.
>
> But we should keep the notion that if a constructor invokes `this()`, and the invoked constructor has annotations suppressed, then we skip over the constructor invocation.
>
> I will make these updates.
Yep - I guess there's a general theme of "where do we want the warnings to be reported". My general feeling is that reporting a warning on the constructor might be enough - but I see that you try to generate a warning in the exact spot where the leakage happens - which I'm not sure if it's ok, or too clever.
>> src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java line 270:
>>
>>> 268: final boolean analyzable = this.currentClassIsExternallyExtendable() &&
>>> 269: TreeInfo.isConstructor(tree) &&
>>> 270: !tree.sym.isPrivate() &&
>>
>> Why aren't private constructors analyzed? If we have a class with a private constructor and public static factory invoking said constructor, and the constructor makes `this` escape, isn't that an issue we should detect?
>
>> If we have a class with a private constructor and public static factory invoking said constructor, and the constructor makes this escape, isn't that an issue we should detect?
>
> A static factory method will not create a subclassed instance, so there's no 'this' escape problem.
>
> But I admit I completely missed factory methods as a potential thing to worry about.
>
> Is it possible for a leak to be missed due to the use of a factory method?
>
> Hmm. I can't immediately think of how, but if you can come up with an example please share.
I guess what I'm thinking about:
class Foo {
private Foo() {
m(this);
}
public void m() { ... } // overridable
static Foo makeFoo() { return new Foo(); }
}
>> src/jdk.compiler/share/classes/com/sun/tools/javac/comp/ThisEscapeAnalyzer.java line 294:
>>
>>> 292: !(this.currentClass.sym.isSealed() && this.currentClass.permitting.isEmpty()) &&
>>> 293: !(this.currentClass.sym.owner.kind == MTH) &&
>>> 294: !this.privateOuter;
>>
>> Here, note that is the owner of the current class symbol is a method, that covers anonymous classes too, which is a part of `privateOuter`. So the only think we need to check here is whether "currentClass" is private, which is a simple predicate. No need to carry `privateOuter` I believe
>
> Unless you explicitly declare a nested class `private`, it won't have the `ACC_PRIVATE` flag, even though it is "implicitly private" because it has a `private` enclosing class.
>
> Example:
>
> $ cat PrivateOuter.java
> public class PrivateOuter {
> private static class Inner1 {
> static class Inner2 {
> }
> }
> }
> $ javap -v PrivateOuter$Inner1$Inner2
> Classfile /Users/archie/proj/jdk/flex-test/classes/PrivateOuter$Inner1$Inner2.class
> Last modified Jan 12, 2023; size 408 bytes
> SHA-256 checksum 51ba6d39a5e66df2a078761d6424acbea7a8e32b8451f6ca7d2af49889673b2c
> Compiled from "PrivateOuter.java"
> class PrivateOuter$Inner1$Inner2
> minor version: 0
> major version: 63
> flags: (0x0020) ACC_SUPER
> this_class: #7 // PrivateOuter$Inner1$Inner2
> super_class: #2 // java/lang/Object
> ...
>
>
> So we have to keep track of this "implicit privateness" manually, which is what the `privateOuter` flag is for.
D'oh - missed the `|=` - so this keeps updating...
-------------
PR: https://git.openjdk.org/jdk/pull/11874
More information about the serviceability-dev
mailing list