Skip to content

Commit f2e3d42

Browse files
committed
Avoid doing redundant work when checking for self references.
Currently we test all maps, arrays or iterables. However, in the case that maps contain sub maps for instance, we will test the sub maps again even though the work has already been done for the top-level map. Relates #26907
1 parent 023d08e commit f2e3d42

File tree

2 files changed

+25
-25
lines changed

2 files changed

+25
-25
lines changed

server/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -773,32 +773,23 @@ public XContentBuilder field(String name, Object value) throws IOException {
773773
}
774774

775775
public XContentBuilder array(String name, Object... values) throws IOException {
776-
return field(name).values(values);
776+
return field(name).values(values, true);
777777
}
778778

779-
XContentBuilder values(Object[] values) throws IOException {
779+
private XContentBuilder values(Object[] values, boolean ensureNoSelfReferences) throws IOException {
780780
if (values == null) {
781781
return nullValue();
782782
}
783783

784-
// checks that the array of object does not contain references to itself because
785-
// iterating over entries will cause a stackoverflow error
786-
ensureNoSelfReferences(values);
787-
788-
startArray();
789-
for (Object o : values) {
790-
value(o);
791-
}
792-
endArray();
793-
return this;
784+
return value(Arrays.asList(values), ensureNoSelfReferences);
794785
}
795786

796787
public XContentBuilder value(Object value) throws IOException {
797-
unknownValue(value);
788+
unknownValue(value, true);
798789
return this;
799790
}
800791

801-
private void unknownValue(Object value) throws IOException {
792+
private void unknownValue(Object value, boolean ensureNoSelfReferences) throws IOException {
802793
if (value == null) {
803794
nullValue();
804795
return;
@@ -810,11 +801,11 @@ private void unknownValue(Object value) throws IOException {
810801
//Path implements Iterable<Path> and causes endless recursion and a StackOverFlow if treated as an Iterable here
811802
value((Path) value);
812803
} else if (value instanceof Map) {
813-
map((Map) value);
804+
map((Map<String,?>) value, ensureNoSelfReferences);
814805
} else if (value instanceof Iterable) {
815-
value((Iterable<?>) value);
806+
value((Iterable<?>) value, ensureNoSelfReferences);
816807
} else if (value instanceof Object[]) {
817-
values((Object[]) value);
808+
values((Object[]) value, ensureNoSelfReferences);
818809
} else if (value instanceof Calendar) {
819810
value((Calendar) value);
820811
} else if (value instanceof ReadableInstant) {
@@ -863,18 +854,25 @@ public XContentBuilder field(String name, Map<String, Object> values) throws IOE
863854
}
864855

865856
public XContentBuilder map(Map<String, ?> values) throws IOException {
857+
return map(values, true);
858+
}
859+
860+
private XContentBuilder map(Map<String, ?> values, boolean ensureNoSelfReferences) throws IOException {
866861
if (values == null) {
867862
return nullValue();
868863
}
869864

870865
// checks that the map does not contain references to itself because
871866
// iterating over map entries will cause a stackoverflow error
872-
ensureNoSelfReferences(values);
867+
if (ensureNoSelfReferences) {
868+
ensureNoSelfReferences(values);
869+
}
873870

874871
startObject();
875872
for (Map.Entry<String, ?> value : values.entrySet()) {
876873
field(value.getKey());
877-
unknownValue(value.getValue());
874+
// pass ensureNoSelfReferences=false as we already performed the check at a higher level
875+
unknownValue(value.getValue(), false);
878876
}
879877
endObject();
880878
return this;
@@ -884,7 +882,7 @@ public XContentBuilder field(String name, Iterable<?> values) throws IOException
884882
return field(name).value(values);
885883
}
886884

887-
private XContentBuilder value(Iterable<?> values) throws IOException {
885+
private XContentBuilder value(Iterable<?> values, boolean ensureNoSelfReferences) throws IOException {
888886
if (values == null) {
889887
return nullValue();
890888
}
@@ -895,11 +893,14 @@ private XContentBuilder value(Iterable<?> values) throws IOException {
895893
} else {
896894
// checks that the iterable does not contain references to itself because
897895
// iterating over entries will cause a stackoverflow error
898-
ensureNoSelfReferences(values);
896+
if (ensureNoSelfReferences) {
897+
ensureNoSelfReferences(values);
898+
}
899899

900900
startArray();
901901
for (Object value : values) {
902-
unknownValue(value);
902+
// pass ensureNoSelfReferences=false as we already performed the check at a higher level
903+
unknownValue(value, false);
903904
}
904905
endArray();
905906
}
@@ -1076,9 +1077,9 @@ private static void ensureNoSelfReferences(final Object value, final Set<Object>
10761077

10771078
Iterable<?> it;
10781079
if (value instanceof Map) {
1079-
it = ((Map) value).values();
1080+
it = ((Map<?,?>) value).values();
10801081
} else if ((value instanceof Iterable) && (value instanceof Path == false)) {
1081-
it = (Iterable) value;
1082+
it = (Iterable<?>) value;
10821083
} else if (value instanceof Object[]) {
10831084
it = Arrays.asList((Object[]) value);
10841085
} else {

server/src/test/java/org/elasticsearch/common/xcontent/BaseXContentTestCase.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,6 @@ public void testObjects() throws Exception {
534534
final String expected = o.getKey();
535535
assertResult(expected, () -> builder().startObject().field("objects", o.getValue()).endObject());
536536
assertResult(expected, () -> builder().startObject().field("objects").value(o.getValue()).endObject());
537-
assertResult(expected, () -> builder().startObject().field("objects").values(o.getValue()).endObject());
538537
assertResult(expected, () -> builder().startObject().array("objects", o.getValue()).endObject());
539538
}
540539
}

0 commit comments

Comments
 (0)