Skip to content

Commit

Permalink
Defer reflective access to Java core modules. Closes x-stream#216.
Browse files Browse the repository at this point in the history
  • Loading branch information
famod authored and joehni committed Aug 3, 2020
1 parent 470f1cb commit 3cec78d
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 154 deletions.
1 change: 1 addition & 0 deletions xstream-distribution/src/content/changes.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ <h2>Major changes</h2>
<li>Support for fail-safe deserialization unless the parser is not directly involved (I/O or syntax error) as
combination of new method getLevel() of HierarchicalStreamReader and GHPR:#91.</li>
<li>GHI:#120: Add <em>Automatic-Module-Name</em> entry to XStream's manifests.</li>
<li>GHPR:#216: Defer reflective access to Java core modules.</li>
</ul>

<h2>Minor changes</h2>
Expand Down
3 changes: 2 additions & 1 deletion xstream-distribution/src/content/team.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<html>
<!--
Copyright (C) 2005, 2006 Joe Walnes.
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016, 2017, 2019 XStream committers.
Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2013, 2014, 2015, 2016, 2017, 2019, 2020 XStream committers.
All rights reserved.
The software in this package is published under the terms of the BSD
Expand Down Expand Up @@ -127,6 +127,7 @@ <h2 id="contributors">Contributors</h2>
<li>Matej Cimbora</li>
<li>Baptiste Mesta</li>
<li>Wes Wannemacher</li>
<li>Falko Modler</li>
</ul>

