jte: `Failed to init template entry.kte, no method named 'render' found in class gg.jte.generated.precompiled.JteentryGenerated` when argument is Kotlin value class

Hello,

I use jte 2.1.1 inside my ktor application with Gradle. Gradle setup is following:

jte {
    generate()
    sourceDirectory.set(Paths.get("templates"))
    contentType.set(gg.jte.ContentType.Html)
    binaryStaticContent.set(true)
    trimControlStructures.set(true)
}

Most part of templates work fine, but for one of them, namely entry.kte seemingly there is no “render” method. At least during debug I can’t find it too. Here’s the entry.kte content:

@import io.github.asm0dey.plugins.*
@import java.time.*

@param content: String
@param summary: String
@param book: BookWithInfo
@param imageType: String
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:dcterms="http://purl.org/dc/terms/">
    <title>${book.book.name}</title>
    <id>/opds/book/${book.id}/info</id>
    @for(author in book.authors)
    <author>
        <name>${author.buildName()}</name>
        <uri>/opds/author/browse/${author.id}</uri>
    </author>
    @endfor
    <published>${formatDate(ZonedDateTime.now())}</published>
    <updated>${formatDate(book.book.added)}</updated>
    @if(book.book.lang != null)
    <dcterms:language>${book.book.lang}</dcterms:language>
    @endif
    @if(book.book.date != null)
    <dcterms:date>${book.book.date}</dcterms:date>
    @endif
    @for(genre in book.genres)
    <category term="${genre}" label="${genre}"/>
    @endfor
    @if(imageType != null)
    <link type="${imageType}" rel="http://opds-spec.org/image" href="/opds/image/${book.id}"/>
    @endif
    <link type="application/fb2+zip" rel="http://opds-spec.org/acquisition/open-access" href="/opds/book/${book.id}/download"/>
    @if(summary != null)
    <summary type="text">${summary}</summary>
    <content type="html">$unsafe{content}</content>
    @endif
</entry>

Looking at javap output I can see following:

❯ javap ./build/classes/kotlin/main/gg/jte/generated/precompiled/JteentryGenerated.class | grep render
  public static final void render-JMhnnco(gg.jte.html.HtmlTemplateOutput, gg.jte.html.HtmlInterceptor, java.lang.String, java.lang.String, org.jooq.Record4<java.lang.Long, java.util.List<? extends io.github.asm0dey.opdsko.jooq.tables.pojos.Book>, java.util.List<? extends io.github.asm0dey.opdsko.jooq.tables.pojos.Author>, java.util.List<? extends java.lang.String>>, java.lang.String);
  public static final void renderMap(gg.jte.html.HtmlTemplateOutput, gg.jte.html.HtmlInterceptor, java.util.Map<java.lang.String, ? extends java.lang.Object>);

Further investigation:

BookWithInfo is a value class:

@JvmInline
value class BookWithInfo(private val record: Record4<Long, List<BookPojo>, List<Author>, List<String>>) {
    val book get() = record.component2()[0]
    val authors get() = record.component3()!!
    val genres get() = record.component4().map { genreNames[it] ?: it }
    val id get() = record.get(BOOK.ID)!!
}

Maybe this is the reason why we can see a bridge-like render-JMhnnco method in the JteentryGenerated.class?

However, for other template, where BookWithInfo participates too, but in a List, not by itself, everything is being generated fine:

❯ javap ./build/classes/kotlin/main/gg/jte/generated/precompiled/JtebooksGenerated.class | grep render
  public static final void render(gg.jte.html.HtmlTemplateOutput, gg.jte.html.HtmlInterceptor, java.util.List<io.github.asm0dey.plugins.BookWithInfo>, java.util.Map<java.lang.Long, java.lang.String>, java.util.Map<java.lang.Long, java.lang.String>, java.lang.String, java.lang.String, java.lang.String, java.time.temporal.TemporalAccessor, java.util.List<io.github.asm0dey.model.Entry$Link>);
  public static final void renderMap(gg.jte.html.HtmlTemplateOutput, gg.jte.html.HtmlInterceptor, java.util.Map<java.lang.String, ? extends java.lang.Object>);

Maybe this is an error on Kotlin compiler’s side, but it appears to me that template detection method should be just a bit more sophisticated and be aware that Kotlin value classes exist as well as such bridge methods.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 17 (8 by maintainers)

Commits related to this issue

Most upvoted comments

Me too! Thank you for help with investigation and effort to fix!

вс, 3 июл. 2022 г., 12:44 Andreas Hager @.***>:

Seemingly it works! You can find the build here: https://jitpack.io/#casid/jte/fa73ea1a9d (I hate mvn install when I have other options 😃

Oh, that’s really convenient, thanks for sharing this trick!

Thank you for testing & confirming the fix. I’m glad we got it working 😃

— Reply to this email directly, view it on GitHub https://github.com/casid/jte/issues/163#issuecomment-1173048337, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAJ4XATOFHRDCKKSGLMIMPTVSFOHZANCNFSM5Z3RP3JQ . You are receiving this because you were mentioned.Message ID: @.***>

Generated bytecode includes

  public static final void render-JMhnnco(gg.jte.html.HtmlTemplateOutput, gg.jte.html.HtmlInterceptor, java.lang.String, java.lang.String, org.jooq.Record4<java.lang.Long, java.util.List<? extends io.github.asm0dey.opdsko.jooq.tables.pojos.Book>, java.util.List<? extends io.github.asm0dey.opdsko.jooq.tables.pojos.Author>, java.util.List<? extends java.lang.String>>, java.lang.String);

Seemingly it works! You can find the build here: https://jitpack.io/#casid/jte/fa73ea1a9d (I hate mvn install when I have other options 😃

Impressive message!