PebbleTemplates / pebble

Java Template Engine
https://pebbletemplates.io
BSD 3-Clause "New" or "Revised" License
1.09k stars 166 forks source link

Prefer public methods over made-up ones that happen to exist and be private #661

Open grishka opened 11 months ago

grishka commented 11 months ago

If a class has a public method something() and a private method hasSomething(), Pebble would first try to call the latter one and that would cause an exception to be thrown if the class is part of JDK. I just ran into this behavior once again. Template:

{{ to.iterator().next() }}

Exception:

java.lang.reflect.InaccessibleObjectException: Unable to make public boolean java.util.ImmutableCollections$Set12$1.hasNext() accessible: module java.base does not "opens java.util" to unnamed module @4e04a765
    at java.base/java.lang.reflect.AccessibleObject.throwInaccessibleObjectException(AccessibleObject.java:391)
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:367)
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:315)
    at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:203)
    at java.base/java.lang.reflect.Method.setAccessible(Method.java:197)
    at io.pebbletemplates.pebble.attributes.MemberCacheUtils.reflect(MemberCacheUtils.java:103)
    at io.pebbletemplates.pebble.attributes.MemberCacheUtils.cacheMember(MemberCacheUtils.java:32)
    at io.pebbletemplates.pebble.attributes.DefaultAttributeResolver.resolve(DefaultAttributeResolver.java:64)
    at io.pebbletemplates.pebble.node.expression.GetAttributeExpression.evaluate(GetAttributeExpression.java:83)
    at io.pebbletemplates.pebble.node.expression.FilterExpression.evaluate(FilterExpression.java:66)
    at io.pebbletemplates.pebble.node.PrintNode.render(PrintNode.java:37)
    at io.pebbletemplates.pebble.node.BodyNode.render(BodyNode.java:44)
    at io.pebbletemplates.pebble.node.RootNode.render(RootNode.java:31)
    at io.pebbletemplates.pebble.template.PebbleTemplateImpl.evaluate(PebbleTemplateImpl.java:157)
    at io.pebbletemplates.pebble.template.PebbleTemplateImpl.evaluate(PebbleTemplateImpl.java:105)
    at smithereen.templates.RenderedTemplateResponse.renderToWriter(RenderedTemplateResponse.java:103)
    at smithereen.SmithereenApplication.lambda$main$45(SmithereenApplication.java:614)
...

I never intended to call hasNext(), I wanted to call literally what I wrote but Pebble won't let me.

The problem is here: https://github.com/PebbleTemplates/pebble/blob/master/pebble/src/main/java/io/pebbletemplates/pebble/attributes/MemberCacheUtils.java#L68-L100 It first checks all the made-up methods, and only then, if none of them exist (regardless of the access), looks for the one I intended. That could lead to all kinds of hard-to-debug bugs. IMO "check if attribute is a public method" should come first.