Skip to content

Core upgrade#1367

Open
Thrameos wants to merge 30 commits into
jpype-project:masterfrom
Thrameos:core_upgrade
Open

Core upgrade#1367
Thrameos wants to merge 30 commits into
jpype-project:masterfrom
Thrameos:core_upgrade

Conversation

@Thrameos

@Thrameos Thrameos commented May 10, 2026

Copy link
Copy Markdown
Contributor

Pull Request Summary: JPype Reverse Bridge & Modular Infrastructure

This PR implements a significant architectural overhaul to support Java's Module System (Jigsaw) while maintaining compatibility across the "Broken Land" of Java 9 through 21.

Core Infrastructure & Module Support

  • Transition to Named Module: Switches org.jpype to use formal module support. This includes providing the necessary module-path arguments and permission flags internally during startJVM.
  • Centralized Permission Management: Hardcoded mandatory --add-opens and --add-reads flags (e.g., java.base/java.lang=org.jpype). This abstracts away the complexity of Jigsaw security, ensuring the bridge "just works" without user-side flag management.
  • Module-Aware Resource Loading: Rewrote internal static initializers (such as the HTML entity cache) to use module-relative lookups (Class.getResourceAsStream). This ensures internal assets are accessible even when the JAR is encapsulated as a named module.
  • AI Contextualization: Added file markers to key source files to improve referencing and context-awareness for AI-assisted development.

Proxy & JNI Performance Enhancements

  • JNI "Master Key" (Java 11-15 Support): Added a native getDefaultHandle bridge to handle default method invocation. This utilizes JNI to bypass the "Strong Encapsulation" restrictions that previously blocked proxies from accessing default methods in foreign modules.
  • Aggressive Proxy Caching: Upgraded the Proxy layer to aggressively cache type arguments and return types.
  • Resource Sharing: Reworked proxy implementation to share underlying resources when the same interface list is provided, reducing the memory footprint and overhead of the bridge.

JDK 21+ Compliance Note

  • Native Access Management: Addressed the requirement for --enable-native-access=org.jpype. To avoid "poisoning" the startup of legacy JVMs (which do not recognize this flag), the bridge currently prioritizes universal compatibility. Future iterations may include a C++-level "probe" to safely apply this flag only when supported by the loaded DLL.

The last one requires a bit of work as version probing is not something Java does well.

This PR should likely target JPype 1.8.

Fixes #1369

