Skip to content

Add support for Set in filter function #8873

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 26, 2025
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
@@ -0,0 +1,64 @@
package com.datadog.debugger.el.expressions;

import static com.datadog.debugger.el.PrettyPrintVisitor.print;

import com.datadog.debugger.el.EvaluationException;
import com.datadog.debugger.el.Expression;
import com.datadog.debugger.el.Value;
import com.datadog.debugger.el.values.ListValue;
import com.datadog.debugger.el.values.MapValue;
import com.datadog.debugger.el.values.SetValue;
import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver;
import datadog.trace.bootstrap.debugger.util.WellKnownClasses;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class CollectionExpressionHelper {
public static void checkSupportedMap(MapValue map, Expression<?> expression) {
Map<?, ?> mapHolder = (Map<?, ?>) map.getMapHolder();
if (!WellKnownClasses.isSafe(mapHolder)) {
throw new EvaluationException(
"Unsupported Map class: " + mapHolder.getClass().getTypeName(), print(expression));
}
}

public static void checkSupportedList(ListValue collection, Expression<?> expression) {
Object holder = collection.getValue();
if (holder instanceof List) {
if (!WellKnownClasses.isSafe((List<?>) holder)) {
throw new EvaluationException(
"Unsupported List class: " + holder.getClass().getTypeName(), print(expression));
}
}
}

public static Value<?> evaluateTargetCollection(
ValueExpression<?> collectionTarget,
Expression<?> expression,
ValueReferenceResolver valueRefResolver) {
if (collectionTarget == null) {
throw new EvaluationException(
"Cannot evaluate the expression for null value", print(expression));
}
Value<?> value = collectionTarget.evaluate(valueRefResolver);
if (value.isUndefined()) {
throw new EvaluationException(
"Cannot evaluate the expression for undefined value", print(expression));
}
if (value.isNull()) {
throw new EvaluationException(
"Cannot evaluate the expression for null value", print(expression));
}
return value;
}

public static Set<?> checkSupportedSet(SetValue set, Expression<?> expression) {
Set<?> setHolder = (Set<?>) set.getSetHolder();
if (!WellKnownClasses.isSafe(setHolder)) {
throw new EvaluationException(
"Unsupported Set class: " + setHolder.getClass().getTypeName(), print(expression));
}
return setHolder;
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
package com.datadog.debugger.el.expressions;

import static com.datadog.debugger.el.PrettyPrintVisitor.print;
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedList;
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedMap;
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedSet;
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.evaluateTargetCollection;

import com.datadog.debugger.el.EvaluationException;
import com.datadog.debugger.el.Value;
import com.datadog.debugger.el.Visitor;
import com.datadog.debugger.el.values.CollectionValue;
import com.datadog.debugger.el.values.ListValue;
import com.datadog.debugger.el.values.MapValue;
import com.datadog.debugger.el.values.SetValue;
import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver;
import datadog.trace.bootstrap.debugger.el.ValueReferences;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

@Override
public CollectionValue<?> evaluate(ValueReferenceResolver valueRefResolver) {
Value<?> collectionValue = source.evaluate(valueRefResolver);
if (collectionValue.isUndefined()) {
return (collectionValue instanceof CollectionValue)
? (CollectionValue<?>) collectionValue
: CollectionValue.UNDEFINED;
} else if (collectionValue.isNull()) {
return (collectionValue instanceof CollectionValue)
? (CollectionValue<?>) collectionValue
: CollectionValue.NULL;
}
Value<?> collectionValue = evaluateTargetCollection(source, filterExpression, valueRefResolver);
if (collectionValue instanceof ListValue) {
ListValue materialized = (ListValue) collectionValue;
checkSupportedList(materialized, this);
Collection<Object> filtered = new ArrayList<>();
int len = materialized.count();
for (int i = 0; i < len; i++) {
Expand All @@ -57,6 +59,7 @@ public CollectionValue<?> evaluate(ValueReferenceResolver valueRefResolver) {
return new ListValue(filtered);
} else if (collectionValue instanceof MapValue) {
MapValue materialized = (MapValue) collectionValue;
checkSupportedMap(materialized, this);
Map<Object, Object> filtered = new HashMap<>();
for (Value<?> key : materialized.getKeys()) {
Value<?> value = key.isUndefined() ? Value.undefinedValue() : materialized.get(key);
Expand All @@ -70,9 +73,22 @@ public CollectionValue<?> evaluate(ValueReferenceResolver valueRefResolver) {
}
}
return new MapValue(filtered);
} else if (collectionValue instanceof SetValue) {
SetValue materialized = (SetValue) collectionValue;
Collection<Object> filtered = new HashSet<>();
Set<?> setHolder = checkSupportedSet(materialized, this);
for (Object value : setHolder) {
if (filterExpression.evaluate(
valueRefResolver.withExtensions(
Collections.singletonMap(ValueReferences.ITERATOR_EXTENSION_NAME, value)))) {
filtered.add(value);
}
}
return new SetValue(filtered);
}
log.warn("Unsupported collection type {}", collectionValue.getValue().getClass().getTypeName());
return CollectionValue.UNDEFINED;
throw new EvaluationException(
"Unsupported collection type: " + collectionValue.getValue().getClass().getTypeName(),
print(this));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.datadog.debugger.el.expressions;

import static com.datadog.debugger.el.PrettyPrintVisitor.print;
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedList;
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedMap;
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedSet;
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.evaluateTargetCollection;

import com.datadog.debugger.el.EvaluationException;
import com.datadog.debugger.el.PrettyPrintVisitor;
import com.datadog.debugger.el.Value;
import com.datadog.debugger.el.Visitor;
import com.datadog.debugger.el.values.ListValue;
import com.datadog.debugger.el.values.MapValue;
import com.datadog.debugger.el.values.SetValue;
import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver;
import datadog.trace.bootstrap.debugger.el.ValueReferences;
import datadog.trace.bootstrap.debugger.util.WellKnownClasses;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -29,21 +33,10 @@ public HasAllExpression(ValueExpression<?> valueExpression, BooleanExpression fi

@Override
public Boolean evaluate(ValueReferenceResolver valueRefResolver) {
if (valueExpression == null) {
throw new EvaluationException(
"Cannot evaluate the expression for null value", PrettyPrintVisitor.print(this));
}
Value<?> value = valueExpression.evaluate(valueRefResolver);
if (value.isUndefined()) {
throw new EvaluationException(
"Cannot evaluate the expression for undefined value", PrettyPrintVisitor.print(this));
}
if (value.isNull()) {
throw new EvaluationException(
"Cannot evaluate the expression for null value", PrettyPrintVisitor.print(this));
}
Value<?> value = evaluateTargetCollection(valueExpression, this, valueRefResolver);
if (value instanceof ListValue) {
ListValue collection = (ListValue) value;
checkSupportedList(collection, this);
if (collection.isEmpty()) {
// always return TRUE for empty values (cf vacuous truth, see also Stream::allMatch)
return Boolean.TRUE;
Expand All @@ -61,6 +54,7 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) {
}
if (value instanceof MapValue) {
MapValue map = (MapValue) value;
checkSupportedMap(map, this);
if (map.isEmpty()) {
// always return TRUE for empty values (cf vacuous truth, see also Stream::allMatch)
return Boolean.TRUE;
Expand All @@ -81,28 +75,22 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) {
}
if (value instanceof SetValue) {
SetValue set = (SetValue) value;
Set<?> setHolder = checkSupportedSet(set, this);
if (set.isEmpty()) {
// always return TRUE for empty values (cf vacuous truth, see also Stream::allMatch)
return Boolean.TRUE;
}
Set<?> setHolder = (Set<?>) set.getSetHolder();
if (WellKnownClasses.isSafe(setHolder)) {
for (Object val : setHolder) {
if (!filterPredicateExpression.evaluate(
valueRefResolver.withExtensions(
Collections.singletonMap(ValueReferences.ITERATOR_EXTENSION_NAME, val)))) {
return Boolean.FALSE;
}
for (Object val : setHolder) {
if (!filterPredicateExpression.evaluate(
valueRefResolver.withExtensions(
Collections.singletonMap(ValueReferences.ITERATOR_EXTENSION_NAME, val)))) {
return Boolean.FALSE;
}
return Boolean.TRUE;
}
throw new EvaluationException(
"Unsupported Set class: " + setHolder.getClass().getTypeName(),
PrettyPrintVisitor.print(this));
return Boolean.TRUE;
}
return filterPredicateExpression.evaluate(
valueRefResolver.withExtensions(
Collections.singletonMap(ValueReferences.ITERATOR_EXTENSION_NAME, value)));
throw new EvaluationException(
"Unsupported collection class: " + value.getValue().getClass().getTypeName(), print(this));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package com.datadog.debugger.el.expressions;

import static com.datadog.debugger.el.PrettyPrintVisitor.print;
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedList;
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedMap;
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.checkSupportedSet;
import static com.datadog.debugger.el.expressions.CollectionExpressionHelper.evaluateTargetCollection;

import com.datadog.debugger.el.EvaluationException;
import com.datadog.debugger.el.PrettyPrintVisitor;
import com.datadog.debugger.el.Value;
import com.datadog.debugger.el.Visitor;
import com.datadog.debugger.el.values.ListValue;
import com.datadog.debugger.el.values.MapValue;
import com.datadog.debugger.el.values.SetValue;
import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver;
import datadog.trace.bootstrap.debugger.el.ValueReferences;
import datadog.trace.bootstrap.debugger.util.WellKnownClasses;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -30,21 +34,10 @@ public HasAnyExpression(

@Override
public Boolean evaluate(ValueReferenceResolver valueRefResolver) {
if (valueExpression == null) {
throw new EvaluationException(
"Cannot evaluate the expression for null value", PrettyPrintVisitor.print(this));
}
Value<?> value = valueExpression.evaluate(valueRefResolver);
if (value.isUndefined()) {
throw new EvaluationException(
"Cannot evaluate the expression for undefined value", PrettyPrintVisitor.print(this));
}
if (value.isNull()) {
throw new EvaluationException(
"Cannot evaluate the expression for null value", PrettyPrintVisitor.print(this));
}
Value<?> value = evaluateTargetCollection(valueExpression, this, valueRefResolver);
if (value instanceof ListValue) {
ListValue collection = (ListValue) value;
checkSupportedList(collection, this);
if (collection.isEmpty()) {
// always return FALSE for empty collection
return Boolean.FALSE;
Expand All @@ -62,6 +55,7 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) {
}
if (value instanceof MapValue) {
MapValue map = (MapValue) value;
checkSupportedMap(map, this);
if (map.isEmpty()) {
return Boolean.FALSE;
}
Expand All @@ -81,28 +75,22 @@ public Boolean evaluate(ValueReferenceResolver valueRefResolver) {
}
if (value instanceof SetValue) {
SetValue set = (SetValue) value;
Set<?> setHolder = checkSupportedSet(set, this);
if (set.isEmpty()) {
return Boolean.FALSE;
}
Set<?> setHolder = (Set<?>) set.getSetHolder();
if (WellKnownClasses.isSafe(setHolder)) {
for (Object val : setHolder) {
if (filterPredicateExpression.evaluate(
valueRefResolver.withExtensions(
Collections.singletonMap(
ValueReferences.ITERATOR_EXTENSION_NAME, Value.of(val))))) {
return Boolean.TRUE;
}
for (Object val : setHolder) {
if (filterPredicateExpression.evaluate(
valueRefResolver.withExtensions(
Collections.singletonMap(
ValueReferences.ITERATOR_EXTENSION_NAME, Value.of(val))))) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
throw new EvaluationException(
"Unsupported Set class: " + setHolder.getClass().getTypeName(),
PrettyPrintVisitor.print(this));
return Boolean.FALSE;
}
return filterPredicateExpression.evaluate(
valueRefResolver.withExtensions(
Collections.singletonMap(ValueReferences.ITERATOR_EXTENSION_NAME, value)));
throw new EvaluationException(
"Unsupported collection class: " + value.getValue().getClass().getTypeName(), print(this));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,13 @@ public Value<?> evaluate(ValueReferenceResolver valueRefResolver) {
} else {
result = ((MapValue) targetValue).get(objKey);
}
}
if (targetValue instanceof ListValue) {
} else if (targetValue instanceof ListValue) {
result = ((ListValue) targetValue).get(keyValue.getValue());
} else {
throw new EvaluationException(
"Cannot evaluate the expression for unsupported type: "
+ targetValue.getClass().getTypeName(),
PrettyPrintVisitor.print(this));
}
} catch (IllegalArgumentException ex) {
throw new EvaluationException(ex.getMessage(), PrettyPrintVisitor.print(this), ex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ public boolean contains(Value<?> val) {
return false;
}

@Override
public boolean isNull() {
return setHolder == null || (setHolder instanceof Value && ((Value<?>) setHolder).isNull());
}

@Override
public boolean isUndefined() {
return setHolder instanceof Value && ((Value<?>) setHolder).isUndefined();
}

@Override
public <R> R accept(Visitor<R> visitor) {
return visitor.visit(this);
Expand Down
Loading
Loading