added string support for lambda expressions
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -29,6 +29,9 @@ public class ConstantExpression implements Expression {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (value instanceof String) {
|
||||
return "\"" + value + "\"";
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
;
|
||||
|
||||
32
src/main/java/jef/expressions/FunctionExpression.java
Normal file
32
src/main/java/jef/expressions/FunctionExpression.java
Normal file
@@ -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<Expression> 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(", ")) + ")";
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,6 @@ public class TableExpression extends ConstantExpression {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "`" + super.toString() + "`";
|
||||
return "`" + name + "`";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user