From 27198a1e780b1db6cefdf01be404584a10a661fc Mon Sep 17 00:00:00 2001 From: wea_ondara Date: Sun, 18 Sep 2022 14:28:50 +0200 Subject: [PATCH] added string support for lambda expressions --- README.md | 1 - .../java/jef/asm/FilterMethodVisitor.java | 16 ++++++++++ .../jef/expressions/ConstantExpression.java | 3 ++ src/main/java/jef/expressions/Expression.java | 3 +- .../jef/expressions/FunctionExpression.java | 32 +++++++++++++++++++ .../jef/expressions/ParameterExpression.java | 3 +- .../java/jef/expressions/TableExpression.java | 2 +- .../modifier/ExpressionModifier.java | 6 ++++ .../modifier/ExpressionOptimizerBottomUp.java | 30 ++++++++--------- .../visitors/DebugExpressionVisitor.java | 6 ++++ .../visitors/ExpressionVisitor.java | 5 +++ .../java/jef/asm/OptimizedAsmParserTest.java | 23 +++++++++++++ 12 files changed, 110 insertions(+), 20 deletions(-) create mode 100644 src/main/java/jef/expressions/FunctionExpression.java diff --git a/README.md b/README.md index c12c1c1..695777c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ - references to platform specific impls ## Asm -- equals function for primitive and string + String::equalsIgnoreCase - equals function for registered primitive conversion types - IConst0Fixer: IConst0/1 on its own => false/true - actually parse getter diff --git a/src/main/java/jef/asm/FilterMethodVisitor.java b/src/main/java/jef/asm/FilterMethodVisitor.java index bedb9f7..ff1b18e 100644 --- a/src/main/java/jef/asm/FilterMethodVisitor.java +++ b/src/main/java/jef/asm/FilterMethodVisitor.java @@ -3,6 +3,7 @@ package jef.asm; import jef.expressions.BinaryExpression; import jef.expressions.ConstantExpression; import jef.expressions.Expression; +import jef.expressions.FunctionExpression; import jef.expressions.IntermediateFieldExpression; import jef.expressions.NullExpression; import jef.expressions.ParameterExpression; @@ -114,6 +115,18 @@ class FilterMethodVisitor extends MethodVisitor { var element = varStack.pop(); var collection = varStack.pop(); varStack.push(new BinaryExpression(element, collection, BinaryExpression.Operator.IN)); + } else if (name.equals("equals") + && owner.equals("java/lang/String")) { + var value = varStack.pop(); + var variable = varStack.pop(); + varStack.push(new BinaryExpression(variable, value, BinaryExpression.Operator.EQ)); + } else if (name.equals("equalsIgnoreCase") + && owner.equals("java/lang/String")) { + var value = varStack.pop(); + var variable = varStack.pop(); + varStack.push(new BinaryExpression(new FunctionExpression(FunctionExpression.TOLOWER, List.of(variable)), + new FunctionExpression(FunctionExpression.TOLOWER, List.of(value)), + BinaryExpression.Operator.EQ)); } else if (descriptor.startsWith("()")) { var method = Class.forName(owner.replace("/", ".")).getDeclaredMethod(name); var res = new OptimizedAsmParser(method).parse(); @@ -136,6 +149,9 @@ class FilterMethodVisitor extends MethodVisitor { throw new RuntimeException("method insn: unsupported function " + name + " in " + owner); } } catch (Exception e) { + if (e instanceof RuntimeException re) { + throw re; + } throw new RuntimeException("method insn: ", e); } } else { diff --git a/src/main/java/jef/expressions/ConstantExpression.java b/src/main/java/jef/expressions/ConstantExpression.java index b7cf7e6..b803f4f 100644 --- a/src/main/java/jef/expressions/ConstantExpression.java +++ b/src/main/java/jef/expressions/ConstantExpression.java @@ -29,6 +29,9 @@ public class ConstantExpression implements Expression { @Override public String toString() { + if (value instanceof String) { + return "\"" + value + "\""; + } return value.toString(); } } diff --git a/src/main/java/jef/expressions/Expression.java b/src/main/java/jef/expressions/Expression.java index f2d94e3..27e372f 100644 --- a/src/main/java/jef/expressions/Expression.java +++ b/src/main/java/jef/expressions/Expression.java @@ -13,6 +13,7 @@ public interface Expression { BINARY, CONSTANT, FIELD, + FUNCTION, INTERMEDIATE_FIELD, LIMIT, NULL, @@ -42,7 +43,7 @@ public interface Expression { BIT_SHIFT(10), // <<, >> ADD_SUB(13), // +, - MUL_DIV_MOD(14), // *, /, % - UNARY_PRE(15), // +, -, !, ~, --i, ++i + UNARY_PRE(15), // +, -, !, ~, --i, ++i, function calls UNARY_POST(16), // i++, i-- CONSTANT(17), // constant values ; diff --git a/src/main/java/jef/expressions/FunctionExpression.java b/src/main/java/jef/expressions/FunctionExpression.java new file mode 100644 index 0000000..acd02f0 --- /dev/null +++ b/src/main/java/jef/expressions/FunctionExpression.java @@ -0,0 +1,32 @@ +package jef.expressions; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +import java.util.List; +import java.util.stream.Collectors; + +@AllArgsConstructor +@Getter +@EqualsAndHashCode +public class FunctionExpression implements Expression { + public static final String TOLOWER = "TOLOWER"; + private final String function; + private final List parameters; + + @Override + public Type getType() { + return Type.FUNCTION; + } + + @Override + public Priority getPriority() { + return Priority.UNARY_PRE; + } + + @Override + public String toString() { + return function + "(" + parameters.stream().map(Expression::toString).collect(Collectors.joining(", ")) + ")"; + } +} diff --git a/src/main/java/jef/expressions/ParameterExpression.java b/src/main/java/jef/expressions/ParameterExpression.java index a24d975..baf7359 100644 --- a/src/main/java/jef/expressions/ParameterExpression.java +++ b/src/main/java/jef/expressions/ParameterExpression.java @@ -4,7 +4,6 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import java.util.Collection; -import java.util.Objects; import java.util.stream.Collectors; @Getter @@ -33,7 +32,7 @@ public class ParameterExpression extends ConstantExpression implements Expressio } else if (this.value == null) { return "null"; } else if (this.value instanceof Collection) { - return "(" + ((Collection) this.value).stream().map(e -> e == null ? "NULL" : Objects.toString(e)).collect(Collectors.joining(", ")) + ")"; + return "(" + ((Collection) this.value).stream().map(e -> e == null ? "NULL" : (e instanceof String ? "\"" + e + "\"" : e.toString())).collect(Collectors.joining(", ")) + ")"; } return value.toString(); } diff --git a/src/main/java/jef/expressions/TableExpression.java b/src/main/java/jef/expressions/TableExpression.java index 77ce31b..03c6f1d 100644 --- a/src/main/java/jef/expressions/TableExpression.java +++ b/src/main/java/jef/expressions/TableExpression.java @@ -25,6 +25,6 @@ public class TableExpression extends ConstantExpression { @Override public String toString() { - return "`" + super.toString() + "`"; + return "`" + name + "`"; } } diff --git a/src/main/java/jef/expressions/modifier/ExpressionModifier.java b/src/main/java/jef/expressions/modifier/ExpressionModifier.java index 87ba917..cbf6a4a 100644 --- a/src/main/java/jef/expressions/modifier/ExpressionModifier.java +++ b/src/main/java/jef/expressions/modifier/ExpressionModifier.java @@ -5,6 +5,7 @@ import jef.expressions.BinaryExpression; import jef.expressions.ConstantExpression; import jef.expressions.Expression; import jef.expressions.FieldExpression; +import jef.expressions.FunctionExpression; import jef.expressions.IntermediateFieldExpression; import jef.expressions.LimitExpression; import jef.expressions.NullExpression; @@ -28,6 +29,7 @@ public abstract class ExpressionModifier { case BINARY -> modifyBinary((BinaryExpression) expr); case CONSTANT -> modifyConstant((ConstantExpression) expr); case FIELD -> modifyField((FieldExpression) expr); + case FUNCTION -> modifyFunction((FunctionExpression) expr); case INTERMEDIATE_FIELD -> modifyIntermediateField((IntermediateFieldExpression) expr); case LIMIT -> modifyLimit((LimitExpression) expr); case NULL -> modifyNull((NullExpression) expr); @@ -63,6 +65,10 @@ public abstract class ExpressionModifier { return expr; } + public Expression modifyFunction(FunctionExpression expr) { + return new FunctionExpression(expr.getFunction(), expr.getParameters().stream().map(this::modify).toList()); + } + public Expression modifyIntermediateField(IntermediateFieldExpression expr) { return expr; } diff --git a/src/main/java/jef/expressions/modifier/ExpressionOptimizerBottomUp.java b/src/main/java/jef/expressions/modifier/ExpressionOptimizerBottomUp.java index 748127a..db66d61 100644 --- a/src/main/java/jef/expressions/modifier/ExpressionOptimizerBottomUp.java +++ b/src/main/java/jef/expressions/modifier/ExpressionOptimizerBottomUp.java @@ -51,15 +51,15 @@ public class ExpressionOptimizerBottomUp extends ExpressionModifier { //<- SELECT * FROM (SELECT * FROM (SELECT * FROM table) WHERE 1) LIMIT 1 //-> SELECT * FROM (SELECT * FROM table) WHERE 1 LIMIT 1 if (inner instanceof SelectExpression s - && s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE)) - && s.getFrom() instanceof WhereExpression w) { + && s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE)) + && s.getFrom() instanceof WhereExpression w) { return new LimitExpression(w, expr.getStart(), expr.getCount()); } //<- SELECT * FROM (SELECT * FROM (SELECT * FROM table) ORDER BY x ASC 1) LIMIT 1 //-> SELECT * FROM (SELECT * FROM table) ORDER BY x ASC LIMIT 1 else if (inner instanceof SelectExpression s - && s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE)) - && s.getFrom() instanceof OrderExpression o) { + && s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE)) + && s.getFrom() instanceof OrderExpression o) { return new LimitExpression(o, expr.getStart(), expr.getCount()); } //<- SELECT * FROM (SELECT * FROM (SELECT * FROM table) LIMIT 3) LIMIT 1 @@ -103,8 +103,8 @@ public class ExpressionOptimizerBottomUp extends ExpressionModifier { //<- order(select(where(...))) //-> order(where(...)) if (inner instanceof SelectExpression s - && s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE)) - && s.getFrom() instanceof WhereExpression w) { + && s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE)) + && s.getFrom() instanceof WhereExpression w) { return new OrderExpression(w, expr.getSorts()); } //<- order1(order2(...)) @@ -119,7 +119,7 @@ public class ExpressionOptimizerBottomUp extends ExpressionModifier { public Expression modifySelect(SelectExpression expr) { var from = modify(expr.getFrom()); if (from instanceof SelectExpression s - && s.getFields().equals(expr.getFields())) { + && s.getFields().equals(expr.getFields())) { return s; } return super.modifySelect(expr); @@ -141,18 +141,18 @@ public class ExpressionOptimizerBottomUp extends ExpressionModifier { public Expression modifyUnary(UnaryExpression expr) { var inner = modify(expr.getExpr()); if (inner instanceof UnaryExpression u - && u.getOperator() == UnaryExpression.Operator.NOT - && expr.getOperator() == UnaryExpression.Operator.NOT) { + && u.getOperator() == UnaryExpression.Operator.NOT + && expr.getOperator() == UnaryExpression.Operator.NOT) { //!!x -> x return modify(u.getExpr()); } else if (inner instanceof BinaryExpression b - && expr.getOperator() == UnaryExpression.Operator.NOT - && b.getOperator().isInvertible()) { + && expr.getOperator() == UnaryExpression.Operator.NOT + && b.getOperator().isInvertible()) { //!(a < b) -> a >= b return new BinaryExpression(b.getLeft(), b.getRight(), b.getOperator().invert()); } else if (inner instanceof BinaryExpression b - && expr.getOperator() == UnaryExpression.Operator.NOT - && b.getOperator() == BinaryExpression.Operator.IS) { + && expr.getOperator() == UnaryExpression.Operator.NOT + && b.getOperator() == BinaryExpression.Operator.IS) { //!(a < b) -> a >= b return new BinaryExpression(b.getLeft(), new UnaryExpression(b.getRight(), UnaryExpression.Operator.NOT), b.getOperator()); } else { @@ -173,8 +173,8 @@ public class ExpressionOptimizerBottomUp extends ExpressionModifier { //<- where(cond1, select(where(cond2, ...))) //-> where(and(cond1, cond2) ...)) else if (queryable instanceof SelectExpression s - && s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE)) - && s.getFrom() instanceof WhereExpression w) { + && s.getFields().equals(List.of(DatabaseSelectAllExpression.INSTANCE)) + && s.getFrom() instanceof WhereExpression w) { return new WhereExpression(w.getQueryable(), new AndExpression(w.getWhere(), where)); } return super.modifyWhere(expr); diff --git a/src/main/java/jef/expressions/visitors/DebugExpressionVisitor.java b/src/main/java/jef/expressions/visitors/DebugExpressionVisitor.java index 7df61ba..8958096 100644 --- a/src/main/java/jef/expressions/visitors/DebugExpressionVisitor.java +++ b/src/main/java/jef/expressions/visitors/DebugExpressionVisitor.java @@ -4,6 +4,7 @@ import jef.expressions.AndExpression; import jef.expressions.BinaryExpression; import jef.expressions.ConstantExpression; import jef.expressions.FieldExpression; +import jef.expressions.FunctionExpression; import jef.expressions.IntermediateFieldExpression; import jef.expressions.LimitExpression; import jef.expressions.NullExpression; @@ -56,6 +57,11 @@ public class DebugExpressionVisitor extends ExpressionVisitor { System.out.println(i() + expr.toString()); } + @Override + public void visitFunction(FunctionExpression expr) { + System.out.println(i() + expr.toString()); + } + @Override public void visitIntermediateField(IntermediateFieldExpression expr) { System.out.println(i() + expr.toString()); diff --git a/src/main/java/jef/expressions/visitors/ExpressionVisitor.java b/src/main/java/jef/expressions/visitors/ExpressionVisitor.java index f05ded1..d921e5a 100644 --- a/src/main/java/jef/expressions/visitors/ExpressionVisitor.java +++ b/src/main/java/jef/expressions/visitors/ExpressionVisitor.java @@ -5,6 +5,7 @@ import jef.expressions.BinaryExpression; import jef.expressions.ConstantExpression; import jef.expressions.Expression; import jef.expressions.FieldExpression; +import jef.expressions.FunctionExpression; import jef.expressions.IntermediateFieldExpression; import jef.expressions.LimitExpression; import jef.expressions.NullExpression; @@ -24,6 +25,7 @@ public abstract class ExpressionVisitor { case BINARY -> visitBinary((BinaryExpression) expr); case CONSTANT -> visitConstant((ConstantExpression) expr); case FIELD -> visitField((FieldExpression) expr); + case FUNCTION -> visitFunction((FunctionExpression) expr); case INTERMEDIATE_FIELD -> visitIntermediateField((IntermediateFieldExpression) expr); case LIMIT -> visitLimit((LimitExpression) expr); case NULL -> visitNull((NullExpression) expr); @@ -56,6 +58,9 @@ public abstract class ExpressionVisitor { public void visitField(FieldExpression expr) { } + public void visitFunction(FunctionExpression expr) { + } + public void visitIntermediateField(IntermediateFieldExpression expr) { } diff --git a/src/test/java/jef/asm/OptimizedAsmParserTest.java b/src/test/java/jef/asm/OptimizedAsmParserTest.java index 1cf53f0..826079b 100644 --- a/src/test/java/jef/asm/OptimizedAsmParserTest.java +++ b/src/test/java/jef/asm/OptimizedAsmParserTest.java @@ -166,6 +166,28 @@ public class OptimizedAsmParserTest { Assertions.assertEquals("`b`", act); } + @Test + public void testCollectionContains() throws AsmParseException { + String act; + var s = Arrays.asList(null, 2, "str"); + act = new OptimizedAsmParser((SerializablePredicate) (TestClass e) -> s.contains(e.i)).parse().getExpression().toString(); + Assertions.assertEquals("`i` IN (NULL, 2, \"str\")", act); + } + + @Test + public void testCollectionEquals() throws AsmParseException { + String act; + act = new OptimizedAsmParser((SerializablePredicate) (TestClass e) -> e.s.equals("str")).parse().getExpression().toString(); + Assertions.assertEquals("`s` = \"str\"", act); + } + + @Test + public void testCollectionEqualsIgnoreCase() throws AsmParseException { + String act; + act = new OptimizedAsmParser((SerializablePredicate) (TestClass e) -> e.s.equalsIgnoreCase("str")).parse().getExpression().toString(); + Assertions.assertEquals("TOLOWER(`s`) = TOLOWER(\"str\")", act); + } + @Getter public static class TestClass extends SerializableObject { public int i = 1; @@ -174,5 +196,6 @@ public class OptimizedAsmParserTest { public float f; public long l; public boolean b; + public String s; } } \ No newline at end of file