<p>Please direct all correspondence about XStream to the <a href="mailing-lists.html">mailing list</a>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/*
* Copyright (C) 2004, 2005 Joe Walnes.
* Copyright (C) 2006, 2007, 2008, 2009, 2013, 2014, 2015 XStream Committers.
* Copyright (C) 2006, 2007, 2008, 2009, 2013, 2014, 2015, 2020 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 23. February 2004 by Joe Walnes
*/
package com.thoughtworks.xstream.converters.collections;
Expand Down Expand Up @@ -35,13 +35,12 @@
* permissions for SecurityManager.checkPackageAccess, SecurityManager.checkMemberAccess(this, EnumSet.MEMBER) and
* ReflectPermission("suppressAccessChecks").
* </p>
*
*
* @author Joe Walnes
* @author Kevin Ring
*/
public class PropertiesConverter implements Converter {

private final static Field defaultsField = Fields.locate(Properties.class, Properties.class, false);
private final boolean sort;

public PropertiesConverter() {
Expand All @@ -67,8 +66,8 @@ public void marshal(final Object source, final HierarchicalStreamWriter writer,
writer.addAttribute("value", entry.getValue().toString());
writer.endNode();
}
if (defaultsField != null) {
final Properties defaults = (Properties)Fields.read(defaultsField, properties);
if (Reflections.defaultsField != null) {
final Properties defaults = (Properties)Fields.read(Reflections.defaultsField, properties);
if (defaults != null) {
writer.startNode("defaults");
marshal(defaults, writer, context);
Expand Down Expand Up @@ -101,4 +100,7 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
}
}

private static class Reflections {
private final static Field defaultsField = Fields.locate(Properties.class, Properties.class, false);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2004, 2005 Joe Walnes.
* Copyright (C) 2006, 2007, 2010, 2011, 2013, 2014, 2015, 2016, 2018 XStream Committers.
* Copyright (C) 2006, 2007, 2010, 2011, 2013, 2014, 2015, 2016, 2018, 2020 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
Expand Down Expand Up @@ -49,7 +49,6 @@ public int compare(final Comparable o1, final Comparable o2) {

@SuppressWarnings("rawtypes")
private final static Comparator NULL_MARKER = new NullComparator();
private final static Field comparatorField = Fields.locate(TreeMap.class, Comparator.class, false);

public TreeMapConverter(final Mapper mapper) {
super(mapper, TreeMap.class);
Expand All @@ -66,16 +65,17 @@ protected void marshalComparator(final Comparator<?> comparator, final Hierarchi
final MarshallingContext context) {
if (comparator != null) {
writer.startNode("comparator");
writer.addAttribute(mapper().aliasForSystemAttribute("class"), mapper().serializedClass(comparator
.getClass()));
writer
.addAttribute(mapper().aliasForSystemAttribute("class"), mapper()
.serializedClass(comparator.getClass()));
context.convertAnother(comparator);
writer.endNode();
}
}

@Override
public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) {
TreeMap<Object, Object> result = comparatorField != null ? new TreeMap<>() : null;
TreeMap<Object, Object> result = Reflections.comparatorField != null ? new TreeMap<>() : null;
@SuppressWarnings("unchecked")
final Comparator<Object> comparator = (Comparator<Object>)unmarshalComparator(reader, context, result);
if (result == null) {
Expand Down Expand Up @@ -125,19 +125,23 @@ protected void populateTreeMap(final HierarchicalStreamReader reader, final Unma
final TreeMap<Object, Object> typedResult = (TreeMap<Object, Object>)result;
try {
if (JVM.hasOptimizedTreeMapPutAll()) {
if (comparator != null && comparatorField != null) {
comparatorField.set(result, comparator);
if (comparator != null && Reflections.comparatorField != null) {
Reflections.comparatorField.set(result, comparator);
}
typedResult.putAll(sortedMap); // internal optimization will not call comparator
} else if (comparatorField != null) {
comparatorField.set(result, sortedMap.comparator());
} else if (Reflections.comparatorField != null) {
Reflections.comparatorField.set(result, sortedMap.comparator());
typedResult.putAll(sortedMap); // "sort" by index
comparatorField.set(result, comparator);
Reflections.comparatorField.set(result, comparator);
} else {
typedResult.putAll(sortedMap); // will use comparator for already sorted map
}
} catch (final IllegalAccessException e) {
throw new ObjectAccessException("Cannot set comparator of TreeMap", e);
}
}

private static class Reflections {
private final static Field comparatorField = Fields.locate(TreeMap.class, Comparator.class, false);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2004, 2005 Joe Walnes.
* Copyright (C) 2006, 2007, 2010, 2011, 2013, 2014, 2015, 2016, 2018 XStream Committers.
* Copyright (C) 2006, 2007, 2010, 2011, 2013, 2014, 2015, 2016, 2018, 2020 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
Expand Down Expand Up @@ -42,43 +42,6 @@
*/
public class TreeSetConverter extends CollectionConverter {
private transient TreeMapConverter treeMapConverter;
private final static Field sortedMapField;
private final static Object constantValue;

static {
Object value = null;
sortedMapField = JVM.hasOptimizedTreeSetAddAll() ? Fields.locate(TreeSet.class, SortedMap.class, false) : null;
if (sortedMapField != null) {
final TreeSet<String> set = new TreeSet<>();
set.add("1");
set.add("2");

Map<String, Object> backingMap = null;
try {
@SuppressWarnings("unchecked")
final Map<String, Object> map = (Map<String, Object>)sortedMapField.get(set);
backingMap = map;
} catch (final IllegalAccessException e) {
// give up;
}
if (backingMap != null) {
final Object[] values = backingMap.values().toArray();
if (values[0] == values[1]) {
value = values[0];
}
}
} else {
final Field valueField = Fields.locate(TreeSet.class, Object.class, true);
if (valueField != null) {
try {
value = valueField.get(null);
} catch (final IllegalAccessException e) {
// give up;
}
}
}
constantValue = value;
}

public TreeSetConverter(final Mapper mapper) {
super(mapper, TreeSet.class);
Expand All @@ -100,11 +63,11 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
final boolean inFirstElement = unmarshalledComparator instanceof Mapper.Null;
@SuppressWarnings("unchecked")
final Comparator<Object> comparator = inFirstElement ? null : (Comparator<Object>)unmarshalledComparator;
if (sortedMapField != null) {
if (Reflections.sortedMapField != null) {
final TreeSet<Object> possibleResult = comparator == null ? new TreeSet<>() : new TreeSet<>(comparator);
Object backingMap = null;
try {
backingMap = sortedMapField.get(possibleResult);
backingMap = Reflections.sortedMapField.get(possibleResult);
} catch (final IllegalAccessException e) {
throw new ObjectAccessException("Cannot get backing map of TreeSet", e);
}
Expand Down Expand Up @@ -146,7 +109,10 @@ protected void populateMap(final HierarchicalStreamReader reader, final Unmarsha
public boolean add(final Object object) {
@SuppressWarnings("unchecked")
final Map<Object, Object> collectionTarget = (Map<Object, Object>)target;
return collectionTarget.put(object, constantValue != null ? constantValue : object) != null;
return collectionTarget
.put(object, Reflections.constantValue != null
? Reflections.constantValue
: object) != null;
}

@Override
Expand All @@ -164,13 +130,57 @@ public int size() {
@Override
protected void putCurrentEntryIntoMap(final HierarchicalStreamReader reader,
final UnmarshallingContext context, final Map<?, ?> map, final Map<?, ?> target) {
// call readBareItem when deprecated method is removed
@SuppressWarnings("deprecation")
final Object key = readItem(reader, context, map); // call readBareItem when deprecated method is removed
final Object key = readItem(reader, context, map);
@SuppressWarnings("unchecked")
final Map<Object, Object> checkedTarget = (Map<Object, Object>)target;
checkedTarget.put(key, key);
}
};
return this;
}

private static class Reflections {

private final static Field sortedMapField;
private final static Object constantValue;

static {
Object value = null;
sortedMapField = JVM.hasOptimizedTreeSetAddAll()
? Fields.locate(TreeSet.class, SortedMap.class, false)
: null;
if (sortedMapField != null) {
final TreeSet<String> set = new TreeSet<>();
set.add("1");
set.add("2");

Map<String, Object> backingMap = null;
try {
@SuppressWarnings("unchecked")
final Map<String, Object> map = (Map<String, Object>)sortedMapField.get(set);
backingMap = map;
} catch (final IllegalAccessException e) {
// give up;
}
if (backingMap != null) {
final Object[] values = backingMap.values().toArray();
if (values[0] == values[1]) {
value = values[0];
}
}
} else {
final Field valueField = Fields.locate(TreeSet.class, Object.class, true);
if (valueField != null) {
try {
value = valueField.get(null);
} catch (final IllegalAccessException e) {
// give up;
}
}
}
constantValue = value;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/*
* Copyright (C) 2005 Joe Walnes.
* Copyright (C) 2006, 2007, 2008, 2009, 2013, 2014 XStream Committers.
* Copyright (C) 2006, 2007, 2008, 2009, 2013, 2014, 2020 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 06. April 2005 by Joe Walnes
*/

Expand All @@ -28,28 +28,27 @@
/**
* Converts an {@link EnumMap}, including the type of Enum it's for.
* <p>
* If a {@link SecurityManager} is set, the converter will only work with permissions for SecurityManager.checkPackageAccess,
* SecurityManager.checkMemberAccess(this, EnumSet.MEMBER) and ReflectPermission("suppressAccessChecks").
* If a {@link SecurityManager} is set, the converter will only work with permissions for
* SecurityManager.checkPackageAccess, SecurityManager.checkMemberAccess(this, EnumSet.MEMBER) and
* ReflectPermission("suppressAccessChecks").
* </p>
*
*
* @author Joe Walnes
*/
public class EnumMapConverter extends MapConverter {

private final static Field typeField = Fields.locate(EnumMap.class, Class.class, false);

public EnumMapConverter(final Mapper mapper) {
super(mapper);
}

@Override
public boolean canConvert(final Class<?> type) {
return typeField != null && type == EnumMap.class;
return type == EnumMap.class && Reflections.typeField != null;
}

@Override
public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) {
final Class<?> type = (Class<?>)Fields.read(typeField, source);
final Class<?> type = (Class<?>)Fields.read(Reflections.typeField, source);
final String attributeName = mapper().aliasForSystemAttribute("enum-type");
if (attributeName != null) {
writer.addAttribute(attributeName, mapper().serializedClass(type));
Expand All @@ -69,4 +68,8 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
populateMap(reader, context, map);
return map;
}

private static class Reflections {
private final static Field typeField = Fields.locate(EnumMap.class, Class.class, false);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/*
* Copyright (C) 2005 Joe Walnes.
* Copyright (C) 2006, 2007, 2008, 2009, 2014, 2018 XStream Committers.
* Copyright (C) 2006, 2007, 2008, 2009, 2014, 2018, 2020 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 06. April 2005 by Joe Walnes
*/

Expand All @@ -31,13 +31,12 @@
* If a SecurityManager is set, the converter will only work with permissions for SecurityManager.checkPackageAccess,
* SecurityManager.checkMemberAccess(this, EnumSet.MEMBER) and ReflectPermission("suppressAccessChecks").
* </p>
*
*
* @author Joe Walnes
* @author J&ouml;rg Schaible
*/
public class EnumSetConverter implements Converter {

private final static Field typeField = Fields.locate(EnumSet.class, Class.class, false);
private final Mapper mapper;

public EnumSetConverter(final Mapper mapper) {
Expand All @@ -46,13 +45,13 @@ public EnumSetConverter(final Mapper mapper) {

@Override
public boolean canConvert(final Class<?> type) {
return typeField != null && type != null && EnumSet.class.isAssignableFrom(type);
return type != null && EnumSet.class.isAssignableFrom(type) && Reflections.typeField != null;
}

@Override
public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) {
final EnumSet<?> set = (EnumSet<?>)source;
final Class<?> enumTypeForSet = (Class<?>)Fields.read(typeField, set);
final Class<?> enumTypeForSet = (Class<?>)Fields.read(Reflections.typeField, set);
final String attributeName = mapper.aliasForSystemAttribute("enum-type");
if (attributeName != null) {
writer.addAttribute(attributeName, mapper.serializedClass(enumTypeForSet));
Expand Down Expand Up @@ -99,4 +98,7 @@ private <T extends Enum<T>> EnumSet<T> create(final Class<T> type, final String
return set;
}

private static class Reflections {
private final static Field typeField = Fields.locate(EnumSet.class, Class.class, false);
}
}
Loading

0 comments on commit 3cec78d

Please sign in to comment.