On Thu, 12 Jan 2023 19:01:10 GMT, Maurizio Cimadamore <[email protected]>
wrote:
>> The code you quoted has nothing specifically to do with method invocations.
>> This code is simply handing the evaluation of the expressions `this` and
>> `super`. For example, `this` could just be a parameter we're passing to
>> another method.
>>
>> When `this` or `super` expressions are evaluated, the thing left on top of
>> the stack is a direct/indirect reference (i.e., an `ExprRef`) exactly when
>> the there is a direct/indirect reference of type `ThisRef` in the current
>> reference set.
>>
>> In cases where `this` is then "invoked", e.g., `this()`, the `ExprRef` (top
>> of Java stack) becomes the method receiver, and when the method is "invoked"
>> it turns back into a `ThisRef` (see earlier question).
>>
>> Regarding the optimization you mention, in light of the above I'm not sure
>> it would still work.
>
> My point is about who puts ThisRef in the set to begin with. It seems to me
> that ThisRef is put there at the start of a method analysis. After which,
> there's several code points where we say "if there's a direct ThisRef in the
> set, do this, otherwise, if there's an indirect ThisRef in the set, do that".
> But the ThisRef (whether indirect or direct) seems set once and for all (when
> we start the analysis, and then inside visitApply).
>
> There is also this bit in `visitReference`:
>
>
> case SUPER:
> if (this.refs.contains(ThisRef.direct()))
> receiverRefs.add(ThisRef.direct());
> if (this.refs.contains(ThisRef.indirect()))
> receiverRefs.add(ThisRef.indirect());
> break;
>
>
> But this doesn't change what I'm saying - there seems to be a general
> property when we execute this analysis which says whether the current
> execution context has a direct `this` or not. This seems to me better
> captured with a boolean, which is then fed back to all the other various
> downstream ref creation.
>
> The main observation, at least for me, is that the code unifies everything
> under refs, when in reality there are different aspects:
>
> * the set of variables that can point to this, whether directly or indirectly
> (this is truly a set)
> * whether the current context has a direct or indirect this (this seems more
> a flag to me)
> * whether the expression on top of stack is direct/indirect this reference or
> not (again, 3 possible values here) - but granted, there `depth` here to take
> into account, so you probably end up with a set (given that we don't want to
> model a scope with its own set)
>
> When reading the code, seeing set expression like containment checks or
> removals for things that doesn't seem to be truly sets (unless I'm missing
> something) was genuinely confusing me.
I get what you're saying - it seems silly to model what is essentially a fixed,
boolean property with the membership of a singleton in a set field, rather than
with a simple boolean field.
There is a conceptual trade-off though... a lot of the code relates to
converting `Ref`'s from one type to another. For example, as pointed out above,
a method invocation might convert a `ExprRef` to a `ThisRef`, then to a
`ReturnRef`'s, etc. Having these things all be considered part of the same
family helps conceptually. The fact that a direct `ThisRef` is a singleton is
just a coincidence in this way of looking at things.
The benefit is the simplicity of being able to think of the data model as "just
a set of references".
For example, methods like `RefSet.replaceExprs()` become less elegant (or
basically impossible) if there have to be special cases for each type of
reference, whereas currently we can do clean stuff like this:
@Override
public void visitReturn(JCReturn tree) {
scan(tree.expr);
refs.replaceExprs(depth, ReturnRef::new);
}
But I'm also realizing now that several places can be cleaned up by taking this
event further. E.g., we should replace this code:
if (refs.contains(ThisRef.direct()))
outerRefs.add(OuterRef.direct());
if (refs.contains(ThisRef.indirect()))
outerRefs.add(OuterRef.indirect());
with something like this:
refs.mapInto(outerRefs, ThisRef.class, OuterRef::new);
I will go through and refactor to do that and clean things up a little more.
> When reading the code, seeing set expression like containment checks or
> removals for things that doesn't seem to be truly sets (unless I'm missing
> something) was genuinely confusing me.
Probably my fault for not providing better documentation of the overall "set of
references" conceptual approach. FWIW I added a little bit more in f83a9cf0.
-------------
PR: https://git.openjdk.org/jdk/pull/11874