Skip to content

Commit 7d25eca

Browse files
authored
Add support for Set in filter function (#8873)
also refactor the check for supported collection and maps add unit tests
1 parent c62a6fc commit 7d25eca

File tree

13 files changed

+447
-147
lines changed

13 files changed

+447
-147
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.datadog.debugger.el.expressions;
2+
3+
import static com.datadog.debugger.el.PrettyPrintVisitor.print;
4+
5+
import com.datadog.debugger.el.EvaluationException;
6+
import com.datadog.debugger.el.Expression;
7+
import com.datadog.debugger.el.Value;
8+
import com.datadog.debugger.el.values.ListValue;
9+
import com.datadog.debugger.el.values.MapValue;
10+
import com.datadog.debugger.el.values.SetValue;
11+
import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver;
12+
import datadog.trace.bootstrap.debugger.util.WellKnownClasses;
13+
import java.util.List;
14+
import java.util.Map;
15+
import java.util.Set;
16+
17+
public class CollectionExpressionHelper {
18+
public static void checkSupportedMap(MapValue map, Expression<?> expression) {
19+
Map<?, ?> mapHolder = (Map<?, ?>) map.getMapHolder();
20+
if (!WellKnownClasses.isSafe(mapHolder)) {
21+
throw new EvaluationException(
22+
"Unsupported Map class: " + mapHolder.getClass().getTypeName(), print(expression));
23+
}
24+
}
25+
26+
public static void checkSupportedList(ListValue collection, Expression<?> expression) {
27+
Object holder = collection.getValue();
28+
if (holder instanceof List) {
29+
if (!WellKnownClasses.isSafe((List<?>) holder)) {
30+
throw new EvaluationException(
31+
"Unsupported List class: " + holder.getClass().getTypeName(), print(expression));
32+
}
33+
}
34+
}
35+
36+
public static Value<?> evaluateTargetCollection(
37+
ValueExpression<?> collectionTarget,
38+
Expression<?> expression,
39+
ValueReferenceResolver valueRefResolver) {
40+
if (collectionTarget == null) {
41+
throw new EvaluationException(
42+
"Cannot evaluate the expression for null value", print(expression));
43+
}
44+
Value<?> value = collectionTarget.evaluate(valueRefResolver);
45+
if (value.isUndefined()) {
46+
throw new EvaluationException(
47+
"Cannot evaluate the expression for undefined value", print(expression));
48+
}
49+
if (value.isNull()) {
50+
throw new EvaluationException(
51+
"Cannot evaluate the expression for null value", print(expression));
52+
}
53+
return value;
54+
}
55+
56+
public static Set<?> checkSupportedSet(SetValue set, Expression<?> expression) {
57+
Set<?> setHolder = (Set<?>) set.getSetHolder();
58+
if (!WellKnownClasses.isSafe(setHolder)) {
59+
throw new EvaluationException(
60+
"Unsupported Set class: " + setHolder.getClass().getTypeName(), print(expression));
61+
}
62+
return setHolder;
63+
}
64+
}

dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/FilterCollectionExpression.java

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
11
package com.datadog.debugger.el.expressions;
22

3+
import static com.datadog.debugger.el.PrettyPrintVisitor.print;
4+
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedList;
5+
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedMap;
6+
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedSet;
7+
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.evaluateTargetCollection;
8+
9+
import com.datadog.debugger.el.EvaluationException;
310
import com.datadog.debugger.el.Value;
411
import com.datadog.debugger.el.Visitor;
512
import com.datadog.debugger.el.values.CollectionValue;
613
import com.datadog.debugger.el.values.ListValue;
714
import com.datadog.debugger.el.values.MapValue;
15+
import com.datadog.debugger.el.values.SetValue;
816
import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver;
917
import datadog.trace.bootstrap.debugger.el.ValueReferences;
1018
import java.util.ArrayList;
1119
import java.util.Collection;
1220
import java.util.Collections;
1321
import java.util.HashMap;
22+
import java.util.HashSet;
1423
import java.util.Map;
24+
import java.util.Set;
1525
import org.slf4j.Logger;
1626
import org.slf4j.LoggerFactory;
1727

@@ -32,18 +42,10 @@ public FilterCollectionExpression(ValueExpression<?> source, BooleanExpression f
3242

3343
@Override
3444
public CollectionValue<?> evaluate(ValueReferenceResolver valueRefResolver) {
35-
Value<?> collectionValue = source.evaluate(valueRefResolver);
36-
if (collectionValue.isUndefined()) {
37-
return (collectionValue instanceof CollectionValue)
38-
? (CollectionValue<?>) collectionValue
39-
: CollectionValue.UNDEFINED;
40-
} else if (collectionValue.isNull()) {
41-
return (collectionValue instanceof CollectionValue)
42-
? (CollectionValue<?>) collectionValue
43-
: CollectionValue.NULL;
44-
}
45+
Value<?> collectionValue = evaluateTargetCollection(source, filterExpression, valueRefResolver);
4546
if (collectionValue instanceof ListValue) {
4647
ListValue materialized = (ListValue) collectionValue;
48+
checkSupportedList(materialized, this);
4749
Collection<Object> filtered = new ArrayList<>();
4850
int len = materialized.count();
4951
for (int i = 0; i < len; i++) {
@@ -57,6 +59,7 @@ public CollectionValue<?> evaluate(ValueReferenceResolver valueRefResolver) {
5759
return new ListValue(filtered);
5860
} else if (collectionValue instanceof MapValue) {
5961
MapValue materialized = (MapValue) collectionValue;
62+
checkSupportedMap(materialized, this);
6063
Map<Object, Object> filtered = new HashMap<>();
6164
for (Value<?> key : materialized.getKeys()) {
6265
Value<?> value = key.isUndefined() ? Value.undefinedValue() : materialized.get(key);
@@ -70,9 +73,22 @@ public CollectionValue<?> evaluate(ValueReferenceResolver valueRefResolver) {
7073
}
7174
}
7275
return new MapValue(filtered);
76+
} else if (collectionValue instanceof SetValue) {
77+
SetValue materialized = (SetValue) collectionValue;
78+
Collection<Object> filtered = new HashSet<>();
79+
Set<?> setHolder = checkSupportedSet(materialized, this);
80+
for (Object value : setHolder) {
81+
if (filterExpression.evaluate(
82+
valueRefResolver.withExtensions(
83+
Collections.singletonMap(ValueReferences.ITERATOR_EXTENSION_NAME, value)))) {
84+
filtered.add(value);
85+
}
86+
}
87+
return new SetValue(filtered);
7388
}
74-
log.warn("Unsupported collection type {}", collectionValue.getValue().getClass().getTypeName());
75-
return CollectionValue.UNDEFINED;
89+
throw new EvaluationException(
90+
"Unsupported collection type: " + collectionValue.getValue().getClass().getTypeName(),
91+
print(this));
7692
}
7793

7894
@Override

dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/HasAllExpression.java

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
package com.datadog.debugger.el.expressions;
22

3+
import static com.datadog.debugger.el.PrettyPrintVisitor.print;
4+
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedList;
5+
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedMap;
6+
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedSet;
7+
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.evaluateTargetCollection;
8+
39
import com.datadog.debugger.el.EvaluationException;
4-
import com.datadog.debugger.el.PrettyPrintVisitor;
510
import com.datadog.debugger.el.Value;
611
import com.datadog.debugger.el.Visitor;
712
import com.datadog.debugger.el.values.ListValue;
813
import com.datadog.debugger.el.values.MapValue;
914
import com.datadog.debugger.el.values.SetValue;
1015
import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver;
1116
import datadog.trace.bootstrap.debugger.el.ValueReferences;
12-
import datadog.trace.bootstrap.debugger.util.WellKnownClasses;
1317
import java.util.Collections;
1418
import java.util.HashMap;
1519
import java.util.Map;
@@ -29,21 +33,10 @@ public HasAllExpression(ValueExpression<?> valueExpression, BooleanExpression fi
2933

3034
@Override
3135
public Boolean evaluate(ValueReferenceResolver valueRefResolver) {
32-
if (valueExpression == null) {
33-
throw new EvaluationException(
34-
"Cannot evaluate the expression for null value", PrettyPrintVisitor.print(this));
35-
}
36-
Value<?> value = valueExpression.evaluate(valueRefResolver);
37-
if (value.isUndefined()) {
38-
throw new EvaluationException(
39-
"Cannot evaluate the expression for undefined value", PrettyPrintVisitor.print(this));
40-
}
41-
if (value.isNull()) {
42-
throw new EvaluationException(
43-
"Cannot evaluate the expression for null value", PrettyPrintVisitor.print(this));
44-
}
36+
Value<?> value = evaluateTargetCollection(valueExpression, this, valueRefResolver);
4537
if (value instanceof ListValue) {
4638
ListValue collection = (ListValue) value;
39+
checkSupportedList(collection, this);
4740
if (collection.isEmpty()) {
4841
// always return TRUE for empty values (cf vacuous truth, see also Stream::allMatch)
4942
return Boolean.TRUE;
@@ -61,6 +54,7 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) {
6154
}
6255
if (value instanceof MapValue) {
6356
MapValue map = (MapValue) value;
57+
checkSupportedMap(map, this);
6458
if (map.isEmpty()) {
6559
// always return TRUE for empty values (cf vacuous truth, see also Stream::allMatch)
6660
return Boolean.TRUE;
@@ -81,28 +75,22 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) {
8175
}
8276
if (value instanceof SetValue) {
8377
SetValue set = (SetValue) value;
78+
Set<?> setHolder = checkSupportedSet(set, this);
8479
if (set.isEmpty()) {
8580
// always return TRUE for empty values (cf vacuous truth, see also Stream::allMatch)
8681
return Boolean.TRUE;
8782
}
88-
Set<?> setHolder = (Set<?>) set.getSetHolder();
89-
if (WellKnownClasses.isSafe(setHolder)) {
90-
for (Object val : setHolder) {
91-
if (!filterPredicateExpression.evaluate(
92-
valueRefResolver.withExtensions(
93-
Collections.singletonMap(ValueReferences.ITERATOR_EXTENSION_NAME, val)))) {
94-
return Boolean.FALSE;
95-
}
83+
for (Object val : setHolder) {
84+
if (!filterPredicateExpression.evaluate(
85+
valueRefResolver.withExtensions(
86+
Collections.singletonMap(ValueReferences.ITERATOR_EXTENSION_NAME, val)))) {
87+
return Boolean.FALSE;
9688
}
97-
return Boolean.TRUE;
9889
}
99-
throw new EvaluationException(
100-
"Unsupported Set class: " + setHolder.getClass().getTypeName(),
101-
PrettyPrintVisitor.print(this));
90+
return Boolean.TRUE;
10291
}
103-
return filterPredicateExpression.evaluate(
104-
valueRefResolver.withExtensions(
105-
Collections.singletonMap(ValueReferences.ITERATOR_EXTENSION_NAME, value)));
92+
throw new EvaluationException(
93+
"Unsupported collection class: " + value.getValue().getClass().getTypeName(), print(this));
10694
}
10795

10896
@Override

dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/HasAnyExpression.java

Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
package com.datadog.debugger.el.expressions;
22

3+
import static com.datadog.debugger.el.PrettyPrintVisitor.print;
4+
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedList;
5+
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedMap;
6+
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedSet;
7+
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.evaluateTargetCollection;
8+
39
import com.datadog.debugger.el.EvaluationException;
4-
import com.datadog.debugger.el.PrettyPrintVisitor;
510
import com.datadog.debugger.el.Value;
611
import com.datadog.debugger.el.Visitor;
712
import com.datadog.debugger.el.values.ListValue;
813
import com.datadog.debugger.el.values.MapValue;
914
import com.datadog.debugger.el.values.SetValue;
1015
import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver;
1116
import datadog.trace.bootstrap.debugger.el.ValueReferences;
12-
import datadog.trace.bootstrap.debugger.util.WellKnownClasses;
1317
import java.util.Collections;
1418
import java.util.HashMap;
1519
import java.util.Map;
@@ -30,21 +34,10 @@ public HasAnyExpression(
3034

3135
@Override
3236
public Boolean evaluate(ValueReferenceResolver valueRefResolver) {
33-
if (valueExpression == null) {
34-
throw new EvaluationException(
35-
"Cannot evaluate the expression for null value", PrettyPrintVisitor.print(this));
36-
}
37-
Value<?> value = valueExpression.evaluate(valueRefResolver);
38-
if (value.isUndefined()) {
39-
throw new EvaluationException(
40-
"Cannot evaluate the expression for undefined value", PrettyPrintVisitor.print(this));
41-
}
42-
if (value.isNull()) {
43-
throw new EvaluationException(
44-
"Cannot evaluate the expression for null value", PrettyPrintVisitor.print(this));
45-
}
37+
Value<?> value = evaluateTargetCollection(valueExpression, this, valueRefResolver);
4638
if (value instanceof ListValue) {
4739
ListValue collection = (ListValue) value;
40+
checkSupportedList(collection, this);
4841
if (collection.isEmpty()) {
4942
// always return FALSE for empty collection
5043
return Boolean.FALSE;
@@ -62,6 +55,7 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) {
6255
}
6356
if (value instanceof MapValue) {
6457
MapValue map = (MapValue) value;
58+
checkSupportedMap(map, this);
6559
if (map.isEmpty()) {
6660
return Boolean.FALSE;
6761
}
@@ -81,28 +75,22 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) {
8175
}
8276
if (value instanceof SetValue) {
8377
SetValue set = (SetValue) value;
78+
Set<?> setHolder = checkSupportedSet(set, this);
8479
if (set.isEmpty()) {
8580
return Boolean.FALSE;
8681
}
87-
Set<?> setHolder = (Set<?>) set.getSetHolder();
88-
if (WellKnownClasses.isSafe(setHolder)) {
89-
for (Object val : setHolder) {
90-
if (filterPredicateExpression.evaluate(
91-
valueRefResolver.withExtensions(
92-
Collections.singletonMap(
93-
ValueReferences.ITERATOR_EXTENSION_NAME, Value.of(val))))) {
94-
return Boolean.TRUE;
95-
}
82+
for (Object val : setHolder) {
83+
if (filterPredicateExpression.evaluate(
84+
valueRefResolver.withExtensions(
85+
Collections.singletonMap(
86+
ValueReferences.ITERATOR_EXTENSION_NAME, Value.of(val))))) {
87+
return Boolean.TRUE;
9688
}
97-
return Boolean.FALSE;
9889
}
99-
throw new EvaluationException(
100-
"Unsupported Set class: " + setHolder.getClass().getTypeName(),
101-
PrettyPrintVisitor.print(this));
90+
return Boolean.FALSE;
10291
}
103-
return filterPredicateExpression.evaluate(
104-
valueRefResolver.withExtensions(
105-
Collections.singletonMap(ValueReferences.ITERATOR_EXTENSION_NAME, value)));
92+
throw new EvaluationException(
93+
"Unsupported collection class: " + value.getValue().getClass().getTypeName(), print(this));
10694
}
10795

10896
@Override

dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/expressions/IndexExpression.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,13 @@ public Value<?> evaluate(ValueReferenceResolver valueRefResolver) {
4343
} else {
4444
result = ((MapValue) targetValue).get(objKey);
4545
}
46-
}
47-
if (targetValue instanceof ListValue) {
46+
} else if (targetValue instanceof ListValue) {
4847
result = ((ListValue) targetValue).get(keyValue.getValue());
48+
} else {
49+
throw new EvaluationException(
50+
"Cannot evaluate the expression for unsupported type: "
51+
+ targetValue.getClass().getTypeName(),
52+
PrettyPrintVisitor.print(this));
4953
}
5054
} catch (IllegalArgumentException ex) {
5155
throw new EvaluationException(ex.getMessage(), PrettyPrintVisitor.print(this), ex);

dd-java-agent/agent-debugger/debugger-el/src/main/java/com/datadog/debugger/el/values/SetValue.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ public boolean contains(Value<?> val) {
100100
return false;
101101
}
102102

103+
@Override
104+
public boolean isNull() {
105+
return setHolder == null || (setHolder instanceof Value && ((Value<?>) setHolder).isNull());
106+
}
107+
108+
@Override
109+
public boolean isUndefined() {
110+
return setHolder instanceof Value && ((Value<?>) setHolder).isUndefined();
111+
}
112+
103113
@Override
104114
public <R> R accept(Visitor<R> visitor) {
105115
return visitor.visit(this);

0 commit comments

Comments
 (0)