Comment thread project/mark.py Fixed
String[] parts = line.split("\\s+");
if (parts.length >= 2)
{
ENTITIES.put(parts[0], Integer.parseInt(parts[1]));
Comment thread native/common/jp_proxy.cpp Fixed
@codecov

codecov Bot commented May 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.70255% with 54 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.97%. Comparing base (0c8a133) to head (0fe9c80).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
jpype/_core.py 71.42% 12 Missing ⚠️
native/common/jp_proxy.cpp 79.62% 7 Missing and 4 partials ⚠️
native/common/jp_context.cpp 76.31% 5 Missing and 4 partials ⚠️
native/python/pyjp_array.cpp 45.45% 0 Missing and 6 partials ⚠️
native/python/pyjp_module.cpp 71.42% 5 Missing and 1 partial ⚠️
native/common/jp_classhints.cpp 82.75% 5 Missing ⚠️
native/common/jp_functional.cpp 0.00% 4 Missing ⚠️
native/python/pyjp_buffer.cpp 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1367      +/-   ##
==========================================
- Coverage   86.20%   85.97%   -0.23%     
==========================================
  Files         112      112              
  Lines       10277    10302      +25     
  Branches     4046     3962      -84     
==========================================
- Hits         8859     8857       -2     
- Misses        812      835      +23     
- Partials      606      610       +4     
Files with missing lines Coverage Δ
native/common/include/jp_array.h 100.00% <ø> (ø)
native/common/include/jp_arrayclass.h 100.00% <ø> (ø)
native/common/include/jp_booleantype.h 100.00% <ø> (ø)
native/common/include/jp_boxedtype.h 100.00% <ø> (ø)
native/common/include/jp_bytetype.h 100.00% <ø> (ø)
native/common/include/jp_chartype.h 100.00% <ø> (ø)
native/common/include/jp_class.h 100.00% <ø> (ø)
native/common/include/jp_context.h 96.77% <ø> (ø)
native/common/include/jp_doubletype.h 100.00% <ø> (ø)
native/common/include/jp_field.h 100.00% <ø> (ø)
... and 68 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Thrameos Thrameos self-assigned this May 10, 2026
@Thrameos

Copy link
Copy Markdown
Contributor Author

@marscher I am not sure if we should make the module org.jpype or just jpype for this jigsaw update. Whatever we use we will be stuck with it as you must open to our module.

It appears there are two potential ways we can operate under jigsaw. The piece meal method is safer but requires the start JVM to manually add each an every module to be used to opens. It took me quite a while to find the right combination to operate. And that means a user is going to quickly find themselve getting random errors. I thus upgraded many points to being very verbose severe error logs so we don't get silent errors and bug reports.

The second method would be to add a "trusted" flag to starting JPype. With that we would attempt to patch java.base to extend all access privileges to Python. I would then need to test to see if it gets respected.

While it is good that we are now operating more in the Java security model, I fear it will make it harder for users to just operate as a simple local bridge like they expect.

@marscher

Copy link
Copy Markdown
Member

Even considered the downsides I have a gut feeling that the second method is kind of a hack and it seems foreseeable that it will fail in the future (of JVM development). Or is there a documented way of achieving it?

As for the module name I'd go for org.jpype

@Thrameos

Copy link
Copy Markdown
Contributor Author

The reality of the Python ecosystem makes runtime module patching even more dangerous. Packages like JayDeBeAPI (and many others heavily promoted by AI models) blindly wrap JPype and invoke startJVM() inside opaque init() functions.
If a wrapper library executes startJVM() first, it deprives the user of any opportunity to supply the necessary --patch-module or --add-opens arguments. The JVM graph is initialized instantly, locking out any subsequent libraries from modifying java.base or altering module permissions later. JPMS prevents any subsequent elevation.

The only structural advantage of a --patch-module strategy is that third-party packages inherently trust and export to java.base, meaning code injected there theoretically inherits whatever access those packages grant to the core runtime. In other words we can gain trust by installing in another module, but that doesn't allow us to change permissions. Java is a ONE AND DONE security model.

Because we cannot stop wrappers from blindly locking down the JVM, trying to engineer a 'trusted flag' hack inside the Java boot layer is a dead end. Instead, we must focus heavily on making org.jpype cooperative:

  • Enforce an explicit module name (org.jpype) so that if an advanced user does manage to control the startup, they can target us cleanly.
  • Provide clear, fatal diagnostic logs when jpype.isJVMStarted() is true but required packages are unexported. We must tell the user exactly which third-party wrapper broke the module graph so they can fix their import orders.

As for how Dynamic class loader will work, is that room will need to be opened to us an all jar loads will have to join that room. Long term we will need to be more clear that packages are not applications and should not be starting the JVM. Only the main application should do that.

@Thrameos

Copy link
Copy Markdown
Contributor Author

I have tried to come up with a more sane method of dealing with modules and paths. The problem is as a bridge our users want to use the files the directed is to. So the assuming is on the path use it. But jigsaw expects a DAG of relationships to be formed. After jvm is loaded you can see the DAG, but it is too late do change configuration.

My current direction is exposing all the raw jigsaw flags to the user. But even basic testing I found myself having to restart and add something as a new random error popped up. Darn no permissions for that. Suddenly the user must manage 2 paths and know JPSM.

One AI proposal is to give a path and module list. We have a user cache file that contains all the known DAG elements. If something on the path appears that we don't know about then we use python zipfile to get its opens and requirements list. We then look at the modules the user requested and grant opens as needed so jpype can access those and any dependent module. This gets cached so we only need to learn deps once until a jar changes.

So we would have path list, an option that tells where we want to look and a modules list. If the users says modules="*", they are saying just figure it out I just want to program, not be an enterprise manager.

Benefits are we only need one path for modules and classes. The system will know which jar is a module abd which is a plain old class. It will also make the required choices. If they name a jar file on modules required and the module name in the jar is different or it is in unnamed we will just figure out the switches instead of forcing every user to become a JPMS expert.

The downside is if a user places a huge directory on the path we are scanning everything to find one needle every startup. And the cache can become corrupted.

On the flip side, we risk the XKCD outcome of well the developer is struggling with 2 paths, let's just give them a third to make it easier! Not sure there is a good direction or even a model we can point to that solves this well.

Comment thread native/common/jp_javaframe.cpp Fixed
@Thrameos

Copy link
Copy Markdown
Contributor Author

There is a frame imbalance in this PR that will need to be address.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Proxy efficiency Improvements

3 participants