Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
import oap.template.runtime.RuntimeContext;

import javax.annotation.Nullable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@ToString( callSuper = true )
public class AstRenderBlockIf extends AstRender {
Expand All @@ -52,9 +54,11 @@ public void render( Render render ) {

conditionAst.render( render.withBooleanIfVar( condVar ) );

Set<String> thenNullSafe = collectNullRequired( conditionAst, render );

render.ntab().append( "if ( %s ) {", condVar );

Render thenBodyRender = render.tabInc().newBlock();
Render thenBodyRender = render.tabInc().newBlock().withNullVerified( thenNullSafe );
for( AstRender child : thenChildren ) {
child.render( thenBodyRender );
}
Expand All @@ -73,6 +77,19 @@ public void render( Render render ) {
}
}

private static Set<String> collectNullRequired( AstRender node, Render render ) {
Set<String> result = new HashSet<>();
if( node instanceof AstRenderField f
&& f.children.size() == 1
&& f.children.getFirst() instanceof AstRenderNullable ) {
result.add( render.peekVariableName( f.fieldName ) );
} else if( node instanceof AstRenderConditionAnd and ) {
result.addAll( collectNullRequired( and.left, render ) );
result.addAll( collectNullRequired( and.right, render ) );
}
return result;
}

@Override
public void interpret( RuntimeContext ctx ) {
boolean[] capture = { false };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@

@ToString( callSuper = true )
public class AstRenderConditionAnd extends AstRender {
private final AstRender left;
private final AstRender right;
final AstRender left;
final AstRender right;

public AstRenderConditionAnd( TemplateType type, AstRender left, AstRender right ) {
super( type );
Expand All @@ -40,6 +40,51 @@ public AstRenderConditionAnd( TemplateType type, AstRender left, AstRender right

@Override
public void render( Render render ) {
String sharedField = sharedTopLevelField();
if( sharedField != null )
renderMerged( render, sharedField );
else
renderDefault( render );
}

private String sharedTopLevelField() {
String lf = topLevelFieldName( left );
String rf = topLevelFieldName( right );
return lf != null && lf.equals( rf ) ? lf : null;
}

private static String topLevelFieldName( AstRender node ) {
if( !( node instanceof AstRenderField f ) ) return null;
if( f.children.size() != 1 ) return null;
if( !( f.children.getFirst() instanceof AstRenderNullable ) ) return null;
return f.fieldName;
}

private void renderMerged( Render render, String fieldName ) {
String leftVar = render.newVariable();
String rightVar = render.newVariable();
render.ntab().append( "boolean %s = false;", leftVar );
render.ntab().append( "boolean %s = false;", rightVar );

AstRenderField leftField = ( AstRenderField ) left;
AstRenderNullable leftNullable = ( AstRenderNullable ) leftField.children.getFirst();
AstRenderField rightField = ( AstRenderField ) right;
AstRenderNullable rightNullable = ( AstRenderNullable ) rightField.children.getFirst();

Render.NewVariable sv = render.newVariable( fieldName );
if( sv.isNew )
render.ntab().append( "%s %s = %s.%s;", leftField.type.getTypeName(), sv.name, render.field, fieldName );

render.ntab().append( "if ( %s != null ) {", sv.name );
Render inner = render.withField( sv.name ).withParentType( leftField.type ).tabInc();
leftNullable.renderBodyOnly( inner.withBooleanIfVar( leftVar ).newBlock() );
rightNullable.renderBodyOnly( inner.withBooleanIfVar( rightVar ).newBlock() );
render.ntab().append( "}" );

render.ntab().append( "%s = %s && %s;", render.booleanIfVar, leftVar, rightVar );
}

private void renderDefault( Render render ) {
String leftVar = render.newVariable();
String rightVar = render.newVariable();
render.ntab().append( "boolean %s = false;", leftVar );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ public AstRenderNullable( TemplateType type ) {
super( type );
}

@Override
public void render( Render render ) {
if( render.nullVerifiedVars.contains( render.field ) ) {
renderBodyOnly( render );
return;
}
super.render( render );
}

@Override
protected String getTrue() {
return " != null";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

@ToString
Expand All @@ -50,6 +51,7 @@ public class Render {
public final String rootField;
public final String prefix;
public final Map<String, String> rangeVarMap;
public final Set<String> nullVerifiedVars;
private final AtomicInteger ids;
private final StringBuilder sb;
private final ArrayDeque<HashSet<String>> variables;
Expand All @@ -61,13 +63,13 @@ private Render( String templateName, String content, TemplateType parentType, Te
{
this.addFirst( new HashSet<>() );
}
} );
}, Set.of() );
}

public Render( StringBuilder sb, String templateName, String content, TemplateType parentType, TemplateAccumulator<?, ?, ?> templateAccumulator,
String field, String templateAccumulatorName, int tab, AtomicInteger ids, String tryVariable,
String booleanIfVar, String scopeVar, String rootField, String prefix,
Map<String, String> rangeVarMap, ArrayDeque<HashSet<String>> variables ) {
Map<String, String> rangeVarMap, ArrayDeque<HashSet<String>> variables, Set<String> nullVerifiedVars ) {
this.sb = sb;
this.templateName = templateName;
this.content = content;
Expand All @@ -84,6 +86,7 @@ public Render( StringBuilder sb, String templateName, String content, TemplateTy
this.prefix = prefix;
this.rangeVarMap = rangeVarMap;
this.variables = variables;
this.nullVerifiedVars = nullVerifiedVars;
}

public static Render init( String templateName, String content, TemplateType type, TemplateAccumulator<?, ?, ?> acc ) {
Expand All @@ -92,32 +95,32 @@ public static Render init( String templateName, String content, TemplateType typ

public Render withField( String field ) {
return new Render( this.sb, this.templateName, this.content, this.parentType, this.templateAccumulator, field,
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, variableNameWithPrefix( field ), rangeVarMap, variables );
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, variableNameWithPrefix( field ), rangeVarMap, variables, nullVerifiedVars );
}

public Render withContent( String content ) {
return new Render( this.sb, this.templateName, content, this.parentType, this.templateAccumulator, field,
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables );
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables, nullVerifiedVars );
}

public Render withTemplateAccumulatorName( String templateAccumulatorName ) {
return new Render( this.sb, this.templateName, this.content, this.parentType, this.templateAccumulator, this.field,
templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables );
templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables, nullVerifiedVars );
}

public Render tabInc() {
return new Render( this.sb, this.templateName, this.content, this.parentType, this.templateAccumulator, this.field,
this.templateAccumulatorName, this.tab + 1, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables );
this.templateAccumulatorName, this.tab + 1, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables, nullVerifiedVars );
}

public Render tabDec() {
return new Render( this.sb, this.templateName, this.content, this.parentType, this.templateAccumulator, this.field,
this.templateAccumulatorName, this.tab - 1, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables );
this.templateAccumulatorName, this.tab - 1, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables, nullVerifiedVars );
}

public Render withParentType( TemplateType parentType ) {
return new Render( this.sb, this.templateName, this.content, parentType, this.templateAccumulator, this.field,
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables );
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables, nullVerifiedVars );
}

public Render n() {
Expand Down Expand Up @@ -195,34 +198,39 @@ public NewVariable newVariable( String name ) {

public Render withTryVariable( String tryVariable ) {
return new Render( this.sb, this.templateName, this.content, this.parentType, this.templateAccumulator, this.field,
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables );
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables, nullVerifiedVars );
}

public Render withBooleanIfVar( String booleanIfVar ) {
return new Render( this.sb, this.templateName, this.content, this.parentType, this.templateAccumulator, this.field,
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables );
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables, nullVerifiedVars );
}

public Render withScopeVar( String scopeVar ) {
return new Render( this.sb, this.templateName, this.content, this.parentType, this.templateAccumulator, this.field,
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables );
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables, nullVerifiedVars );
}

public Render withFieldDirect( String field ) {
return new Render( this.sb, this.templateName, this.content, this.parentType, this.templateAccumulator, field,
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, field, rangeVarMap, variables );
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, field, rangeVarMap, variables, nullVerifiedVars );
}

public Render withRootField( String rootField ) {
return new Render( this.sb, this.templateName, this.content, this.parentType, this.templateAccumulator, this.field,
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables );
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables, nullVerifiedVars );
}

public Render withRangeVar( String name, String javaName ) {
var newMap = new HashMap<>( rangeVarMap );
newMap.put( name, javaName );
return new Render( this.sb, this.templateName, this.content, this.parentType, this.templateAccumulator, this.field,
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, newMap, variables );
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, newMap, variables, nullVerifiedVars );
}

public Render withNullVerified( Set<String> vars ) {
return new Render( this.sb, this.templateName, this.content, this.parentType, this.templateAccumulator, this.field,
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, variables, vars );
}

public Render newBlock() {
Expand All @@ -233,7 +241,13 @@ public Render newBlock() {
newStack.addFirst( new HashSet<>() );

return new Render( this.sb, this.templateName, this.content, this.parentType, this.templateAccumulator, this.field,
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, newStack );
this.templateAccumulatorName, this.tab, ids, tryVariable, booleanIfVar, scopeVar, rootField, prefix, rangeVarMap, newStack, nullVerifiedVars );
}

