added string support for lambda expressions

This commit is contained in:
wea_ondara
2022-09-18 14:28:50 +02:00
parent 6870435eea
commit 27198a1e78
12 changed files with 110 additions and 20 deletions

View File

@@ -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

View File

@@ -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 {

View File

@@ -29,6 +29,9 @@ public class ConstantExpression implements Expression {
@Override
public String toString() {
if (value instanceof String) {
return "\"" + value + "\"";
}
return value.toString();
}
}

View File

@@ -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
;

View 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(", ")) + ")";
}
}

View File

@@ -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();
}

View File

@@ -25,6 +25,6 @@ public class TableExpression extends ConstantExpression {
@Override
public String toString() {
return "`" + super.toString() + "`";
return "`" + name + "`";
}
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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());

View File

@@ -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) {
}

View File

@@ -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;
}
}