<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<font size="4"><font face="monospace">Yes, sorry for the delay, I've
been trying to organize my thoughts on this. <br>
<br>
Overall I am very happy to see this investigation. It is
obviously relevant to a number of points across the Leyden
spectrum. I had done a related thought experiment at one point
about behavioral differences, and came up with a similar list
with respect to LambdaMetafactory:<br>
</font></font><br>
<font size="4"><font face="monospace"><font size="4"><font face="monospace"> - proxy class goes from hidden to
non-hidden;<br>
- perturbs the set of nestmates of both the proxy class and
capturing class;<br>
- potentially perturbs the timing of loading the proxy
class (though this can be controlled);<br>
- freezing of bootstrap behavior -- if the bootstrap
behavior were to change between build time and runtime
(e.g., different JDK), any changes wouldn't be reflected in
the execution. <br>
<br>
Your "stack traces" observation wasn't on my list, so that's
a good catch. <br>
</font></font></font></font><br>
<font size="4"><font face="monospace"><font size="4"><font face="monospace"><font size="4"><font face="monospace">The
"freezing of behavior" one is likely to be common to a
number of Leyden techniques, such as AOT. The answer
there likely involves the creation of some sort of
coupling between the artifact and a specific JDK
version. Since the main mission of `jlink` is to create
a runtime image with both an application and a specific
JDK, this seems sensible but there is likely additional
spec work needed here. <br>
<br>
Overall, none of these seem like show stoppers, but the
devil is in the details. There's categories of details
here, too, such as implementation vs specification. <br>
<br>
The implementation details, such as "is it OK to have
the lambda proxy class be findable via Class::forName"
(even if the lambda is never captured!) need to at least
be evaluated through the security lens; does it allow
anyone to instantiate a lambda with bogus captured
arguments? I'm guessing no, because the
constructor/factory is still private to the nest, but
this is the sort of questions we'd have to ask
ourselves. My gut feeling says that these behavioral
changes can be, as you suggest, framed as acceptable
implementation variation. <br>
<br>
From a specification perspective, there are multiple
separate specifications viewpoints to consider: JLS, JDK
and JVMS. From a JLS perspective, I would say that if
the Java *compiler* were to do what your jlink plugin
does, this would be a reasonable way to implement a
compiler for the Java language -- the classfiles emitted
would respect the semantics of the language. There's
nothing that says a Java compiler has to translate
lambdas with indy, or with hidden classes, so if the
indy never got generated, that's not a problem. <br>
<br>
From the JDK+JVMS perspective, it starts to get a little
murky, and one of the goals of Leyden is to bring more
clarity to this area. The compiler emits certain
classfiles with `invokedynamic`, and then some
build-time tool rewrites these classes to be different.
Is this OK? If the build-time tool is just "Dan's Magic
Unofficial (Not) Java Bytecode Mangler", then this is
the sort of build time mangling people do every day.
But we want this to be an official part of the platform,
so I think there's a little more specification work to
be done to allow (and specify) such transformations.
This is not a deal breaker, but we need to apply more
thought here. I think there are two categories of new
work here: some specification work to characterize what
build-time transformations like this are allowed to do
or not do, and your transformer will likely want a
specification for what it does as well. <br>
<br>
As with related techniques such as intrinsification, we
need to ensure that there are not going to be observable
differences with respect to behavior specified by either
JVMS or JDK, or that those differences are permissible
under the specifications. Some of the things to worry
about here might be: <br>
<br>
- timing of loading the proxy class<br>
- observable side-effects of indy linkage<br>
- observable side-effects of bootstrap execution<br>
- conformance with LMF specification, not just for the
code shapes emitted by `javac`, but for any code shape
supported by LMF<br>
<br>
From a side-effects perspective, the answer might well
be "there aren't any", but the claim "this code has no
side-effects" is often both tricky to ascertain, and can
easily become false over time as the code is evolved.
As an example (I'm not worried about this one, but it is
a good illustration), there's a system property,
`jdk.internal.lambda.dumpProxyClasses`, which causes
proxy class files to be dumped to the file system for
debugging. That's a side-effect of bootstrap execution
that would not happen (or would happen at build time
instead of run time). As this one turns out, this is an
implementation detail, not a specified behavior, but
this is the sort of line-by-line analysis we'd have to
do to convince ourselves that what we're doing is safe
-- and watch how the bootstrap implementation evolves to
keep it so.<br>
<br>
</font></font><br>
</font></font></font></font>
<div class="moz-cite-prefix">On 8/4/2022 8:57 AM, Dan Heidinga
wrote:<br>
</div>
<blockquote type="cite" cite="mid:CAJq4Gi7nE3qxDwzs36H_kVf29QhBpH_jqs6XqQ=pAf1t63jMrQ@mail.gmail.com">
<pre class="moz-quote-pre" wrap="">Hi Brian,
Glad we're on the same page regarding isHidden being an implementation
detail. Do the ::getNestHost & ::getNestMembers calls and stacktrace
differences fall into the same implementation detail bucket in your
mind?
I'd be happy for the nest mates cases to be implementation details
but would need to look closer at the intersection of stacktraces,
@callerSensitive methods, and the SecurityManager to be certain
stacktrace differences aren't making bigger problems. Any other areas
concern with this kind of approach?
--Dan
On Wed, Aug 3, 2022 at 7:26 PM Brian Goetz <a class="moz-txt-link-rfc2396E" href="mailto:brian.goetz@oracle.com"><brian.goetz@oracle.com></a> wrote:
</pre>
<blockquote type="cite">
<pre class="moz-quote-pre" wrap="">
</pre>
<blockquote type="cite">
<pre class="moz-quote-pre" wrap="">isHidden() returning false is a compatibility issue because i've seen it used has an equivalent of isALambda() (like isAnonymous() was used before isHidden()), GraalVM emulates isHidden() for this reason.
</pre>
</blockquote>
<pre class="moz-quote-pre" wrap="">
I'm not very sympathetic here. Code that interprets isHidden in this
way is just wrong. There were extensive discussions about "how do I
detect whether an object is a lambda" and the answer has consistently
been "don't try, you don't need to know, and none of the mechanisms
answer the question you are asking."
</pre>
<blockquote type="cite">
<pre class="moz-quote-pre" wrap="">For me, instead of trying to emulate those differences, i think it's easier here to provide a method Class.isLambdaProxy() and adds an empty classfile attribute LambdaProxy in the VM spec so both the lambda proxy generated using invokedynamic or pre-generated will mostly behave the same way.
</pre>
</blockquote>
<pre class="moz-quote-pre" wrap="">
We made a very clear decision in the JSR 335 EG -- that at runtime,
lambdas are not a thing. The question of "are you a lambda proxy" is no
more interesting than "was it a tuesday when the source file for this
class was last changed", and it was a deliberate choice to not provide
any sort of reflection support here. So I would not want to expose
this; it's an implementation detail.
</pre>
</blockquote>
<pre class="moz-quote-pre" wrap="">
</pre>
</blockquote>
<br>
</body>
</html>