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

cleanup/java-hacks #145

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open

cleanup/java-hacks #145

wants to merge 6 commits into from

Conversation

mineLdiver
Copy link
Member

StationAPI has been locked to exactly Java 17 for a while now due to using unsafe internal APIs.

This pull request cleans up all Java hacks and allows StationAPI to run on modern Java versions and very likely beyond.

Dropping support for Java 17 isn't a goal of this pull request, StationAPI will stay on Java 17 for the foreseeable future.

1. EnumFactory

EnumFactory has been removed in favor of @Invoker Mixins for a few Minecraft enums, such as ToolMaterial.

This solution isn't as widely encompassing as EnumFactory, because newly instantiated enums aren't being added to #values array, #valueOf lookup, and a few other internal caches in Java, but such behavior isn't really desired in the first place, because:

  1. EnumFactory didn't and couldn't factor in every created cache for an enum in the first place. First of all, all instances of EnumMap would have to somehow be patched to get the updated view of the values universe, and second, there are compile-time exhaustiveness assumptions, such as switch-case.
  2. The goal is to simply have another instance of the said enum, not to integrate it as tightly as possible with the original instances.

In an ideal world, all cases where enum instantiation is currently required should be handled by a dedicated API. ToolMaterial is likely to get such treatment in a followup pull request to Tools API.

Impact on mod developers

EnumFactory removal shouldn't affect a normal StAPI mod developer. Specialized factory classes such as ToolMaterialFactory are still there and simply use the invoker mixins internally now. However, EnumFactory was a part of public API, so if a developer depends on it directly, they should reconsider their approach to whatever problem they're solving.

2. Entrypoints

There are a few things about StAPI's handling of entrypoints which didn't work with modern Java.

Non-public utility fields and listener methods

IMPL_LOOKUP isn't meant to be obtainable, and will eventually be properly encapsulated once Unsafe memory access methods are removed.

StationAPI and UnsafeEvents abused IMPL_LOOKUP to gain access to an entrypoint's non-public members, such as @Namespace, @Logger, and @Instance fields (or "utility fields"), as well as @EventListener methods.

The solution is to make the entrypoints explicitly register their private lookups to StationAPI and UnsafeEvents if they want to keep their utility fields and listener methods non-public. Example:

public class ExampleEntrypoint {
    static {
        EntrypointManager.registerLookup(MethodHandles.lookup());
    }
    
    // the rest of the entrypoint code
}

Final utility fields

Setting final fields with reflection became impossible in Java 18, and the workaround of using Unsafe will eventually be dealt with too.

StationAPI abused reflection to allow the entrypoints to have final utility fields which are later set to their appropriate objects.

The solution is to disallow final utility fields completely, and instead provide getters for the appropriate values, such as:

  1. Namespace.resolve() - returns the caller class's namespace. Caches the result. Currently marked as experimental since it's based on heuristics, but was tested in IntelliJ runtime, Gradle runtime, in prod, and with modularity, and worked in all cases. Example usage:
public class ExampleEntrypoint {
    // the annotation is no longer applicable since the field is final
    // @Entrypoint.Namespace
    public static final Namespace NAMESPACE = Namespace.resolve();
}
  1. Namespace#getLogger - returns the namespace's logger. Caches the result. Has the same usage as @Entrypoint.Logger and is now the backend of it. Example usage:
public class ExampleEntrypoint {
    public static final Namespace NAMESPACE = Namespace.resolve();

    // the annotation is no longer applicable since the field is final
    // @Entrypoint.Logger
    public static final Logger LOGGER = NAMESPACE.getLogger(/* optionally a custom name string here */);
}

@Entrypoint.Instance is impossible to implement in a getter form because the instance isn't yet available when the class initializes.

Impact on mod developers

If your entrypoints contain non-public and/or final utility fields, or non-public listener methods, read above for the ways to update them, otherwise your entrypoints won't work with these changes.

Conclusion

There were also a couple of internal cases where Unsafe was used in StAPI, but those were patched away silently and without any impact to the mod developers.
This pull request will ensure StationAPI's long-term maintainability by properly respecting Java's integrity. Mod developers will also be able to leverage new Java features, and end users will be able to potentially get better performance from newer Java versions.

@mineLdiver mineLdiver added the enhancement New feature or request label Jan 3, 2025
@mineLdiver
Copy link
Member Author

Note: there are a lot of changed files in this pull request, but most of them are StAPI's own entrypoints patched to meet the new requirements, reviewing all of them isn't necessary.

Copy link
Member

@calmilamsy calmilamsy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only one issue, otherwise this looks great.

@mineLdiver mineLdiver requested a review from calmilamsy January 9, 2025 18:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants