<html><body><div style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000"><div><br></div><div><br></div><hr id="zwchr" data-marker="__DIVIDER__"><div data-marker="__HEADERS__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><b>From: </b>"John Hendrikx" <john@int4.org><br><b>To: </b>"Michael van Acken" <michael.van.acken@gmail.com><br><b>Cc: </b>"classfile-api-dev" <classfile-api-dev@openjdk.org><br><b>Sent: </b>Saturday, March 15, 2025 11:31:24 AM<br><b>Subject: </b>Re: Define classes with circular dependency?<br></blockquote></div><div data-marker="__QUOTED_TEXT__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><p>Sorry,<br>
      <br>
      It seems I'm just not well enough versed in byte code generation
      and how the class verifier works.  <br>
      <br>
      The verifier was fine with a `getProperty(outer)` definition with
      the code within it referring to the outer type.  But the abstract
      class doesn't define that method, it defines
      `getProperty(Styleable)` so this method wouldn't actually
      implement the abstract method.  If the only change I made was the
      signature, then the verifier would reject it, as the method body
      refers to outer.  So I had this:<br>
    </p>
    <div style="background-color:#ffffff;padding:0px 0px 0px 2px;">
      <div style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:11pt;white-space:pre;"><p style="margin:0;"><span style="color:#000000;">            cb.withMethodBody(</span><span style="color:#2a00ff;">"getStyleableProperty"</span><span style="color:#000000;">, MethodTypeDesc.</span><span style="color:#000000;font-style:italic;">of</span><span style="color:#000000;">(ClassDesc.</span><span style="color:#000000;font-style:italic;">of</span><span style="color:#000000;">(</span><span style="color:#2a00ff;">"javafx.css.StyleableProperty"</span><span style="color:#000000;">), outer</span><span style="color:#000000;">), ClassFile.</span><span style="color:#0000c0;">ACC_PUBLIC</span><span style="color:#000000;">, mb -> {</span></p><p style="margin:0;"><span style="color:#000000;">              mb.aload(1)</span></p><p style="margin:0;"><span style="color:#000000;">                .getfield(</span><span style="color:#000000;background-color:#d4d4d4;">outer</span><span style="color:#000000;">, cssData.</span><span style="color:#0000c0;">fieldName</span><span style="color:#000000;">, ClassDesc.</span><span style="color:#000000;font-style:italic;">of</span><span style="color:#000000;">(</span><span style="color:#2a00ff;">"javafx.css.CssMetaData"</span><span style="color:#000000;">))</span></p><p style="margin:0;"><span style="color:#000000;">                .checkcast(ClassDesc.</span><span style="color:#000000;font-style:italic;">of</span><span style="color:#000000;">(</span><span style="color:#2a00ff;">"javafx.css.StyleableProperty"</span><span style="color:#000000;">))</span></p><p style="margin:0;"><span style="color:#000000;">                .areturn();</span></p><p style="margin:0;"><span style="color:#000000;">            });</span></p></div>
    </div>
    <p>The above works, but doesn't implement the abstract method as it
      has a different signature.  I found this very confusing as I'm
      hard referencing an outer ClassDesc here that doesn't exist yet,
      but its fine with it, yet with the signature modified to correctly
      implement the abstract method it rejects it suddenly:</p>
    <div style="background-color:#ffffff;padding:0px 0px 0px 2px;">
      <div style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:11pt;white-space:pre;"><p style="margin:0;"><span style="color:#000000;">            cb.withMethodBody(</span><span style="color:#2a00ff;">"getStyleableProperty"</span><span style="color:#000000;">, MethodTypeDesc.</span><span style="color:#000000;font-style:italic;">of</span><span style="color:#000000;">(ClassDesc.</span><span style="color:#000000;font-style:italic;">of</span><span style="color:#000000;">(</span><span style="color:#2a00ff;">"javafx.css.StyleableProperty"</span><span style="color:#000000;">), ClassDesc.</span><span style="color:#000000;font-style:italic;">of</span><span style="color:#000000;">(</span><span style="color:#2a00ff;">"javafx.css.Styleable"</span><span style="color:#000000;">)), ClassFile.</span><span style="color:#0000c0;">ACC_PUBLIC</span><span style="color:#000000;">, mb -> {</span></p><p style="margin:0;"><span style="color:#000000;">              mb.aload(1)</span></p><p style="margin:0;"><span style="color:#000000;">                .getfield(</span><span style="color:#000000;background-color:#d4d4d4;">outer</span><span style="color:#000000;">, cssData.</span><span style="color:#0000c0;">fieldName</span><span style="color:#000000;">, ClassDesc.</span><span style="color:#000000;font-style:italic;">of</span><span style="color:#000000;">(</span><span style="color:#2a00ff;">"javafx.css.CssMetaData"</span><span style="color:#000000;">))</span></p><p style="margin:0;"><span style="color:#000000;">                .checkcast(ClassDesc.</span><span style="color:#000000;font-style:italic;">of</span><span style="color:#000000;">(</span><span style="color:#2a00ff;">"javafx.css.StyleableProperty"</span><span style="color:#000000;">))</span></p><p style="margin:0;"><span style="color:#000000;">                .areturn();</span></p><p style="margin:0;"><span style="color:#000000;">            });</span></p></div>
    </div>
    
    <p>The above gets rejected with a NoClassDefFoundError for the outer
      type.  The solution was to insert a checkcast(outer):<br>
    </p>
    <div style="background-color:#ffffff;padding:0px 0px 0px 2px;">
      <div style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:11pt;white-space:pre;"><p style="margin:0;"><span style="color: rgb(0, 0, 0);">            cb.withMethodBody(</span><span style="color: rgb(42, 0, 255);">"getStyleableProperty"</span><span style="color: rgb(0, 0, 0);">, MethodTypeDesc.</span><span style="color: rgb(0, 0, 0); font-style: italic;">of</span><span style="color: rgb(0, 0, 0);">(ClassDesc.</span><span style="color: rgb(0, 0, 0); font-style: italic;">of</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(42, 0, 255);">"javafx.css.StyleableProperty"</span><span style="color: rgb(0, 0, 0);">), ClassDesc.</span><span style="color: rgb(0, 0, 0); font-style: italic;">of</span><span style="color: rgb(0, 0, 0);">(</span><span style="color: rgb(42, 0, 255);">"javafx.css.Styleable"</span><span style="color: rgb(0, 0, 0);">)), ClassFile.</span><span style="color: rgb(0, 0, 192);">ACC_PUBLIC</span><span style="color: rgb(0, 0, 0);">, mb -> {</span></p><p style="margin:0;"><span style="color:#000000;">              mb.aload(1)</span></p><p style="margin:0;"><span style="color:#000000;">                .checkcast(</span><span style="color:#000000;background-color:#d4d4d4;">outer</span><span style="color:#000000;">)</span></p><p style="margin:0;"><span style="color:#000000;">                .getfield(</span><span style="color:#000000;background-color:#d4d4d4;">outer</span><span style="color:#000000;">, cssData.</span><span style="color:#0000c0;">fieldName</span><span style="color:#000000;">, ClassDesc.</span><span style="color:#000000;font-style:italic;">of</span><span style="color:#000000;">(</span><span style="color:#2a00ff;">"javafx.css.CssMetaData"</span><span style="color:#000000;">))</span></p><p style="margin:0;"><span style="color:#000000;">                .checkcast(ClassDesc.</span><span style="color:#000000;font-style:italic;">of</span><span style="color:#000000;">(</span><span style="color:#2a00ff;">"javafx.css.StyleableProperty"</span><span style="color:#000000;">))</span></p><p style="margin:0;"><span style="color:#000000;">                .areturn();</span></p><p style="margin:0;"><span style="color:#000000;">            });</span></p></div>
    </div>
    <p>I'm very happy this works.  At first I thought it was just
      something that only javac in combination with class loaders was
      allowed to do (a circular class reference) and it couldn't be done
      with Lookup::defineClass -- it turns out the actual reason seems
      to be that the reference isn't actually circular during
      loading, but simply attempted at runtime.  </p></blockquote><div><br></div><div>yes,</div><div>from the verifier POV checkcast only checks that the top of the stack is an object,</div><div>the actual checkcast is done at runtime.</div><div><br data-mce-bogus="1"></div><div>see https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.10.1.9.checkcast</div><div><br data-mce-bogus="1"></div><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><p><br>
      <br>
      --John</p></blockquote><div><br></div><div>Rémi</div><div><br data-mce-bogus="1"></div><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><p><br>
    </p>
    <p><br>
    </p>
    <div class="moz-cite-prefix">On 15/03/2025 10:53, Michael van Acken
      wrote:<br>
    </div>
    <blockquote cite="mid:CAEYHehhcMfFmR6g2yiRVaM-JfHL-fTzHn=Q3ST_Dtt8rSdHPVA@mail.gmail.com">
      
      <div dir="ltr">As far as I know, at time of definition only the
        class being extended must be available.
        <div>From your example, this seems to be j.l.Object both times,
          so this should not be the problem.</div>
        <div><br>
        </div>
        <div>But your case triggers a vague recollection, where I had
          the same behaviour of </div>
        <div>Lookup.defineClass() in some gnarly unit test of my
          compiler -- with a NoCDFE that</div>
        <div>I was not able to explain.</div>
        <div><br>
        </div>
        <div>Out of curiosity: what happens when you catch the exception
          and do a findClass using</div>
        <div>the same lookup and the dotted class name of the class you
          just tried to define?  In</div>
        <div>my case, I got back the class instance in the catch clause,
          suggesting the defineClass</div>
        <div>completed after all.</div>
        <div><br>
        </div>
        <div>-- mva</div>
        <div><br>
        </div>
        <div><br>
        </div>
        <div><br>
        </div>
        <div><br>
        </div>
        <div><br>
        </div>
      </div>
      <br>
      <div class="gmail_quote gmail_quote_container">
        <div dir="ltr" class="gmail_attr">Am Sa., 15. März 2025 um
          10:19 Uhr schrieb John Hendrikx <<a href="mailto:john@int4.org" class="moz-txt-link-freetext" target="_blank">john@int4.org</a>>:<br>
        </div>
        <blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
          <div>
            <p>Hi list,<br>
              <br>
              I'm trying to use the ClassFile API to automatically
              implement control classes (as found in JavaFX). These
              classes define inner class CssMetaData implementations
              that refer back to the outer class, and the outer class
              refers to these implementations via static fields.  When I
              define one of the inner types using Lookup::defineClass I
              get a NoClassDefFoundError for the outer type.  When I
              define the outer type first, I get a NoClassDefFoundError
              for one of the inner types.  The situation is essentially
              this:</p>
            <div style="background-color:rgb(255,255,255);padding:0px 0px 0px 2px">
              <div style="color:rgb(0,0,0);background-color:rgb(255,255,255);font-family:Consolas;font-size:11pt;white-space:pre-wrap"><p style="margin:0px"><span style="color:rgb(0,0,160);font-weight:bold">public</span><span style="color:rgb(0,0,160);font-weight:bold">class</span><span style="color:rgb(0,0,0)"> Sample {</span></p><p style="margin:0px"><span style="color:rgb(0,0,160);font-weight:bold">private</span><span style="color:rgb(0,0,160);font-weight:bold">final</span><span style="color:rgb(0,0,0)"> Property </span><span style="color:rgb(0,0,192)">b</span><span style="color:rgb(0,0,0)"> = </span><span style="color:rgb(0,0,160);font-weight:bold">new</span><span style="color:rgb(0,0,0)"> Property(</span><span style="color:rgb(0,0,192)">A</span><span style="color:rgb(0,0,0)">);</span></p><p style="margin:0px"><span style="color:rgb(0,0,160);font-weight:bold">private</span><span style="color:rgb(0,0,160);font-weight:bold">static</span><span style="color:rgb(0,0,160);font-weight:bold">final</span><span style="color:rgb(0,0,0)"> CssMetaData </span><span style="color:rgb(0,0,192)">A</span><span style="color:rgb(0,0,0)"> = </span><span style="color:rgb(0,0,160);font-weight:bold">new</span><span style="color:rgb(0,0,0)"> CssMetaData() {</span></p><p style="margin:0px"><span style="color:rgb(100,100,100)">@Override</span></p><p style="margin:0px"><span style="color:rgb(0,0,160);font-weight:bold">public</span><span style="color:rgb(0,0,0)"> Property getProperty(Object obj) {</span></p><p style="margin:0px"><span style="color:rgb(127,0,85);font-weight:bold">return</span><span style="color:rgb(0,0,0)"> ((Sample)obj).</span><span style="color:rgb(0,0,192)">b</span><span style="color:rgb(0,0,0)">;</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)">        }</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)">      };</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)">    }</span></p><p style="margin:0px">
