mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
[epilogue] Add superclass field & method logging (#7993)
This commit is contained in:
@@ -13,7 +13,9 @@ import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.type.TypeVariable;
|
||||
|
||||
/**
|
||||
* Handles logging of fields or methods. An element that passes the {@link #isLoggable(Element)}
|
||||
@@ -126,11 +128,15 @@ public abstract class ElementHandler {
|
||||
}
|
||||
|
||||
private static String fieldAccess(VariableElement field) {
|
||||
if (field.getModifiers().contains(Modifier.PRIVATE)) {
|
||||
if (!field.getModifiers().contains(Modifier.PUBLIC)) {
|
||||
// ((com.example.Foo) $fooField.get(object))
|
||||
// Extra parentheses so cast evaluates before appended methods
|
||||
// (e.g. when appending .getAsDouble())
|
||||
return "((" + field.asType() + ") $" + field.getSimpleName() + ".get(object))";
|
||||
TypeMirror type = field.asType();
|
||||
if (type.getKind() == TypeKind.TYPEVAR) {
|
||||
type = ((TypeVariable) type).getUpperBound();
|
||||
}
|
||||
return "((" + type.toString() + ") $" + field.getSimpleName() + ".get(object))";
|
||||
} else {
|
||||
// object.fooField
|
||||
return "object." + field.getSimpleName();
|
||||
|
||||
@@ -114,42 +114,10 @@ public class LoggerGenerator {
|
||||
if (config == null) {
|
||||
config = m_defaultConfig;
|
||||
}
|
||||
boolean requireExplicitOptIn = config.strategy() == Logged.Strategy.OPT_IN;
|
||||
|
||||
Predicate<Element> notSkipped = LoggerGenerator::isNotSkipped;
|
||||
Predicate<Element> optedIn =
|
||||
e -> !requireExplicitOptIn || e.getAnnotation(Logged.class) != null;
|
||||
|
||||
List<VariableElement> fieldsToLog;
|
||||
if (Objects.equals(clazz.getSuperclass().toString(), "java.lang.Record")) {
|
||||
// Do not log record members - just use the accessor methods
|
||||
fieldsToLog = List.of();
|
||||
} else {
|
||||
fieldsToLog =
|
||||
clazz.getEnclosedElements().stream()
|
||||
.filter(e -> e instanceof VariableElement)
|
||||
.map(e -> (VariableElement) e)
|
||||
.filter(notSkipped)
|
||||
.filter(optedIn)
|
||||
.filter(e -> !e.getModifiers().contains(Modifier.STATIC))
|
||||
.filter(this::isLoggable)
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<ExecutableElement> methodsToLog =
|
||||
clazz.getEnclosedElements().stream()
|
||||
.filter(e -> e instanceof ExecutableElement)
|
||||
.map(e -> (ExecutableElement) e)
|
||||
.filter(notSkipped)
|
||||
.filter(optedIn)
|
||||
.filter(e -> !e.getModifiers().contains(Modifier.STATIC))
|
||||
.filter(e -> e.getModifiers().contains(Modifier.PUBLIC))
|
||||
.filter(e -> e.getParameters().isEmpty())
|
||||
.filter(e -> e.getReceiverType() != null)
|
||||
.filter(kIsBuiltInJavaMethod.negate())
|
||||
.filter(this::isLoggable)
|
||||
.filter(e -> !isSimpleGetterMethodForLoggedField(e, fieldsToLog))
|
||||
.toList();
|
||||
List<VariableElement> fieldsToLog = new ArrayList<>();
|
||||
List<ExecutableElement> methodsToLog = new ArrayList<>();
|
||||
collectLoggables(clazz, fieldsToLog, methodsToLog);
|
||||
|
||||
// Validate no name collisions
|
||||
Map<String, List<Element>> usedNames =
|
||||
@@ -216,9 +184,9 @@ public class LoggerGenerator {
|
||||
|
||||
var loggerFile = m_processingEnv.getFiler().createSourceFile(loggerClassName);
|
||||
|
||||
var privateFields =
|
||||
loggableFields.stream().filter(e -> e.getModifiers().contains(Modifier.PRIVATE)).toList();
|
||||
boolean requiresVarHandles = !privateFields.isEmpty();
|
||||
var varHandleFields =
|
||||
loggableFields.stream().filter(e -> !e.getModifiers().contains(Modifier.PUBLIC)).toList();
|
||||
boolean requiresVarHandles = !varHandleFields.isEmpty();
|
||||
|
||||
try (var out = new PrintWriter(loggerFile.openWriter())) {
|
||||
if (packageName != null) {
|
||||
@@ -246,10 +214,10 @@ public class LoggerGenerator {
|
||||
+ "> {");
|
||||
|
||||
if (requiresVarHandles) {
|
||||
for (var privateField : privateFields) {
|
||||
for (var varHandleField : varHandleFields) {
|
||||
// This field needs a VarHandle to access.
|
||||
// Cache it in the class to avoid lookups
|
||||
out.println(" private static final VarHandle $" + privateField.getSimpleName() + ";");
|
||||
out.println(" private static final VarHandle $" + varHandleField.getSimpleName() + ";");
|
||||
}
|
||||
out.println();
|
||||
|
||||
@@ -262,8 +230,8 @@ public class LoggerGenerator {
|
||||
+ classReference
|
||||
+ ", MethodHandles.lookup());");
|
||||
|
||||
for (var privateField : privateFields) {
|
||||
var fieldName = privateField.getSimpleName();
|
||||
for (var varHandleField : varHandleFields) {
|
||||
var fieldName = varHandleField.getSimpleName();
|
||||
out.println(
|
||||
" $"
|
||||
+ fieldName
|
||||
@@ -272,7 +240,7 @@ public class LoggerGenerator {
|
||||
+ ", \""
|
||||
+ fieldName
|
||||
+ "\", "
|
||||
+ m_processingEnv.getTypeUtils().erasure(privateField.asType())
|
||||
+ m_processingEnv.getTypeUtils().erasure(varHandleField.asType())
|
||||
+ ".class);");
|
||||
}
|
||||
|
||||
@@ -347,6 +315,57 @@ public class LoggerGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private void collectLoggables(
|
||||
TypeElement clazz, List<VariableElement> fields, List<ExecutableElement> methods) {
|
||||
var config = clazz.getAnnotation(Logged.class);
|
||||
if (config == null) {
|
||||
config = m_defaultConfig;
|
||||
}
|
||||
boolean requireExplicitOptIn = config.strategy() == Logged.Strategy.OPT_IN;
|
||||
|
||||
Predicate<Element> notSkipped = LoggerGenerator::isNotSkipped;
|
||||
Predicate<Element> optedIn =
|
||||
e -> !requireExplicitOptIn || e.getAnnotation(Logged.class) != null;
|
||||
|
||||
List<VariableElement> classFields;
|
||||
if (Objects.equals(clazz.getSuperclass().toString(), "java.lang.Record")) {
|
||||
// Do not log record members - just use the accessor methods
|
||||
classFields = List.of();
|
||||
} else {
|
||||
classFields =
|
||||
clazz.getEnclosedElements().stream()
|
||||
.filter(e -> e instanceof VariableElement)
|
||||
.map(e -> (VariableElement) e)
|
||||
.filter(notSkipped)
|
||||
.filter(optedIn)
|
||||
.filter(e -> !e.getModifiers().contains(Modifier.STATIC))
|
||||
.filter(this::isLoggable)
|
||||
.toList();
|
||||
}
|
||||
fields.addAll(classFields);
|
||||
|
||||
methods.addAll(
|
||||
clazz.getEnclosedElements().stream()
|
||||
.filter(e -> e instanceof ExecutableElement)
|
||||
.map(e -> (ExecutableElement) e)
|
||||
.filter(notSkipped)
|
||||
.filter(optedIn)
|
||||
.filter(e -> !e.getModifiers().contains(Modifier.STATIC))
|
||||
.filter(e -> e.getModifiers().contains(Modifier.PUBLIC))
|
||||
.filter(e -> e.getParameters().isEmpty())
|
||||
.filter(e -> e.getReceiverType() != null)
|
||||
.filter(kIsBuiltInJavaMethod.negate())
|
||||
.filter(this::isLoggable)
|
||||
.filter(e -> !isSimpleGetterMethodForLoggedField(e, classFields))
|
||||
.toList());
|
||||
|
||||
TypeElement superclass =
|
||||
(TypeElement) m_processingEnv.getTypeUtils().asElement(clazz.getSuperclass());
|
||||
if (superclass != null) {
|
||||
collectLoggables(superclass, fields, methods);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLoggable(Element element) {
|
||||
return m_handlers.stream().anyMatch(h -> h.isLoggable(element));
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user