<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
I'll answer your questions with more questions. <br>
<br>
Part of your question depends on: is applying an update to a model
(producing a new model) expensive enough that it is better to let an
updater also act as a model-view of sorts, or should you just apply
the update whenever you want to make changes visible to later
operations, and move on? Secondarily, it raises the question about
what the natural granularity of updates is: should a condenser batch
all its updates into a single updater, or should it generate
finer-grained updates?<br>
<br>
As you point out, you have choices of how to write the condenser --
incrementally or batched. Your initial example is incremental,
searching for classes (B) with lambdas, and rewriting both that
class and the nest host (A and B) together. You then raise the
possibility of accumulating a data structure of classes with lambdas
to be rewritten, and doing it all at once. This question of how to
approach classfile transformation is essentially the same question
as how to approach updating the application model, just moved around
a bit.<br>
<br>
My working assumption is that applying an update to a model will
eventually be cheap enough that either option 2 or option 3 will
always be preferable to option 1. Option 1 involves a lot of "do
the same thing two ways", which means more code, more surface, more
confusion, and more chance for things to get out of sync. <br>
<br>
To your question about classpath keys / module keys, the data model
says these are singletons (the double circles.) In the current
(mostly mechanical) mapping of data model to API, for each attribute
of these singleton keys, we generate a Stream-bearing method to
iterate the attribute values (e.g., modules() and classPath()). The
actual ClassPathKey, being a singleton, is completely uninteresting
(its an empty record) so it didn't get a representation in the API.
(You might reasonably characterize this as premature API
optimization, and that's a fair discussion.)<br>
<br>
<br>
<div class="moz-cite-prefix">On 8/2/2023 10:36 AM, Dan Heidinga
wrote:<br>
</div>
<blockquote type="cite" cite="mid:CAJq4Gi7QE4FR1p1OuZCxwPVbcC58pYX0t4SBbCkTRQukcSmaNQ@mail.gmail.com">
<div dir="ltr">Is there a way to query the current value from a
ModelUpdater?
<div><br>
</div>
<div>The use case I'm thinking of is likely to be fairly common
where updating one class requires updates to another (ie: Nest
attributes). Take the follow nest of classes and a condenser
that pregenerates lambdas as an example:</div>
<div><br>
</div>
<div>class A {</div>
<div> NestMembers: [B, C]</div>
<div>}</div>
<div><br>
</div>
<div>class B {</div>
<div> NestHost: A</div>
<div><br>
</div>
<div> void foo() { Runnable r = () -> ....; }</div>
<div>}</div>
<div><br>
</div>
<div>
<div>class C {</div>
<div> NestHost: A</div>
<div><br>
</div>
<div> void bar() { Runnable r = () -> ....; }</div>
<div>}</div>
</div>
<div><br>
</div>
<div>When processing class B, a new class will be generated and
it will be necessary to update A's NestMembers to include the
pregenerated class so the constraint that the Host knows its
members is maintained. Similarly, when processing class C,
A's NestMembers will again need to be updated.</div>
<div><br>
</div>
<div>I think this becomes the following calls to the APIs:</div>
<div><br>
</div>
<div>public ApplicationModel condense(ApplicationModel model) { </div>
<div> // NIT: Should this be ApplicationModel or Model?
Examples use ApplicationModel but interface is named "Model"<br>
</div>
<div><br>
</div>
<div> ModelUpdater updater = model.updater();</div>
<div><br>
</div>
<div> // ----- process class B ------ </div>
<div> // update the nestHost's nestMembers</div>
<div> ClassKey classK = getNestHostClassKey(?????);</div>
<div> updater.addToContainer(classK, </div>
<div> updateNestHost(model.classContents(classK))); //
First fetch of classContents</div>
<div>
<div><br>
</div>
<div> // ----- process class C ------ </div>
<div> // update the nestHost's nestMembers</div>
<div> ClassKey classK = getNestHostClassKey(?????);</div>
<div> updater.addToContainer(classK, </div>
<div> updateNestHost(model.classContents(classK))); //
Critical line: where do I get classContents?</div>
<div><br>
</div>
</div>
<div> return model.apply(updater);</div>
<div>}</div>
<div><br>
</div>
<div>The critical question is where do I get the classContents
for the second update to the classK? The Model is immutable
so the second query will get the original bytes and lose the
changes done by the first update. </div>
<div><br>
</div>
<div>Option 1: Query the ModelUpdater instead? That would imply
that ModelUpdater extends Model which may be ugly to patch the
Stream's to return the updated values instead.</div>
<div><br>
</div>
<div>Option 2: Batch the updates and only apply them once?
That's what I do now in the jlink plugin but it means carrying
more state in the condenser. It keeps this as the users
problem to manage state</div>
<div><br>
</div>
<div>Option 3: Generate a new Model after every transformation?</div>
<div><br>
</div>
<div>Model m = model;</div>
<div>{ </div>
<div> ModelUpdater mUpdater = m.updater();</div>
<div> // Process class B</div>
<div> m = m.apply(mUpdater);</div>
<div>}</div>
<div>{ </div>
<div> ModelUpdater mUpdater = m.updater();</div>
<div> // Process class C</div>
<div> m = m.apply(mUpdater);</div>
<div>} </div>
<div><br>
</div>
<div>All three options are workable but result in different API
shapes. Option 3 has some nice debugging properties in that
it's always easy to see what changed in each transformation
and it may play well with structured logging as discussed in
the other thread. </div>
<div><br>
</div>
<div>An unrelated question: where does ClassPathKey() or
ModulesKey() show up in the model? They aren't subtypes of
ContainerKey which is where I expected them to appear. How do
you see them being used?</div>
<div><br>
</div>
<div>Sorry for the deluge of questions / comments. I'm excited
to see the progress here.</div>
<div><br>
</div>
<div>--Dan</div>
<div><br>
</div>
</div>
<br>
<div class="gmail_quote">
<div dir="ltr" class="gmail_attr">On Mon, Jul 31, 2023 at
4:31 PM Mark Reinhold <<a href="mailto:mark.reinhold@oracle.com" moz-do-not-send="true" class="moz-txt-link-freetext">mark.reinhold@oracle.com</a>>
wrote:<br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px
0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">A
few of us have been thinking about how condensers might work.
We now<br>
have a prototype design and implementation of a condenser API
and tool.<br>
<br>
We’ve deliberately started small, focusing on principles of
condenser<br>
operation and a minimal set of features sufficient for the
simplest<br>
condensers, i.e., those that don’t require additional changes
to the<br>
Platform Specification.<br>
<br>
Design note: <a href="https://openjdk.org/projects/leyden/notes/03-toward-condensers" rel="noreferrer" target="_blank" moz-do-not-send="true" class="moz-txt-link-freetext">https://openjdk.org/projects/leyden/notes/03-toward-condensers</a><br>
<br>
Summary:<br>
<br>
We elaborate the concept of composable condensers to
introduce a<br>
simple, abstract, immutable, data-driven model of
applications so that<br>
condensers can be expressed as transformers of instances of
the model.<br>
The model is sufficient to express simple condensers; we
include two<br>
examples.<br>
<br>
This is just a starting point; we expect to evolve it
considerably going<br>
forward. We’ll publish the prototype code shortly after we
return from<br>
the upcoming JVM Language Summit.<br>
<br>
- Mark</blockquote>
</div>
</blockquote>
<br>
</body>
</html>