Skip to content

Conversation

@ItsWendell
Copy link

Fix: Prioritize getters over methods in property access

Problem

When accessing properties on Java objects exposed to JavaScript, getters were not being prioritized over methods with the same name. This caused property access like obj.name to return a function reference instead of the value from getName().

Example:

// Before fix: obj.name returns a function
typeof obj.name === 'function' // true
obj.name() // works, but not the intended behavior

// After fix: obj.name returns the value
typeof obj.name === 'string' // true
obj.name // "TestValue"

Root Cause

In BaseJavetReflectionProxyHandler.getByMethod(), the method resolution order checked methodsMap before gettersMap. This meant that when both a getter (e.g., getName()) and a method (e.g., name()) existed, the method took precedence, causing property access to return a function instead of invoking the getter.

Solution

Swapped the resolution order in BaseJavetReflectionProxyHandler.getByMethod() to check gettersMap first, then methodsMap. This ensures that:

  1. Property access (obj.name) invokes getters and returns values
  2. Explicit method calls (obj.getName()) still work correctly
  3. Behavior aligns with JavaScript semantics where getters are invoked on property access

Changes

  • File: src/main/java/com/caoccao/javet/interop/proxy/BaseJavetReflectionProxyHandler.java
  • Method: getByMethod() (lines ~274-299)
  • Change: Check gettersMap before methodsMap when resolving property access

Testing

Added test case testGetterPriorityOverMethod() in TestJavetProxyConverter.java that verifies:

  • Property access returns getter values, not functions
  • Explicit method calls still work
  • Fix works for multiple property names (e.g., name, displayName)

Impact

  • ✅ Fixes property access for JavaBean-style getters in JavaScript
  • ✅ Backward compatible: explicit method calls unchanged
  • ✅ Aligns with JavaScript semantics and developer expectations
  • ✅ Resolves issues in frameworks like PaperTS where player.name now works correctly

Related

This fix addresses a common issue when bridging Java objects to JavaScript, particularly affecting frameworks that rely on JavaBean conventions (e.g., Bukkit/PaperMC plugins).

@caoccao
Copy link
Owner

caoccao commented Nov 12, 2025

Thank you for the fix. Things are more complicated.

  • The current order has been accepted by the majority of Javet users for many years. Changing it will cause backward compatibility issues.
  • In some cases with generic getter and setter like Map, fields have to be the last to be visited, otherwise some keys in the Map can never be accessed.
  • Fields shadowing methods in JS should be avoided. Modern linters mark this as errors. I suppose we shouldn't design the system to be in favor of a discouraged behavior.

I tend to provide a few flags in the converter config to specify the order. That would be more flexible. In your case, you can specify fields take higher priority.

@caoccao
Copy link
Owner

caoccao commented Nov 14, 2025

This feature is in this commit. Please let me know if you have any questions.

@caoccao caoccao closed this Nov 30, 2025
@caoccao
Copy link
Owner

caoccao commented Nov 30, 2025

This feature was just released. Please let me know if it works well for you. And it would be great that you could buy me a cup of coffee by clicking the sponsor button.

@caoccao caoccao self-assigned this Nov 30, 2025
@caoccao caoccao added the enhancement New feature or request label Nov 30, 2025
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