Repository navigation
Fix sandbox static init and finalize - #123
Merged
Merged
Conversation
…ializers outside the robot's thread When a robot class loader is cleaned up, for example after building the robot database, cleanStaticReferences sets the robot's static reference fields to null. Setting a static field makes the JVM initialize the class, so a static initializer that hadn't run yet now ran on the thread doing the cleanup instead of on the robot's thread. Its System.out output then went to the main console instead of the robot's console. This also meant robot code ran during a robot database build, making the build slower than needed. Cleanup now skips classes that haven't been initialized, using sun.misc.Unsafe.shouldBeInitialized(). Such classes can't hold references anyway, because their static fields were never set. If the check isn't available, nothing is cleaned. This only matters on Java 8-11. On Java 12+ cleaning static references fails earlier because Field.modifiers can't be accessed anymore. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lizer thread The JVM calls finalize() of unreachable objects on its finalizer thread, not on a robot's thread. A robot class that overrides Object.finalize() therefore gets its code run on a thread that the sandbox treats as safe, where robot code is not checked like on the robot's own threads. RobotClassRewriter now renames finalize()V methods of robot classes, and calls to them, to robocode$finalize()V. They no longer override Object.finalize(), so the JVM never calls them. Calls to finalize() of Java classes, e.g. super.finalize() with Object as superclass, are not renamed. A call to a finalize() that a robot class only inherits throws NoSuchMethodError, but only if the robot calls it itself. Impact on the RoboRumble participants: 3 of 1472 robot jars declare finalize(), and all of these methods only clear fields, call super.finalize(), or close Rhino generators, so robots lose nothing by them no longer being called. The classes of kid.Toa .0.5 and kid.team.OmegaSquad .0.2 load and verify with their finalize() methods renamed. inonit.robocode.javascript.MyFirstRobot 1.1 fails to load the same Rhino classes with or without this change, as the verifier already denies some of them. RobotClassLoaderTest.finalizeIsRenamed compiles a robot with a finalize() method, and checks that it is renamed and that super.finalize() still works. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Static initializers running during cleanup
When a robot class loader is cleaned up, e.g. after building the robot database,
cleanStaticReferencessets the robot's static reference fields to null. Setting a static field initializes the class, so a static initializer that hadn't run yet ran on the cleanup thread instead of on the robot's thread. ItsSystem.outoutput went to the main console instead of the robot's console, and robot code ran while building the robot database, which made it slower than needed.Cleanup now skips classes that haven't been initialized, using
sun.misc.Unsafe.shouldBeInitialized(). Such classes can't hold references anyway, because their static fields were never set. If the check isn't available, nothing is cleaned.This only matters on Java 8–11. On Java 12+, cleaning static references already fails before any field is set, because
Field.modifierscan't be accessed anymore.finalize() running on the JVM's finalizer thread
The JVM calls
finalize()on its finalizer thread, which the sandbox treats as safe.RobotClassRewriternow renamesfinalize()Vmethods of robot classes, and calls to them, torobocode$finalize()V, so they no longer overrideObject.finalize()and the JVM never calls them. Calls tofinalize()of Java classes, e.g.super.finalize()withObjectas superclass, are not renamed.Impact on the RoboRumble participants:
finalize(). All of these methods only clear fields, callsuper.finalize(), or close Rhino generators, so robots lose nothing by them no longer being called.kid.Toa .0.5andkid.team.OmegaSquad .0.2load and verify with theirfinalize()methods renamed.inonit.robocode.javascript.MyFirstRobot 1.1fails to load the same Rhino classes with or without this change, as the verifier already denies some of them. This is a separate issue.Testing
RobotClassLoaderTest.finalizeIsRenamedcompiles a robot with afinalize()method, and checks that it is renamed and thatsuper.finalize()still works. It fails without the rewriter change.🤖 Generated with Claude Code