Skip to content

Commit

Permalink
Serialize restricted collections as regular object
Browse files Browse the repository at this point in the history
We specially serialize collections/maps inot snapshot using interfaces
and calling methods like size() but we restrict this for JDK
collections that are guarantee to be only in-memory.
For all others we are raising an exception. Instead we should just
serialize then as regular object with fields.
  • Loading branch information
jpbempel committed Jul 3, 2024
1 parent 179ce68 commit 95b376e
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,17 @@ public void serialize(Object value, String type, Limits limits) throws Exception
} else if (value.getClass().isArray() && (limits.maxReferenceDepth > 0)) {
serializeArray(value, limits);
} else if (value instanceof Collection && (limits.maxReferenceDepth > 0)) {
serializeCollection(value, limits);
if (WellKnownClasses.isSafe((Collection<?>) value)) {
serializeCollection(value, limits);
} else {
serializeObjectValue(value, limits);
}
} else if (value instanceof Map && (limits.maxReferenceDepth > 0)) {
serializeMap(value, limits);
if (WellKnownClasses.isSafe((Map<?, ?>) value)) {
serializeMap(value, limits);
} else {
serializeObjectValue(value, limits);
}
} else if (value instanceof Enum) {
serializeEnum(value, limits);
} else if (limits.maxReferenceDepth > 0) {
Expand All @@ -170,13 +178,9 @@ private void serializeMap(Object value, Limits limits) throws Exception {
int size = 0;
try {
map = (Map<?, ?>) value;
if (WellKnownClasses.isSafe(map)) {
size = map.size(); // /!\ alien call /!\
Set<? extends Map.Entry<?, ?>> entries = map.entrySet(); // /!\ alien call /!\
isComplete = serializeMapEntries(entries, limits); // /!\ contains alien calls /!\
} else {
throw new RuntimeException("Unsupported Map type: " + map.getClass().getTypeName());
}
size = map.size(); // /!\ alien call /!\
Set<? extends Map.Entry<?, ?>> entries = map.entrySet(); // /!\ alien call /!\
isComplete = serializeMapEntries(entries, limits); // /!\ contains alien calls /!\
tokenWriter.mapEpilogue(isComplete, size);
} catch (Exception ex) {
tokenWriter.mapEpilogue(isComplete, size);
Expand All @@ -191,12 +195,8 @@ private void serializeCollection(Object value, Limits limits) throws Exception {
int size = 0;
try {
col = (Collection<?>) value;
if (WellKnownClasses.isSafe(col)) {
size = col.size(); // /!\ alien call /!\
isComplete = serializeCollection(col, limits); // /!\ contains alien calls /!\
} else {
throw new RuntimeException("Unsupported Collection type: " + col.getClass().getTypeName());
}
size = col.size(); // /!\ alien call /!\
isComplete = serializeCollection(col, limits); // /!\ contains alien calls /!\
tokenWriter.collectionEpilogue(value, isComplete, size);
} catch (Exception ex) {
tokenWriter.collectionEpilogue(value, isComplete, size);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -744,10 +744,11 @@ public void collectionUnknown() throws IOException {
String buffer = adapter.toJson(snapshot);
System.out.println(buffer);
Map<String, Object> locals = getLocalsFromJson(buffer);
Map<String, Object> mapFieldObj = (Map<String, Object>) locals.get("listLocal");
Assertions.assertEquals(
"java.lang.RuntimeException: Unsupported Collection type: com.datadog.debugger.agent.SnapshotSerializationTest$1",
mapFieldObj.get(NOT_CAPTURED_REASON));
Map<String, Object> listLocalField = (Map<String, Object>) locals.get("listLocal");
Map<String, Object> listLocalFieldFields = (Map<String, Object>) listLocalField.get(FIELDS);
assertTrue(listLocalFieldFields.containsKey("elementData"));
assertTrue(listLocalFieldFields.containsKey("size"));
assertTrue(listLocalFieldFields.containsKey("modCount"));
}

@Test
Expand All @@ -763,10 +764,12 @@ public void mapValueUnknown() throws IOException {
String buffer = adapter.toJson(snapshot);
System.out.println(buffer);
Map<String, Object> locals = getLocalsFromJson(buffer);
Map<String, Object> mapFieldObj = (Map<String, Object>) locals.get("mapLocal");
Assertions.assertEquals(
"java.lang.RuntimeException: Unsupported Map type: com.datadog.debugger.agent.SnapshotSerializationTest$2",
mapFieldObj.get(NOT_CAPTURED_REASON));
Map<String, Object> mapLocalField = (Map<String, Object>) locals.get("mapLocal");
Map<String, Object> mapLocalFieldFields = (Map<String, Object>) mapLocalField.get(FIELDS);
assertTrue(mapLocalFieldFields.containsKey("table"));
assertTrue(mapLocalFieldFields.containsKey("size"));
assertTrue(mapLocalFieldFields.containsKey("threshold"));
assertTrue(mapLocalFieldFields.containsKey("loadFactor"));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,25 +91,26 @@ public void collections() throws Exception {
list.add("foo");
list.add("bar");
list.add(null);
assertEquals("[foo, bar, null]", serializeValue(list, Limits.DEFAULT));
assertEquals("[foo, bar, null]", serializeValue(list, DEPTH_1));
}

@Test
public void maps() throws Exception {
HashMap<String, String> map = new HashMap<>();
map.put("foo1", "bar1");
map.put(null, null);
String serializedStr = serializeValue(map, Limits.DEFAULT);
String serializedStr = serializeValue(map, DEPTH_1);
assertTrue(serializedStr.contains("[foo1=bar1]"));
assertTrue(serializedStr.contains("[null=null]"));
}

@Test
public void collectionUnknown() throws Exception {
class MyArrayList<T> extends ArrayList<T> {}
assertEquals(
"[](Error: java.lang.RuntimeException: Unsupported Collection type: com.datadog.debugger.util.StringTokenWriterTest$1MyArrayList)",
serializeValue(new MyArrayList<>(), Limits.DEFAULT));
String str = serializeValue(new MyArrayList<>(), DEPTH_1);
assertTrue(str.contains("elementData="));
assertTrue(str.contains("size="));
assertTrue(str.contains("modCount="));
}

@Test
Expand All @@ -120,9 +121,12 @@ public Set<Entry<K, V>> entrySet() {
throw new UnsupportedOperationException("entrySet");
}
}
assertEquals(
"{}(Error: java.lang.RuntimeException: Unsupported Map type: com.datadog.debugger.util.StringTokenWriterTest$1MyMap)",
serializeValue(new MyMap<String, String>(), Limits.DEFAULT));
String str = serializeValue(new MyMap<String, String>(), DEPTH_1);
assertTrue(str.contains("table="));
assertTrue(str.contains("size="));
assertTrue(str.contains("modCount="));
assertTrue(str.contains("threshold="));
assertTrue(str.contains("loadFactor="));
}

static class Person {
Expand Down Expand Up @@ -171,7 +175,7 @@ private String serializeValue(Object value, Limits limits) throws Exception {
SerializerWithLimits serializer =
new SerializerWithLimits(
new StringTokenWriter(sb, new ArrayList<>()),
new TimeoutChecker(Duration.of(1, ChronoUnit.SECONDS)));
new TimeoutChecker(Duration.of(300, ChronoUnit.SECONDS)));
serializer.serialize(
value, value != null ? value.getClass().getTypeName() : Object.class.getTypeName(), limits);
return sb.toString();
Expand Down

0 comments on commit 95b376e

Please sign in to comment.