<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0cm;
font-size:10.0pt;
font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
{mso-style-priority:99;
color:blue;
text-decoration:underline;}
p.gmail-m7404434680222909313msolistparagraph, li.gmail-m7404434680222909313msolistparagraph, div.gmail-m7404434680222909313msolistparagraph
{mso-style-name:gmail-m_7404434680222909313msolistparagraph;
mso-margin-top-alt:auto;
margin-right:0cm;
mso-margin-bottom-alt:auto;
margin-left:0cm;
font-size:11.0pt;
font-family:"Calibri",sans-serif;}
span.EmailStyle19
{mso-style-type:personal-reply;
font-family:"Calibri",sans-serif;
color:windowtext;}
.MsoChpDefault
{mso-style-type:export-only;
font-size:10.0pt;}
@page WordSection1
{size:612.0pt 792.0pt;
margin:72.0pt 72.0pt 72.0pt 72.0pt;}
div.WordSection1
{page:WordSection1;}
/* List Definitions */
@list l0
{mso-list-id:170216927;
mso-list-template-ids:1627294592;}
@list l0:level1
{mso-level-start-at:2;
mso-level-tab-stop:36.0pt;
mso-level-number-position:left;
text-indent:-18.0pt;}
@list l1
{mso-list-id:585186127;
mso-list-template-ids:-171939524;}
@list l2
{mso-list-id:1311402665;
mso-list-template-ids:1072090928;}
@list l2:level2
{mso-level-number-format:alpha-lower;
mso-level-tab-stop:72.0pt;
mso-level-number-position:left;
text-indent:-18.0pt;}
@list l3
{mso-list-id:2039087179;
mso-list-template-ids:-547686210;}
@list l3:level1
{mso-level-number-format:bullet;
mso-level-text:\F0B7 ;
mso-level-tab-stop:36.0pt;
mso-level-number-position:left;
text-indent:-18.0pt;
mso-ansi-font-size:10.0pt;
font-family:Symbol;}
@list l3:level2
{mso-level-number-format:bullet;
mso-level-text:\F0B7 ;
mso-level-tab-stop:72.0pt;
mso-level-number-position:left;
text-indent:-18.0pt;
mso-ansi-font-size:10.0pt;
font-family:Symbol;}
@list l3:level3
{mso-level-number-format:bullet;
mso-level-text:\F0B7 ;
mso-level-tab-stop:108.0pt;
mso-level-number-position:left;
text-indent:-18.0pt;
mso-ansi-font-size:10.0pt;
font-family:Symbol;}
@list l3:level4
{mso-level-number-format:bullet;
mso-level-text:\F0B7 ;
mso-level-tab-stop:144.0pt;
mso-level-number-position:left;
text-indent:-18.0pt;
mso-ansi-font-size:10.0pt;
font-family:Symbol;}
@list l3:level5
{mso-level-number-format:bullet;
mso-level-text:\F0B7 ;
mso-level-tab-stop:180.0pt;
mso-level-number-position:left;
text-indent:-18.0pt;
mso-ansi-font-size:10.0pt;
font-family:Symbol;}
@list l3:level6
{mso-level-number-format:bullet;
mso-level-text:\F0B7 ;
mso-level-tab-stop:216.0pt;
mso-level-number-position:left;
text-indent:-18.0pt;
mso-ansi-font-size:10.0pt;
font-family:Symbol;}
@list l3:level7
{mso-level-number-format:bullet;
mso-level-text:\F0B7 ;
mso-level-tab-stop:252.0pt;
mso-level-number-position:left;
text-indent:-18.0pt;
mso-ansi-font-size:10.0pt;
font-family:Symbol;}
@list l3:level8
{mso-level-number-format:bullet;
mso-level-text:\F0B7 ;
mso-level-tab-stop:288.0pt;
mso-level-number-position:left;
text-indent:-18.0pt;
mso-ansi-font-size:10.0pt;
font-family:Symbol;}
@list l3:level9
{mso-level-number-format:bullet;
mso-level-text:\F0B7 ;
mso-level-tab-stop:324.0pt;
mso-level-number-position:left;
text-indent:-18.0pt;
mso-ansi-font-size:10.0pt;
font-family:Symbol;}
ol
{margin-bottom:0cm;}
ul
{margin-bottom:0cm;}
--></style>
</head>
<body lang="en-CZ" link="blue" vlink="purple" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal"><span lang="EN-US" style="font-size:11.0pt"><o:p> </o:p></span></p>
<p class="MsoNormal"><span lang="EN-US" style="font-size:11.0pt"><o:p> </o:p></span></p>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt">On 13.07.2022 13:49, "Rafael Winterhalter" <rafael.wth@gmail.com> wrote:<o:p></o:p></span></p>
</div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt"><o:p> </o:p></span></p>
<div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt">ASM offers to override a method in ClassWriter to compute frames without loading classes. Byte Buddy does so, and it is possible to generate stack map frames this way. But it is
still quite an overhead and just as with OpenJDK right now, it does not work when types are missing what is unfortunately rather common, for example if Spring is involved. In contrast, Byte Buddy's frame weaving is always single-pass and does not require any
allocation (per frame), and works with missing types. (Currently, OpenJDK produces a verification error
<a href="https://urldefense.com/v3/__https:/github.com/raphw/asm-jdk-bridge/blob/writer-poc/src/test/java/codes/rafael/asmjdkbridge/sample/FrameWithMissingType.java__;!!ACWV5N9M2RV99hQ!P7dgFjGSUa8LRpucquOmAlRGEEtLaI1HT7magyDRppDtnF_JI6VY4NOKjQ17zq3TH-dib0AbJPVElA5JA6Bv$">
https://github.com/raphw/asm-jdk-bridge/blob/writer-poc/src/test/java/codes/rafael/asmjdkbridge/sample/FrameWithMissingType.java</a>). This is why I would prefer to plug this logic into OpenJDK's class writer, if possible. Of course, this logic is based on
some assumptions, but if you are not trying to cover the general case, one can always be more efficient than a generic processor, just for that it would be a helpful addition.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt">Could you be, please, more specific about “</span><span style="font-size:11.0pt">OpenJDK produces a verification error”</span><span style="font-size:11.0pt">. I don’t see any code using Classfile API in your
sample.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt">Classfile API is passing unchanged stack maps of unchanged methods through transformations (in default shared constant pool mode).
<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt">What I agree with is API extension to allow manually pass stack maps also through builder. However
</span><span style="font-size:11.0pt">I’m not sure what else do you mean by “</span><span style="font-size:11.0pt">frame weaving”.</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt"><o:p> </o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt">As for the generator API: The current StackMapFrame objects contain information that is not present in the class file. For example, the attribute offers an "initialFrame", and the
frames themselves contain effective stack and effective locals. For crop frames, those currently contain the types of the cropped frames. For a writer API, OpenJDK's API would hopefully only consume the data as it is written to the class file. That would be
(a) the type of frame and (b) the declared values for stack and locals, or the amount of cropped frames. Of course, one could link all frames within a
</span><span lang="EN-US" style="font-size:11.0pt">StackMapTableAttribute together and compute this information on the fly. For example by providing some builder:</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt"><o:p> </o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span lang="EN-US" style="font-size:11.0pt">StackMapFrameAttribute b = StackMapFrameAttribute.builder(MethodDesc)</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span lang="EN-US" style="font-size:11.0pt"> .append(...)</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span lang="EN-US" style="font-size:11.0pt"> .crop(...)</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span lang="EN-US" style="font-size:11.0pt"> .same(...)</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span lang="EN-US" style="font-size:11.0pt"> .same1(...)</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span lang="EN-US" style="font-size:11.0pt"> .full(...)</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span lang="EN-US" style="font-size:11.0pt"> .build();</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt"><o:p> </o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt">One could then add this attribute or fail the CodeBuilder if "manual mode" was set without the attribute being present. I think this later option would be a decent API. If you would
consider it, I can offer to prototype such a solution.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt">What you are asking for is compressed form of stack map table. Initial stack map frame is a key information for anyone working with stack maps. Having only relative offsets and differentially compressed subsequent
frames is useless information without the initial frame. According to my experience only effective full stack map frames can be transformed or any other way processed. Information about compressed form of every frame is just a secondary. Every stack map table
can be compressed into many equivalent forms.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt">I agree with an option for user to specificy labeled full stack map frames.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt">However frames compression is something user should not be responsible of, it is similar as to request of manual deflation when writing a zip file. <o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt">Thanks,<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt">Adam<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt"><o:p> </o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt">Best regards, Rafael<o:p></o:p></span></p>
</div>
<div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt"><o:p> </o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt"><o:p> </o:p></span></p>
</div>
</div>
</div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt"><o:p> </o:p></span></p>
<div>
<div>
<p class="MsoNormal" style="margin-left:36.0pt"><span style="font-size:11.0pt">Am Di., 12. Juli 2022 um 14:20 Uhr schrieb Adam Sotona <<a href="mailto:adam.sotona@oracle.com">adam.sotona@oracle.com</a>>:<o:p></o:p></span></p>
</div>
<blockquote style="border:none;border-left:solid #CCCCCC 1.0pt;padding:0cm 0cm 0cm 6.0pt;margin-left:4.8pt;margin-right:0cm">
<div>
<div>
<p class="MsoNormal" style="mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:36.0pt">
<span style="font-size:11.0pt"> <o:p></o:p></span></p>
<p class="MsoNormal" style="mso-margin-top-alt:auto;mso-margin-bottom-alt:auto;margin-left:36.0pt">
<span style="font-size:11.0pt"> <o:p></o:p></span></p>
<div style="border:none;border-top:solid windowtext 1.0pt;padding:3.0pt 0cm 0cm 0cm;border-color:currentcolor currentcolor">
<p class="MsoNormal" style="mso-margin-top-alt:auto;margin-bottom:12.0pt;margin-left:36.0pt">
<b><span style="font-size:12.0pt;color:black">From: </span></b><span style="font-size:12.0pt;color:black">classfile-api-dev <<a href="mailto:classfile-api-dev-retn@openjdk.org" target="_blank">classfile-api-dev-retn@openjdk.org</a>> on behalf of Brian Goetz
<<a href="mailto:brian.goetz@oracle.com" target="_blank">brian.goetz@oracle.com</a>><br>
<b>Date: </b>Sunday, 10 July 2022 19:40<br>
<b>To: </b>Rafael Winterhalter <<a href="mailto:rafael.wth@gmail.com" target="_blank">rafael.wth@gmail.com</a>><br>
<b>Cc: </b><a href="mailto:classfile-api-dev@openjdk.org" target="_blank">classfile-api-dev@openjdk.org</a> <<a href="mailto:classfile-api-dev@openjdk.org" target="_blank">classfile-api-dev@openjdk.org</a>><br>
<b>Subject: </b>Re: POC: JDK ClassModel -> ASM ClassReader</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="mso-margin-top-alt:auto;margin-bottom:12.0pt;margin-left:72.0pt">
<span style="font-size:11.0pt"><br>
<br>
> StackMapFrames could on the other hand just be added at their position in the CodeElement iteration to receive them where they become relevant. This way, one would not need to keep track of the current offset. This would also allow for an easier write model
where ASM does not allow you to know the offset of a stack map. I assume that the current model is very much modeled after the needs of the javap tool. Ideally, the frame objects would be reduced to the information that is contained in a class file and the
consumer could track implicit information such as the "effective" stack and locals.<br>
<br>
There are two concerns with this, one minor and one major. The minor one is that this has a significant cost, and most users don’t want this information. So we would surely want to gate this with an option whose default is false. (We care the most about
transformation, and most transformations make only light changes, so we don’t want to add costs that most users won’t want to bear.).
<br>
<br>
The major one is how it perturbs the model. The element stream delivered by a model, and the one consumed by a builder, should be duals. What should a builder do when handed a frame? Switch responsibility over to the user for correct frame generation? Ignore
it and regenerate stack maps anyway? <br>
<br>
I think we need a better picture of “who is the audience for this sub feature” before designing the API.
<o:p></o:p></span></p>
<p class="MsoNormal" style="mso-margin-top-alt:auto;margin-bottom:12.0pt;margin-left:36.0pt">
<span lang="EN-US" style="font-size:11.0pt">I’ve been considering (and re-considering) many various scenarios related to stack maps.</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
<p class="MsoNormal" style="mso-margin-top-alt:auto;margin-bottom:12.0pt;margin-left:36.0pt">
<span lang="EN-US" style="font-size:11.0pt">Number one requirement is to generate valid stack maps in any circumstances and with minimal performance penalty. And we already do a lot in this area:</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
<p class="gmail-m7404434680222909313msolistparagraph" style="mso-margin-top-alt:5.0pt;margin-right:0cm;margin-bottom:12.0pt;margin-left:72.0pt;text-indent:-18.0pt;mso-list:l3 level1 lfo1">
<![if !supportLists]><span style="font-size:10.0pt;font-family:Symbol"><span style="mso-list:Ignore">·<span style="font:7.0pt "Times New Roman"">
</span></span></span><![endif]><span lang="EN-US">Stack maps generation requires minimal information about involved classes and only to resolve controversial situations (for example when looking for common parent of dual assignment to a single local variable).
Required information is minimal and limited to individual classes (is this specific class an interface or what is its parent class). On the other side for example ASM requires to load the classes with all dependencies to generate stack maps.</span><o:p></o:p></p>
<p class="gmail-m7404434680222909313msolistparagraph" style="mso-margin-top-alt:5.0pt;margin-right:0cm;margin-bottom:12.0pt;margin-left:72.0pt;text-indent:-18.0pt;mso-list:l3 level1 lfo1">
<![if !supportLists]><span style="font-size:10.0pt;font-family:Symbol"><span style="mso-list:Ignore">·<span style="font:7.0pt "Times New Roman"">
</span></span></span><![endif]><span lang="EN-US">Generation process is fast and does not produce any temporary structures and objects. It is single-pass in >95% cases, dual-pass in >4% cases and maximum three-pass in the remaining ~1% of cases (statistics
calculated from very large corpus of classes).</span><o:p></o:p></p>
<p class="MsoNormal" style="mso-margin-top-alt:auto;margin-bottom:12.0pt;margin-left:36.0pt">
<span lang="EN-US" style="font-size:11.0pt">Experiments to involve transformed original stack maps led to increased complexity, worse performance, and mainly failed to produce valid stack maps. There is no benefit of passing user-created (or somehow transformed)
stack map frames to the generator for final processing.</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
<p class="MsoNormal" style="mso-margin-top-alt:auto;margin-bottom:12.0pt;margin-left:36.0pt">
<span lang="EN-US" style="font-size:11.0pt">From the discussion (and from my experience with class instrumentation) I see one use case we didn’t cover and one case where we can improve:</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
<p class="gmail-m7404434680222909313msolistparagraph" style="mso-margin-top-alt:5.0pt;margin-right:0cm;margin-bottom:12.0pt;margin-left:72.0pt;text-indent:-18.0pt;mso-list:l1 level1 lfo2">
<![if !supportLists]><span style="mso-list:Ignore">1.<span style="font:7.0pt "Times New Roman"">
</span></span><![endif]><span lang="EN-US">We handle well class transformation from single source. Shared constant pool allows to keep original stack maps for all methods with unmodified bytecode. However, class instrumentation is a transformation with at least
two sources. Such transformation can share only one constant pool. All methods from the second source must be exploded to instructions, reconstructed and stack maps generated from scratch. Author of such transformation must be fully aware of the consequences
and having an option to pass stack maps through non-shared CP transformation would be a valuable feature. It would require:</span><o:p></o:p></p>
<p class="gmail-m7404434680222909313msolistparagraph" style="mso-margin-top-alt:5.0pt;margin-right:0cm;margin-bottom:12.0pt;margin-left:108.0pt;text-indent:-18.0pt;mso-list:l2 level2 lfo3">
<![if !supportLists]><span style="mso-list:Ignore">a.<span style="font:7.0pt "Times New Roman"">
</span></span><![endif]><span lang="EN-US">Option to individually turn off stack map generation per method (because there might be also synthetic methods where sm generation is required). I would propose to implement Code-level override of global options.
</span><o:p></o:p></p>
<p class="gmail-m7404434680222909313msolistparagraph" style="mso-margin-top-alt:5.0pt;margin-right:0cm;margin-bottom:12.0pt;margin-left:108.0pt;text-indent:-18.0pt;mso-list:l2 level2 lfo3">
<![if !supportLists]><span style="mso-list:Ignore">b.<span style="font:7.0pt "Times New Roman"">
</span></span><![endif]><span lang="EN-US">Factory for stack map table manual construction (based on labels, not offsets). I would propose to put this “manual mode” aside from CodeBuilder and implement it as StackMapTableAttribute.of(Frame…) factory.</span><o:p></o:p></p>
<p class="gmail-m7404434680222909313msolistparagraph" style="mso-margin-top-alt:5.0pt;margin-right:0cm;margin-bottom:12.0pt;margin-left:72.0pt;text-indent:-18.0pt;mso-list:l0 level1 lfo4">
<![if !supportLists]><span style="mso-list:Ignore">2.<span style="font:7.0pt "Times New Roman"">
</span></span><![endif]><span lang="EN-US">There are cases where required class hierarchy is not available (when there is no access to all jars), so it would be hard for user to provide appropriate ClassHierarchyResolver. However, many individual class information
can be theoretically extracted from the source classes (from class headers, from existing stack maps, from other attributes or from the bytecode itself). It is just an idea; however I think it might be possible to implement an optional hierarchy resolver,
that will learn from the parsed classes. It is a theoretical option for improvement, without throwing that responsibility on user. However, any such solution would remain in category of non-deterministic. Altering a stack map without minimal knowledge of the
involved classes is still a blind surgery.</span><o:p></o:p></p>
<p class="MsoNormal" style="mso-margin-top-alt:auto;margin-bottom:12.0pt;margin-left:72.0pt">
<span style="font-size:11.0pt"> <o:p></o:p></span></p>
<p class="MsoNormal" style="mso-margin-top-alt:auto;margin-bottom:12.0pt;margin-left:36.0pt">
<span lang="EN-US" style="font-size:11.0pt">Thanks,</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
<p class="MsoNormal" style="mso-margin-top-alt:auto;margin-bottom:12.0pt;margin-left:36.0pt">
<span lang="EN-US" style="font-size:11.0pt">Adam</span><span style="font-size:11.0pt"><o:p></o:p></span></p>
<p class="MsoNormal" style="mso-margin-top-alt:auto;margin-bottom:12.0pt;margin-left:72.0pt">
<span style="font-size:11.0pt"><o:p> </o:p></span></p>
</div>
</div>
</div>
</blockquote>
</div>
</div>
</body>
</html>