Skip to content
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
Expand Up @@ -31,9 +31,11 @@
import com.datadog.debugger.el.values.NullValue;
import com.datadog.debugger.el.values.NumericValue;
import com.datadog.debugger.el.values.ObjectValue;
import com.datadog.debugger.el.values.SetValue;
import com.datadog.debugger.el.values.StringValue;
import java.util.Collection;
import java.util.Map;
import java.util.Set;

/**
* A debugger DSL representation. A simple all-static class which can be used to build a complete
Expand Down Expand Up @@ -140,6 +142,10 @@ public static ListValue value(Collection<?> value) {
return new ListValue(value);
}

public static SetValue value(Set<?> value) {
return new SetValue(value);
}

public static NullValue nullValue() {
return NullValue.INSTANCE;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.datadog.debugger.el.values.NullValue;
import com.datadog.debugger.el.values.NumericValue;
import com.datadog.debugger.el.values.ObjectValue;
import com.datadog.debugger.el.values.SetValue;
import com.datadog.debugger.el.values.StringValue;
import datadog.trace.bootstrap.debugger.el.Values;
import java.math.BigDecimal;
Expand Down Expand Up @@ -268,6 +269,14 @@ public String visit(MapValue mapValue) {
return "null";
}

@Override
public String visit(SetValue setValue) {
if (setValue.getSetHolder() instanceof Set) {
return "Set";
}
return "null";
}

private String stringPredicateExpression(StringPredicateExpression stringPredicateExpression) {
return stringPredicateExpression.getName()
+ "("
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
import com.datadog.debugger.el.values.NullValue;
import com.datadog.debugger.el.values.NumericValue;
import com.datadog.debugger.el.values.ObjectValue;
import com.datadog.debugger.el.values.SetValue;
import com.datadog.debugger.el.values.StringValue;
import com.datadog.debugger.el.values.UndefinedValue;
import datadog.trace.bootstrap.debugger.el.Values;
import datadog.trace.bootstrap.debugger.util.WellKnownClasses;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

/** Represents any value of the expression language */
Expand Down Expand Up @@ -76,6 +78,8 @@ static Value<?> of(Object value) {
return new ListValue(value);
} else if (value instanceof Map) {
return new MapValue(value);
} else if (value instanceof Set) {
return new SetValue(value);
} else if (value instanceof Value) {
return (Value<?>) value;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.datadog.debugger.el.values.NullValue;
import com.datadog.debugger.el.values.NumericValue;
import com.datadog.debugger.el.values.ObjectValue;
import com.datadog.debugger.el.values.SetValue;
import com.datadog.debugger.el.values.StringValue;

public interface Visitor<R> {
Expand Down Expand Up @@ -91,4 +92,6 @@ public interface Visitor<R> {
R visit(ListValue listValue);

R visit(MapValue mapValue);

R visit(SetValue setValue);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.datadog.debugger.el.values;

import com.datadog.debugger.el.Value;
import com.datadog.debugger.el.Visitor;
import com.datadog.debugger.el.expressions.ValueExpression;
import datadog.trace.bootstrap.debugger.el.ValueReferenceResolver;
import datadog.trace.bootstrap.debugger.el.Values;
import datadog.trace.bootstrap.debugger.util.WellKnownClasses;
import java.util.Set;

public class SetValue implements CollectionValue<Object>, ValueExpression<SetValue> {

private final Object setHolder;

public SetValue(Object object) {
if (object instanceof Set) {
setHolder = object;
} else if (object == null || object == Values.NULL_OBJECT) {
setHolder = Value.nullValue();
} else {
setHolder = Value.undefinedValue();
}
}

@Override
public SetValue evaluate(ValueReferenceResolver valueRefResolver) {
return this;
}

@Override
public Object getValue() {
return setHolder;
}

@Override
public boolean isEmpty() {
if (setHolder instanceof Set) {
return ((Set<?>) setHolder).isEmpty();
} else if (setHolder instanceof Value) {
Value<?> val = (Value<?>) setHolder;
return val.isNull() || val.isUndefined();
}
return true;
}

@Override
public int count() {
if (setHolder instanceof Set) {
if (WellKnownClasses.isSizeSafe((Set<?>) setHolder)) {
return ((Set<?>) setHolder).size();
}
throw new RuntimeException("Unsupported Set class: " + setHolder.getClass().getTypeName());
} else if (setHolder == Value.nullValue()) {
return 0;
}
return -1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why -1? why not undefined? is that in the spec ?
I think count on String also return -1 for null. not sure why we do that.

Copy link
Member Author

@jpbempel jpbempel May 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

legacy from the initial implementation. should never reached this code.

why not undefined?

because method should return an int! otherwise we need to throw an exception

}

@Override
public Value<?> get(Object key) {
if (key == Value.undefinedValue() || key == Values.UNDEFINED_OBJECT) {
return Value.undefinedValue();
}
if (key == null || key == Value.nullValue() || key == Values.NULL_OBJECT) {
return Value.nullValue();
}

if (setHolder instanceof Set) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we don't check WellKnownClasses here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same for Maps.
Need to revisit this in fact in a next PR

Set<?> set = (Set<?>) setHolder;
key = key instanceof Value ? ((Value<?>) key).getValue() : key;
return Value.of(set.contains(key));
}
// the result will be either Value.nullValue() or Value.undefinedValue() depending on the holder
// value
return (Value<?>) setHolder;
}

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

public Object getSetHolder() {
return setHolder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import okio.Okio;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -217,6 +219,7 @@ void testBooleanOperation() throws Exception {
fields.put("emptyStr", "");
fields.put("emptyList", new ArrayList<>());
fields.put("emptyMap", new HashMap<>());
fields.put("emptySet", new HashSet<>());
fields.put("emptyArray", new Object[0]);
ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null, fields);
assertTrue(probeCondition.execute(ctx));
Expand All @@ -237,6 +240,26 @@ void testLiterals() throws Exception {
assertTrue(probeCondition.execute(ctx));
}

@Test
void testLenCount() throws Exception {
ProbeCondition probeCondition = load("/test_conditional_14.json");
Map<String, Object> fields = new HashMap<>();
fields.put("intArray", new int[] {1, 1, 1});
fields.put("strArray", new String[] {"foo", "bar"});
Map<String, String> strMap = new HashMap<>();
strMap.put("foo", "bar");
strMap.put("bar", "foobar");
fields.put("strMap", strMap);
Set<String> strSet = new HashSet<>();
strSet.add("foo");
fields.put("strSet", strSet);
List<String> strList = new ArrayList<>();
strList.add("foo");
fields.put("strList", strList);
ValueReferenceResolver ctx = RefResolverHelper.createResolver(null, null, fields);
assertTrue(probeCondition.execute(ctx));
}

private static ProbeCondition load(String resourcePath) throws IOException {
InputStream input = ProbeConditionTest.class.getResourceAsStream(resourcePath);
Moshi moshi =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.datadog.debugger.el.values;

import static com.datadog.debugger.el.PrettyPrintVisitor.print;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.datadog.debugger.el.Value;
import datadog.trace.bootstrap.debugger.el.Values;
import java.util.Collections;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class SetValueEmptyTest {
private SetValue instance;

@BeforeEach
void setup() throws Exception {
instance = new SetValue(Collections.emptySet());
}

@Test
void prettyPrint() {
assertEquals("Set", print(instance));
}

@Test
void isEmpty() {
assertTrue(instance.isEmpty());
}

@Test
void count() {
assertEquals(0, instance.count());
}

@Test
void get() {
assertEquals(BooleanValue.FALSE, instance.get("a"));
assertEquals(BooleanValue.FALSE, instance.get("b"));
assertEquals(Value.undefinedValue(), instance.get(Values.UNDEFINED_OBJECT));
assertEquals(Value.undefinedValue(), instance.get(Value.undefinedValue()));
assertEquals(Value.nullValue(), instance.get(Values.NULL_OBJECT));
assertEquals(Value.nullValue(), instance.get(Value.nullValue()));
assertEquals(Value.nullValue(), instance.get(null));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.datadog.debugger.el.values;

import static com.datadog.debugger.el.PrettyPrintVisitor.print;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.datadog.debugger.el.Value;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class SetValueNullTest {
private SetValue instance;

@BeforeEach
void setup() throws Exception {
instance = new SetValue(null);
}

@Test
void prettyPrint() {
assertEquals("null", print(instance));
}

@Test
void isEmpty() {
assertTrue(instance.isEmpty());
}

@Test
void count() {
assertEquals(0, instance.count());
}

@Test
void get() {
assertEquals(Value.nullValue(), instance.get(0));
assertEquals(Value.nullValue(), instance.get(10));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.datadog.debugger.el.values;

import static com.datadog.debugger.el.PrettyPrintVisitor.print;
import static org.junit.jupiter.api.Assertions.*;

import com.datadog.debugger.el.Value;
import datadog.trace.bootstrap.debugger.el.Values;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class SetValueTest {
private SetValue instance;

@BeforeEach
void setup() throws Exception {
Set<String> set = new HashSet<>();
set.add("foo");
set.add("bar");
instance = new SetValue(set);
}

@Test
void prettyPrint() {
assertEquals("Set", print(instance));
}

@Test
void isEmpty() {
assertFalse(instance.isEmpty());
}

@Test
void count() {
assertEquals(2, instance.count());
}

@Test
void get() {
assertEquals(Value.of(true), instance.get("foo"));
assertEquals(BooleanValue.TRUE, instance.get("foo"));
assertEquals(Value.of(true), instance.get(Value.of("foo")));
assertEquals(Value.of(false), instance.get("oof"));
assertEquals(BooleanValue.FALSE, instance.get("oof"));
assertEquals(Value.of(false), instance.get(Value.of("oof")));
assertEquals(Value.undefinedValue(), instance.get(Values.UNDEFINED_OBJECT));
assertEquals(Value.undefinedValue(), instance.get(Value.undefinedValue()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
{"isEmpty": {"ref": "emptyStr"}},
{"isEmpty": {"ref": "emptyList"}},
{"isEmpty": {"ref": "emptyMap"}},
{"isEmpty": {"ref": "emptySet"}},
{"isEmpty": {"ref": "emptyArray"}},
{"not": {"isDefined": {"ref": "@exception"}}}
]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"dsl": "",
"json": {
"and": [
{"eq": [{"count": {"ref": "intArray"}}, 3]},
{"eq": [{"len": {"ref": "intArray"}}, 3]},
{"eq": [{"count": {"ref": "strArray"}}, 2]},
{"eq": [{"count": {"ref": "strMap"}}, 2]},
{"eq": [{"count": {"ref": "strSet"}}, 1]},
{"eq": [{"count": {"ref": "strList"}}, 1]}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import com.datadog.debugger.el.values.NullValue;
import com.datadog.debugger.el.values.NumericValue;
import com.datadog.debugger.el.values.ObjectValue;
import com.datadog.debugger.el.values.SetValue;
import com.datadog.debugger.el.values.StringValue;
import com.datadog.debugger.probe.MetricProbe;
import com.datadog.debugger.probe.Where;
Expand Down Expand Up @@ -658,6 +659,11 @@ public VisitorResult visit(MapValue mapValue) {
throw new UnsupportedOperationException();
}

@Override
public VisitorResult visit(SetValue setValue) {
throw new UnsupportedOperationException();
}

private ASMHelper.Type tryRetrieve(String name, InsnList insnList) {
ASMHelper.Type result = tryRetrieveArgument(name, insnList);
if (result != null) {
Expand Down
Loading