[wpilib] Fix OpModeRobot initialization (#8834)

There were 3 cases failing before.

1. An OpModeRobot with no annotation.
2. An OpModeRobot with an annotation but a parameterless constructor.
3. An OpMode with a UserControls constructor

This PR solves both of these issues. The first one is solved by adding
the null check before setting the user controls instance. That one will
never have an opmode instance.

The second one is solved by falling back to a parameterless constructor
if one with a parameter is not found.

The 3rd one is solved by using the annotation type rather than the
instance for constructor lookup.

Also fixes ExpansionHubSample's missing annotations.
This commit is contained in:
Thad House
2026-04-28 09:54:10 -10:00
committed by GitHub
parent 3bf67edc34
commit 94443c8e84
6 changed files with 28 additions and 12 deletions

View File

@@ -115,9 +115,8 @@ public abstract class OpModeRobot extends RobotBase {
Optional<ConstructorMatch<T>> ctor;
// try 2-parameter constructor
if (m_userControlsInstance != null) {
ctor =
ConstructorMatch.findBestConstructor(cls, getClass(), m_userControlsInstance.getClass());
if (m_userControlsBaseClass.isPresent()) {
ctor = ConstructorMatch.findBestConstructor(cls, getClass(), m_userControlsBaseClass.get());
if (ctor.isPresent()) {
return ctor;
}
@@ -130,8 +129,8 @@ public abstract class OpModeRobot extends RobotBase {
}
// try 1-parameter constructor with UserControls parameter
if (m_userControlsInstance != null) {
ctor = ConstructorMatch.findBestConstructor(cls, m_userControlsInstance.getClass());
if (m_userControlsBaseClass.isPresent()) {
ctor = ConstructorMatch.findBestConstructor(cls, m_userControlsBaseClass.get());
if (ctor.isPresent()) {
return ctor;
}

View File

@@ -294,12 +294,15 @@ public abstract class RobotBase implements AutoCloseable {
UserControlsInstance userControlsAttribute =
robotClass.getDeclaredAnnotation(UserControlsInstance.class);
UserControls userControlsInstance = null;
Optional<ConstructorMatch<T>> constructorMatch;
Optional<ConstructorMatch<T>> constructorMatch = Optional.empty();
if (userControlsAttribute != null) {
var userControlsClass = userControlsAttribute.value();
userControlsInstance = userControlsClass.getDeclaredConstructor().newInstance();
constructorMatch = ConstructorMatch.findBestConstructor(robotClass, userControlsClass);
} else {
}
if (constructorMatch.isEmpty()) {
// Try to find a constructor with no parameters if there is no UserControls constructor
constructorMatch = ConstructorMatch.findBestConstructor(robotClass);
}
@@ -310,7 +313,7 @@ public abstract class RobotBase implements AutoCloseable {
T robot = constructorMatch.get().newInstance(userControlsInstance);
if (robot instanceof OpModeRobot opModeRobot) {
if (userControlsInstance != null && robot instanceof OpModeRobot opModeRobot) {
// Insert the UserControls instance into the opModeRobot for use when constructing opmodes
opModeRobot.setUserControlsInstance(userControlsInstance);
}

View File

@@ -47,6 +47,16 @@ eclipse {
}
}
tasks.withType(JavaExec).configureEach {
// Commands v3 needs reflective access to the continuation classes
jvmArgs += [
'--add-opens',
'java.base/jdk.internal.vm=ALL-UNNAMED',
'--add-opens',
'java.base/java.lang=ALL-UNNAMED',
]
}
jacoco {
toolVersion = "0.8.14"
}

View File

@@ -4,9 +4,11 @@
package org.wpilib.examples.expansionhubsample;
import org.wpilib.opmode.Autonomous;
import org.wpilib.opmode.PeriodicOpMode;
import org.wpilib.system.Timer;
@Autonomous
public class DefaultAutoMode extends PeriodicOpMode {
private final Robot robot;
private final Timer timer = new Timer();

View File

@@ -6,7 +6,9 @@ package org.wpilib.examples.expansionhubsample;
import org.wpilib.driverstation.DefaultUserControls;
import org.wpilib.opmode.PeriodicOpMode;
import org.wpilib.opmode.Teleop;
@Teleop
public class DefaultTeleMode extends PeriodicOpMode {
private final Robot robot;
private final DefaultUserControls userControls;

View File

@@ -57,8 +57,8 @@ public class ConstructorMatch<T> {
/**
* Creates a new instance of the constructor's class using the given arguments. The arguments must
* match the parameter types of the constructor, and must not be assignable to each other. The
* order of the arguments does not matter, as they will be matched to the parameter types.
* Duplicate arguments are ignored, as the first match will match.
* order of the arguments does matter, as they will be matched to the constructor parameter types
* in order.
*
* @param args the arguments to pass to the constructor
* @return a new instance of the constructor's class
@@ -90,8 +90,8 @@ public class ConstructorMatch<T> {
* types must not be assignable to each other. If multiple constructors match, the one with the
* most specific parameter types is chosen. If there is still a tie, the one with the most
* specific first parameter type is chosen, then the second parameter type, and so on. The order
* of the parameter types does not matter, as they will be matched to the constructor's parameter
* types. Duplicate parameter types are ignored, as the first match will match.
* of the parameter types does matter, as they will be matched to the constructor parameter types
* in order.
*
* @param <T> the type of the class to find the constructor for
* @param clazz the class to find the constructor for