konsoletyper / teavm

Compiles Java bytecode to JavaScript, WebAssembly and C
https://teavm.org
Apache License 2.0
2.62k stars 261 forks source link

MainClass clinit does not initailize other classes in correct order #897

Open taylortails opened 6 months ago

taylortails commented 6 months ago

version 0.10.0-dev-10

When I run my generated javascript I get this error:

TypeError: Cannot read property '$clone0' of null
    at <js> jus_Collector$Characteristics_values(target\generated\js\teavm\model.mjs:22749:1000149-1000193)
    at <js> otp_Platform_getEnumConstants(target\generated\js\teavm\model.mjs:3021:177633-177640)
    at <js> otp_Platform_getEnumConstants(target\generated\js\teavm\model.mjs:3025:177694-177729)
    at <js> ju_GenericEnumSet_getConstants(target\generated\js\teavm\model.mjs:24674:1070248-1070292)
    at <js> jus_Collector_of0(target\generated\js\teavm\model.mjs:22777:1001520-1001589)
    at <js> jus_Collector_of(target\generated\js\teavm\model.mjs:22771:1001121-1001225)
    at <js> jus_Collectors_toCollection(target\generated\js\teavm\model.mjs:21341:938512-938688)
    at <js> jus_Collectors_toSet(target\generated\js\teavm\model.mjs:21347:938850-938914)
    at <js> chdtlmu_ModelBuilder_getImpls(target\generated\js\teavm\model.mjs:6890:367818-367839)
    at <js> chdtlmi_TypedReaderImpl__clinit_(target\generated\js\teavm\model.mjs:48486:2033785-2033815)
    at org.graalvm.polyglot.Context.eval(Context.java:402)

This is happing because my class' clinit is called before Characteristics__clinit is called

chdtlmj_Entrypoint__clinit_ = () => {
...
    chdtlmi_TypedReaderImpl__clinit_();
...
    jus_Collector$Characteristics__clinit_();

I can workaround by manually reording code in the generated javascript, or re-write my java code to use a code path that doesnt require this code to be initialized.

Regards.

konsoletyper commented 6 months ago

Can you provide any details on your code? I'm not interested in JVM version or operating system. What I'm interested in is the Java code you are trying to convert to JavaScript. It would be best if you could write a self-contained example that reproduces the issue.

As for workaround, try placing something like Class.forName("java.util.stream.Collector$Characteristics") in Entrypoint's static initializer.

konsoletyper commented 6 months ago

I tried with trivial example like this:

static {
    System.out.println(Stream.of(1, 2, 3).collect(Collectors.toSet()));
}

And what I get in entry point's <clinit> method is following:

    jl_String__clinit_();
    jl_Integer__clinit_();
    jl_Character__clinit_();
    jnci_UTF8Charset__clinit_();
    jus_Collector$Characteristics__clinit_();
},

You can notice that calling initializer does not present in this list. This is expected, since it calls native method Platform.getEnumConstants, for which TeaVM can't establish any relation, so this initializer is marked as 'dynamic', which means that it won't be statically initialized in entry point, but rather be dynamically initialized on first use. Why this does not happen in your case, I don't know since have not idea what do other methods (like ModelBuilder.getImpl and perhaps others) do.

taylortails commented 5 months ago

thanks for spending the time trying to reproduce, yes - I had trouble trying to produce a simple example that reproduces the issue.. I'll try again soon, my best guess currently is that if you export multiple methods they'd each have different initialization order - hence combining them needs to be done carefully. If this is true it would make sense because exporting multiple methods is new.

konsoletyper commented 5 months ago

If it's possible, you can post your original project. In case it's closed-source, you can reach me directly in Gitter and we'll discuss terms.

my best guess currently is that if you export multiple methods they'd each have different initialization order - hence combining them needs to be done carefully

Your guess is wrong. The order of initializers does not depend on 'main' method or other entry points, but rather from relation of static initializers.