Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: prevent serialisation of private component fields #5208

Merged
merged 11 commits into from
Mar 3, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

package org.terasology.engine.persistence.typeHandling;

import com.google.common.collect.Maps;
import org.joml.Quaternionf;
import org.joml.Quaternionfc;
import org.joml.Vector2f;
Expand Down Expand Up @@ -31,6 +32,7 @@
import org.terasology.engine.persistence.typeHandling.extensionTypes.UITextureRegionTypeHandler;
import org.terasology.engine.persistence.typeHandling.extensionTypes.factories.AssetTypeHandlerFactory;
import org.terasology.engine.persistence.typeHandling.extensionTypes.factories.ComponentClassTypeHandlerFactory;
import org.terasology.engine.persistence.typeHandling.extensionTypes.factories.ComponentTypeHandlerFactory;
import org.terasology.engine.persistence.typeHandling.extensionTypes.factories.TextureRegionAssetTypeHandlerFactory;
import org.terasology.engine.persistence.typeHandling.mathTypes.AABBfTypeHandler;
import org.terasology.engine.persistence.typeHandling.mathTypes.AABBiTypeHandler;
Expand Down Expand Up @@ -72,6 +74,7 @@
import org.terasology.persistence.typeHandling.TypeHandlerLibrary;
import org.terasology.persistence.typeHandling.reflection.SerializationSandbox;
import org.terasology.reflection.TypeRegistry;
import org.terasology.reflection.reflect.ConstructorLibrary;

/**
* A library of type handlers. This is used for the construction of class metadata. This library should be initialised
Expand Down Expand Up @@ -159,6 +162,8 @@ private static void populateWithDefaultHandlers(TypeHandlerLibrary serialization
serializationLibrary.addTypeHandler(Quaternionfc.class, new QuaternionfcTypeHandler());

serializationLibrary.addTypeHandler(IntegerRange.class, new IntegerRangeHandler());

serializationLibrary.addTypeHandlerFactory(new ComponentTypeHandlerFactory(new ConstructorLibrary(Maps.newHashMap())));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright 2024 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0

package org.terasology.engine.persistence.typeHandling.extensionTypes.factories;

import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.terasology.gestalt.entitysystem.component.Component;
import org.terasology.persistence.typeHandling.TypeHandler;
import org.terasology.persistence.typeHandling.TypeHandlerContext;
import org.terasology.persistence.typeHandling.TypeHandlerFactory;
import org.terasology.persistence.typeHandling.coreTypes.ObjectFieldMapTypeHandler;
import org.terasology.persistence.typeHandling.coreTypes.RuntimeDelegatingTypeHandler;
import org.terasology.reflection.ReflectionUtil;
import org.terasology.reflection.TypeInfo;
import org.terasology.reflection.reflect.ConstructorLibrary;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.Optional;

/**
* A {@link TypeHandlerFactory} for serialising component types.
* It is similar to {@link org.terasology.persistence.typeHandling.coreTypes.factories.ObjectFieldMapTypeHandlerFactory}
* but does not allow serialising private fields.
*/
public class ComponentTypeHandlerFactory implements TypeHandlerFactory {
private Logger logger = LoggerFactory.getLogger(ComponentTypeHandlerFactory.class);

private ConstructorLibrary constructorLibrary;

public ComponentTypeHandlerFactory(ConstructorLibrary constructorLibrary) {
this.constructorLibrary = constructorLibrary;
}

@Override
public <T> Optional<TypeHandler<T>> create(TypeInfo<T> typeInfo, TypeHandlerContext context) {
Class<? super T> typeClass = typeInfo.getRawType();
if (!Component.class.isAssignableFrom(typeClass)) {
return Optional.empty();
}

if (!Modifier.isAbstract(typeClass.getModifiers())
&& !typeClass.isLocalClass()
&& !(typeClass.isMemberClass() && !Modifier.isStatic(typeClass.getModifiers()))) {
Map<Field, TypeHandler<?>> fieldTypeHandlerMap = Maps.newLinkedHashMap();

getResolvedFields(typeInfo).forEach(
(field, fieldType) -> {
Optional<TypeHandler<?>> declaredFieldTypeHandler =
context.getTypeHandlerLibrary().getTypeHandler(fieldType);

TypeInfo<?> fieldTypeInfo = TypeInfo.of(fieldType);

fieldTypeHandlerMap.put(
field,
new RuntimeDelegatingTypeHandler(
declaredFieldTypeHandler.orElse(null),
fieldTypeInfo,
context
)
);
}
);

ObjectFieldMapTypeHandler<T> mappedHandler =
new ObjectFieldMapTypeHandler<>(constructorLibrary.get(typeInfo), fieldTypeHandlerMap);

return Optional.of(mappedHandler);
}

return Optional.empty();
}

private <T> Map<Field, Type> getResolvedFields(TypeInfo<T> typeInfo) {
return AccessController.doPrivileged((PrivilegedAction<Map<Field, Type>>) () -> {
Map<Field, Type> fields = Maps.newLinkedHashMap();

Type type = typeInfo.getType();
Class<? super T> rawType = typeInfo.getRawType();

while (!Object.class.equals(rawType)) {
for (Field field : rawType.getDeclaredFields()) {
if (Modifier.isTransient(field.getModifiers()) || Modifier.isStatic(field.getModifiers())) {
continue;
}

if (!Modifier.isPublic(field.getModifiers())) {
logger.warn("Field {}#{} will not be serialised. Terasology no longer supports serialising private fields.", field.getName(), rawType.getTypeName());

Check warning on line 94 in engine/src/main/java/org/terasology/engine/persistence/typeHandling/extensionTypes/factories/ComponentTypeHandlerFactory.java

View check run for this annotation

Terasology Jenkins.io / CheckStyle

LineLengthCheck

NORMAL: Line is longer than 150 characters (found 173).
Raw output
<p>Since Checkstyle 3.0</p><p> Checks for long lines. </p><p> Rationale: Long lines are hard to read in printouts or if developers have limited screen space for the source code, e.g. if the IDE displays additional information like project tree, class hierarchy, etc. </p>
continue;
}

Type fieldType = ReflectionUtil.resolveType(type, field.getGenericType());
fields.put(field, fieldType);
}

rawType = rawType.getSuperclass();
}

return fields;
});
}
}
Loading