Define classes with circular dependency?
John Hendrikx
john at int4.org
Sat Mar 15 10:31:24 UTC 2025
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.
--John
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 <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:
>
> publicclassSample {
>
> privatefinalProperty b= newProperty(A);
>
> privatestaticfinalCssMetaData A= newCssMetaData() {
>
> @Override
>
> publicProperty getProperty(Object obj) {
>
> return((Sample)obj).b;
>
> }
>
> };
>
> }
>
> abstractclassCssMetaData {
>
> abstractProperty getProperty(Object obj);
>
> }
>
> classProperty {
>
> publicProperty(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/3047e4ec/attachment.htm>
More information about the classfile-api-dev
mailing list