</p><p style="margin:0px"><span style="color:rgb(0,0,160);font-weight:bold">abstract</span><span style="color:rgb(0,0,160);font-weight:bold">class</span><span style="color:rgb(0,0,0)"> CssMetaData {</span></p><p style="margin:0px"><span style="color:rgb(0,0,160);font-weight:bold">abstract</span><span style="color:rgb(0,0,0)"> Property getProperty(Object obj);</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)">    }</span></p><p style="margin:0px"><span style="color:rgb(0,0,160);font-weight:bold">class</span><span style="color:rgb(0,0,0)"> Property {</span></p><p style="margin:0px"><span style="color:rgb(0,0,160);font-weight:bold">public</span><span style="color:rgb(0,0,0)"> Property(CssMetaData </span><span style="color:rgb(0,0,0);text-decoration:underline wavy rgb(244,200,45)">a</span><span style="color:rgb(0,0,0)">) {</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)">      }</span></p><p style="margin:0px"><span style="color:rgb(0,0,0)">    }</span></p></div>
            </div>
            <p>I'm trying to generate the Sample class.  The classes
              CssMetaData and Propery are pre-existing.  As you can see,
              Sample refers to A in a property it creates, while A
              refers to that property by direct field access after a
              cast.</p>
            <p>Note that the above is perfectly legal as a Java class,
              and I think the bytecode I generate is correct.  It seems
              I would need to be able to define both classes at the same
              time, but Lookup doesn't seem to have anything for this
              purpose.</p>
            <p>I'd appreciate any insights!</p>
            <p>--John<br>
            </p>
          </div>
        </blockquote>
      </div>
    </blockquote><br></blockquote></div></div></body></html>