mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-04 17:59:35 +01:00
Add the ability to write to final fields, even if it is compiled.
This commit is contained in:
parent
76d27017de
commit
0b292af3b1
@ -30,7 +30,6 @@ import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.PrettyPrinter;
|
||||
import com.comphenix.protocol.utility.ChatExtensions;
|
||||
import com.google.common.collect.DiscreteDomains;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.Ranges;
|
||||
import com.google.common.collect.Sets;
|
||||
|
@ -66,8 +66,10 @@ public class ObjectCloner {
|
||||
// Copy every field
|
||||
try {
|
||||
for (int i = 0; i < modifierSource.size(); i++) {
|
||||
Object value = modifierSource.read(i);
|
||||
modifierDest.write(i, value);
|
||||
if (!modifierDest.isReadOnly(i)) {
|
||||
Object value = modifierSource.read(i);
|
||||
modifierDest.write(i, value);
|
||||
}
|
||||
|
||||
// System.out.println(String.format("Writing value %s to %s",
|
||||
// value, modifier.getFields().get(i).getName()));
|
||||
|
@ -61,6 +61,17 @@ public class StructureModifier<TField> {
|
||||
// Whether or subclasses should handle conversion
|
||||
protected boolean customConvertHandling;
|
||||
|
||||
// Whether or not to automatically compile the structure modifier
|
||||
protected boolean useStructureCompiler;
|
||||
|
||||
/**
|
||||
* Creates a structure modifier.
|
||||
* @param targetType - the structure to modify.
|
||||
*/
|
||||
public StructureModifier(Class targetType) {
|
||||
this(targetType, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a structure modifier.
|
||||
* @param targetType - the structure to modify.
|
||||
@ -68,10 +79,22 @@ public class StructureModifier<TField> {
|
||||
* @param requireDefault - whether or not we will be using writeDefaults().
|
||||
*/
|
||||
public StructureModifier(Class targetType, Class superclassExclude, boolean requireDefault) {
|
||||
this(targetType, superclassExclude, requireDefault, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a structure modifier.
|
||||
* @param targetType - the structure to modify.
|
||||
* @param superclassExclude - a superclass to exclude.
|
||||
* @param requireDefault - whether or not we will be using writeDefaults().
|
||||
* @param useStructureModifier - whether or not to automatically compile this structure modifier.
|
||||
*/
|
||||
public StructureModifier(Class targetType, Class superclassExclude, boolean requireDefault, boolean useStructureCompiler) {
|
||||
List<Field> fields = getFields(targetType, superclassExclude);
|
||||
Map<Field, Integer> defaults = requireDefault ? generateDefaultFields(fields) : new HashMap<Field, Integer>();
|
||||
|
||||
initialize(targetType, Object.class, fields, defaults, null, new ConcurrentHashMap<Class, StructureModifier>());
|
||||
initialize(targetType, Object.class, fields, defaults, null,
|
||||
new ConcurrentHashMap<Class, StructureModifier>(), useStructureCompiler);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,7 +110,8 @@ public class StructureModifier<TField> {
|
||||
*/
|
||||
protected void initialize(StructureModifier<TField> other) {
|
||||
initialize(other.targetType, other.fieldType, other.data,
|
||||
other.defaultFields, other.converter, other.subtypeCache);
|
||||
other.defaultFields, other.converter, other.subtypeCache,
|
||||
other.useStructureCompiler);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,12 +126,31 @@ public class StructureModifier<TField> {
|
||||
protected void initialize(Class targetType, Class fieldType,
|
||||
List<Field> data, Map<Field, Integer> defaultFields,
|
||||
EquivalentConverter<TField> converter, Map<Class, StructureModifier> subTypeCache) {
|
||||
initialize(targetType, fieldType, data, defaultFields, converter, subTypeCache, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize every field of this class.
|
||||
* @param targetType - type of the object we're reading and writing from.
|
||||
* @param fieldType - the common type of the fields we're modifying.
|
||||
* @param data - list of fields to modify.
|
||||
* @param defaultFields - list of fields that will be automatically initialized.
|
||||
* @param converter - converts between the common field type and the actual type the consumer expects.
|
||||
* @param subTypeCache - a structure modifier cache.
|
||||
* @param useStructureModifier - whether or not to automatically compile this structure modifier.
|
||||
*/
|
||||
protected void initialize(Class targetType, Class fieldType,
|
||||
List<Field> data, Map<Field, Integer> defaultFields,
|
||||
EquivalentConverter<TField> converter, Map<Class, StructureModifier> subTypeCache,
|
||||
boolean useStructureCompiler) {
|
||||
|
||||
this.targetType = targetType;
|
||||
this.fieldType = fieldType;
|
||||
this.data = data;
|
||||
this.defaultFields = defaultFields;
|
||||
this.converter = converter;
|
||||
this.subtypeCache = subTypeCache;
|
||||
this.useStructureCompiler = useStructureCompiler;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -164,6 +207,40 @@ public class StructureModifier<TField> {
|
||||
return Modifier.isFinal(data.get(fieldIndex).getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether or not a field should be treated as read only.
|
||||
* <p>
|
||||
* Note that changing the read-only state to TRUE will only work if the current
|
||||
* field was recently read-only or the current structure modifier hasn't been compiled yet.
|
||||
*
|
||||
* @param fieldIndex - index of the field.
|
||||
* @param value - TRUE if this field should be read only, FALSE otherwise.
|
||||
* @throws FieldAccessException If we cannot modify the read-only status.
|
||||
*/
|
||||
public void setReadOnly(int fieldIndex, boolean value) throws FieldAccessException {
|
||||
if (fieldIndex < 0 || fieldIndex >= data.size())
|
||||
new IllegalArgumentException("Index parameter is not within [0 - " + data.size() + ")");
|
||||
|
||||
try {
|
||||
StructureModifier.setFinalState(data.get(fieldIndex), value);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new FieldAccessException("Cannot write read only status due to a security limitation.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the final status of a field.
|
||||
* @param field - the field to change.
|
||||
* @param isReadOnly - TRUE if the field should be read only, FALSE otherwise.
|
||||
* @throws IllegalAccessException If an error occured.
|
||||
*/
|
||||
protected static void setFinalState(Field field, boolean isReadOnly) throws IllegalAccessException {
|
||||
if (isReadOnly)
|
||||
FieldUtils.writeField((Object) field, "modifiers", field.getModifiers() | Modifier.FINAL, true);
|
||||
else
|
||||
FieldUtils.writeField((Object) field, "modifiers", field.getModifiers() & ~Modifier.FINAL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the value of a field given its index.
|
||||
* @param fieldIndex - index of the field.
|
||||
@ -293,7 +370,7 @@ public class StructureModifier<TField> {
|
||||
subtypeCache.put(fieldType, result);
|
||||
|
||||
// Automatically compile the structure modifier
|
||||
if (BackgroundCompiler.getInstance() != null)
|
||||
if (useStructureCompiler && BackgroundCompiler.getInstance() != null)
|
||||
BackgroundCompiler.getInstance().scheduleCompilation(subtypeCache, fieldType);
|
||||
}
|
||||
}
|
||||
@ -365,7 +442,8 @@ public class StructureModifier<TField> {
|
||||
|
||||
StructureModifier<T> result = new StructureModifier<T>();
|
||||
result.initialize(targetType, fieldType, filtered, defaults,
|
||||
converter, new ConcurrentHashMap<Class, StructureModifier>());
|
||||
converter, new ConcurrentHashMap<Class, StructureModifier>(),
|
||||
useStructureCompiler);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -378,7 +456,7 @@ public class StructureModifier<TField> {
|
||||
StructureModifier<TField> copy = new StructureModifier<TField>();
|
||||
|
||||
// Create a new instance
|
||||
copy.initialize(targetType, fieldType, data, defaultFields, converter, subtypeCache);
|
||||
copy.initialize(this);
|
||||
copy.target = target;
|
||||
return copy;
|
||||
}
|
||||
|
@ -19,10 +19,12 @@ package com.comphenix.protocol.reflect.compiler;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.comphenix.protocol.reflect.FieldAccessException;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.instances.DefaultInstances;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
* Represents a compiled structure modifier.
|
||||
@ -34,11 +36,33 @@ public abstract class CompiledStructureModifier<TField> extends StructureModifie
|
||||
// Used to compile instances of structure modifiers
|
||||
protected StructureCompiler compiler;
|
||||
|
||||
// Fields that originally were read only
|
||||
private Set<Integer> exempted;
|
||||
|
||||
public CompiledStructureModifier() {
|
||||
super();
|
||||
customConvertHandling = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(int fieldIndex, boolean value) throws FieldAccessException {
|
||||
// We can remove the read-only status
|
||||
if (isReadOnly(fieldIndex) && !value) {
|
||||
if (exempted == null)
|
||||
exempted = Sets.newHashSet();
|
||||
exempted.add(fieldIndex);
|
||||
}
|
||||
|
||||
// We can only make a certain kind of field read only
|
||||
if (!isReadOnly(fieldIndex) && value) {
|
||||
if (exempted == null || !exempted.contains(fieldIndex)) {
|
||||
throw new IllegalStateException("Cannot make compiled field " + fieldIndex + " read only.");
|
||||
}
|
||||
}
|
||||
|
||||
super.setReadOnly(fieldIndex, value);
|
||||
}
|
||||
|
||||
// Speed up the default writer
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user