Define classes with circular dependency?

Remi Forax forax at univ-mlv.fr
Sat Mar 15 11:20:21 UTC 2025


> From: "John Hendrikx" <john at int4.org>
> To: "Michael van Acken" <michael.van.acken at gmail.com>
> Cc: "classfile-api-dev" <classfile-api-dev at openjdk.org>
> Sent: Saturday, March 15, 2025 11:31:24 AM
> Subject: Re: Define classes with circular dependency?

> Sorry,

> It seems I'm just not well enough versed in byte code generation and how the
> class verifier works.

> 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:

> cb.withMethodBody( "getStyleableProperty" , MethodTypeDesc. of (ClassDesc. of (
> "javafx.css.StyleableProperty" ), outer ), ClassFile. ACC_PUBLIC , mb -> {

> mb.aload(1)

> .getfield( outer , cssData. fieldName , ClassDesc. of ( "javafx.css.CssMetaData"
> ))

> .checkcast(ClassDesc. of ( "javafx.css.StyleableProperty" ))

> .areturn();

> });

> 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:

> cb.withMethodBody( "getStyleableProperty" , MethodTypeDesc. of (ClassDesc. of (
> "javafx.css.StyleableProperty" ), ClassDesc. of ( "javafx.css.Styleable" )),
> ClassFile. ACC_PUBLIC , mb -> {

> mb.aload(1)

> .getfield( outer , cssData. fieldName , ClassDesc. of ( "javafx.css.CssMetaData"
> ))

> .checkcast(ClassDesc. of ( "javafx.css.StyleableProperty" ))

> .areturn();

> });

> The above gets rejected with a NoClassDefFoundError for the outer type. The
> solution was to insert a checkcast(outer):

> cb.withMethodBody( "getStyleableProperty" , MethodTypeDesc. of (ClassDesc. of (
> "javafx.css.StyleableProperty" ), ClassDesc. of ( "javafx.css.Styleable" )),
> ClassFile. ACC_PUBLIC , mb -> {

> mb.aload(1)

> .checkcast( outer )

> .getfield( outer , cssData. fieldName , ClassDesc. of ( "javafx.css.CssMetaData"
> ))

> .checkcast(ClassDesc. of ( "javafx.css.StyleableProperty" ))

> .areturn();

> });

> 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.
yes, 
from the verifier POV checkcast only checks that the top of the stack is an object, 
the actual checkcast is done at runtime. 

see https://docs.oracle.com/javase/specs/jvms/se23/html/jvms-4.html#jvms-4.10.1.9.checkcast 

> --John
Rémi 

> On 15/03/2025 10:53, Michael van Acken wrote:

>> As far as I know, at time of definition only the class being extended must be
>> available.
>> From your example, this seems to be j.l.Object both times, so this should not be
>> the problem.

>> But your case triggers a vague recollection, where I had the same behaviour of
>> Lookup.defineClass() in some gnarly unit test of my compiler -- with a NoCDFE
>> that
>> I was not able to explain.

>> Out of curiosity: what happens when you catch the exception and do a findClass
>> using
>> the same lookup and the dotted class name of the class you just tried to define?
>> In
>> my case, I got back the class instance in the catch clause, suggesting the
>> defineClass
>> completed after all.

>> -- mva

>> Am Sa., 15. März 2025 um 10:19 Uhr schrieb John Hendrikx < [
>> mailto:john at int4.org | john at int4.org ] >:

>>> Hi list,

>>> 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:

>>> public class Sample {

>>> private final Property b = new Property( A );

>>> private static final CssMetaData A = new CssMetaData() {

>>> @Override

>>> public Property getProperty(Object obj) {

>>> return ((Sample)obj). b ;

>>> }

>>> };

>>> }

>>> abstract class CssMetaData {

>>> abstract Property getProperty(Object obj);

>>> }

>>> class Property {

>>> public Property(CssMetaData a ) {

>>> }

>>> }

>>> 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.

>>> 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.

>>> I'd appreciate any insights!

>>> --John
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/classfile-api-dev/attachments/20250315/989915de/attachment-0001.htm>


More information about the classfile-api-dev mailing list