-
Notifications
You must be signed in to change notification settings - Fork 847
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
Add support for covariant return types #1575
base: master
Are you sure you want to change the base?
Conversation
Looks like a good quality thing to fix to me but I don't work with the LiveConnect stuff very much. Does anyone else who uses this stuff extensively want to have a look? |
sorry i do not use it |
@andreabergia care to have a look at this one? Seems like your team also uses Java interop a fair bit |
Don't use it either to the extend that we would run into this, but reading the explanation and the linked Java succes, I think the PR makes sense |
if (existing == null) { | ||
map.put(sig, method); | ||
} else if (existing.getReturnType().isAssignableFrom(method.getReturnType())) { | ||
// if there are multiple methods with same name (which is allowed in bytecode, but not |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e.g. if the existing method has a return type of Object
but new method will return String
, we will use this method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm on a Map efficiency kick this week, so I apologize in advance -- but is there a way that you could replace the "map.get" / "map.put" pair with "putIfAbsent" or something like that? I'm not 100% sure but I think that if it works you could have fewer hash map traversals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the Map calls are actually ok the way that they are. The default implementation of putIfAbsent does exactly the same thing, and this way he already has a reference to the existing value for the else condition without needing to get it again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignore my last comment. HashMap does not use the default Map implementation, so there may be a slight improvement. @gbrail, would it need to look something like this instead?
Method putResult = map.putIfAbsent(sig, method);
if (putResult != method && putResult.getReturnType().isAssignableFrom(method.getReturnType())) {
map.put(sig, method);
}
MethodSignature sig = new MethodSignature(method); | ||
if (!map.containsKey(sig)) map.put(sig, method); | ||
} | ||
discoverPublicMethods(clazz, map); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code in discoverPublicMethods:
void discoverPublicMethods(Class<?> clazz, Map<MethodSignature, Method> map) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
registerMethod(map, method);
}
}
I would like to move forward with this but I'd like to know if there is a way to optimize the map usage like I commented above. Thanks! |
Yep -- that saves us from traversing the hash table twice (once for the get
and again for the put) and that can really add up if this happens in a
place that gets called a lot!
…On Wed, Sep 11, 2024 at 10:46 AM Tony Germano ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In rhino/src/main/java/org/mozilla/javascript/JavaMembers.java
<#1575 (comment)>:
> @@ -387,7 +380,12 @@ void discoverPublicMethods(Class<?> clazz, Map<MethodSignature, Method> map) {
static void registerMethod(Map<MethodSignature, Method> map, Method method) {
MethodSignature sig = new MethodSignature(method);
// Array may contain methods with same signature but different return value!
- if (!map.containsKey(sig)) {
+ Method existing = map.get(sig);
+ if (existing == null) {
+ map.put(sig, method);
+ } else if (existing.getReturnType().isAssignableFrom(method.getReturnType())) {
+ // if there are multiple methods with same name (which is allowed in bytecode, but not
Ignore my last comment. HashMap does not use the default Map
implementation, so there may be a slight improvement. @gbrail
<https://github.com/gbrail>, would it need to look something like this
instead?
Method putResult = map.putIfAbsent(sig, method);if (putResult != method && putResult.getReturnType().isAssignableFrom(method.getReturnType())) {
map.put(sig, method);
}
—
Reply to this email directly, view it on GitHub
<#1575 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAD7I26IIFHR6RVAQ4ZV3NLZWB6VZAVCNFSM6AAAAABM5O532SVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHMZDEOJYGEZDAMZTHE>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Can take a look at the PR next week or so |
A few days ago, we discovered also a strange behaviour in the js<->java liveconnect code.
Normally, rhino will detect getters/setters in java classes and allows you, to access them as properties.
e.g. if you have a class like
you can read/write the value with
myBean.value = "foo"
in javascript. This works if there are matching getters/setters found. (If there is an incompatible setter likesetValue(Integer value)
it will no longer work, as Integer is not an instance of String.BTW: It will work if there is a
setValue(Object value)
, as this setter accepts also strings.If you now inherit and narrow the return type, you will get multiple methods with covariant return types, e.g.
will result in the following bytecode:
Note: You get the same method with same signature, but different return types. This is not allowed by the JLS, but at VM/bytecode level and it is explicitly mentioned in https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Class.html#getMethods()
What happens in Rhino now:
getValue()Ljava/lang/String
orgetValue()Ljava/lang/Object
-- If
getValue()Ljava/lang/String
was found, it will find also thesetValue(String)
setter and generates a String-BeanProperty-- If
getValue()Ljava/lang/Object
was found, it will thesetValue(String)
setter, but cannot use it, because rhino thinks the property is of type Object and the setter is incompatibleClass.getMethods()
does not return the methods in a particular order. (What happened: In our unit tests, thecorrect
method was preferred, in production thewrong
method was preferred)This PR changes the method discovery in a way that if a method with same signature was found, but a "better" return type, this method is used.