@SuppressWarnings( "checkstyle:ParameterAssignment" )
public String peekVariableName( String name ) {
name = name.replaceAll( "[\\s,?\\-]", "_" );
return variableNameWithPrefix( name );
}

private String variableNameWithPrefix( String name ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package oap.template;

import oap.reflect.TypeRef;
import org.apache.commons.lang3.StringUtils;
import org.testng.annotations.Test;

import static oap.template.TemplateAccumulators.STRING;
Expand All @@ -17,6 +18,8 @@ public void testBlockWithMultipleFields() {
"{{% with child }}{{ field }}-{{ field2 }}{{% end }}", STRING, null ).render( c ).get() )
.isEqualTo( "f1-f2" );

assertThat( StringUtils.countMatches( listener.javaCode, "if " ) ).isEqualTo( 3 );

assertThat( listener.javaCode )
.containsOnlyOnce( """
oap.template.TestTemplateClass s_child = s.child;
Expand All @@ -42,6 +45,23 @@ public void testBlockWithMultipleFieldsAndComments() {
"{{% with child }}{{ /** c field */ field }}-{{ /** c field2 */field2 }}{{% end }}", STRING, null ).render( c ).get() )
.isEqualTo( "f1-f2" );

assertThat( StringUtils.countMatches( listener.javaCode, "if " ) ).isEqualTo( 3 );

assertThat( listener.javaCode ).containsOnlyOnce( "if ( s_child != null ) {" );
}

@Test
public void testBlockIfOptimization() {
TestTemplateClass c = new TestTemplateClass();
c.child = new TestTemplateClass();
c.child.field = "1";
c.child.field2 = "2";
assertThat( getTemplate( testMethodName + "Both", new TypeRef<TestTemplateClass>() {},
"{{% if child.field and child.field2 }}{{ child.field }}{{% else }}no{{% end }}", STRING, null ).render( c ).get() )
.isEqualTo( "1" );

assertThat( StringUtils.countMatches( listener.javaCode, "if " ) ).isEqualTo( 5 );

assertThat( listener.javaCode ).containsOnlyOnce( "if ( s_child != null )" );
}
}
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
</distributionManagement>

<properties>
<oap.project.version>25.6.4</oap.project.version>
<oap.project.version>25.6.5</oap.project.version>

<oap.deps.config.version>25.0.1</oap.deps.config.version>
<oap.deps.oap-teamcity.version>25.0.0</oap.deps.oap-teamcity.version>
Expand Down
Loading