-
Notifications
You must be signed in to change notification settings - Fork 375
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
Fix native JsMethods with varargs called from Java varargs methods #9957
Fix native JsMethods with varargs called from Java varargs methods #9957
Conversation
This patch has a few issues with it - the native property ref issue is one, discussed above, but also that the fix is incomplete. While this does appear to address the long-standing issue (at least since 2.9, probably older), it seems that the recent JDT update changes how varargs are handled in some small way. Perhaps that issue could be handled separately, but they seem somewhat linked - when I understand what is happening there I'll see if it makes sense to land them together or apart. |
01948a6
to
54cf49b
Compare
I think the concerns above are addressed - the native property ref sideeffect has been backed out, and the JDT issue is resolved, though more tests need to be written to confirm. Here's the fully build succeeding with the latest JDT change: https://github.com/niloc132/gwt/actions/runs/9586615514 The fix revolves around the fact that there are subtypes of MethodBinding that didn't previously exist - using
Unfortunately, GWT has effectively no tests for simple Java features, only indirect tests that often expose problems. I assume either that these tests exist but were never ported to open source, or that they were not needed since Google had enough code written using GWT that such problems were discovered on their own. Nevertheless, it makes bugs like this and switch/case hard to hunt down, since simple tests like "are varargs methods called correctly" or "does the correct case get used in a switch" don't seem to exist in their plainest form. |
@@ -462,8 +462,8 @@ public void endVisit(JMethodCall x, Context ctx) { | |||
// Passed as an array to varargs method will result in an apply call, in which case hoist the | |||
// qualifier to make sure it is only evaluated once. | |||
JExpression instance = x.getInstance(); | |||
if (x.getTarget().needsDynamicDispatch() && !x.isStaticDispatchOnly() | |||
&& instance != null && !(instance instanceof JVariableRef)) { | |||
if (x.getTarget().needsDynamicDispatch() && !x.isStaticDispatchOnly() && instance != null |
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.
Since we dropped support for java 7 and now we only support java 8+ wont it make sense to start using newer syntax like Objects.nonNull(...) and Objects.isNull(...)
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.
Specifically, we dropped support for running this test on Java 7 - the code may or may not still run, but the test won't pass. We are not yet compiling code to use Java 9+ APIs, but could use Java features like that.
That said, two thoughts:
- isNull() is great to use as a lambda, but it any better than the != null? It appears to be the same number of chars (assuming you add spaces, and assuming you do a static import), but will add the cost of a method call (until it is inlined by the jit, etc). As I understand it, the purpose is mostly for passing as a method reference...?
- If I'm not changing the logic (I only moved the null check up a line), I'd rather not change the code to another way of doing the same thing, all else equals.
@@ -143,13 +143,17 @@ public void endVisit(JsNameOf x, JsContext ctx) { | |||
*/ | |||
@Override | |||
public void endVisit(JsNameRef x, JsContext ctx) { | |||
if (x.getQualifier() == null && x.getIdent() == "arguments") { | |||
if (x.getQualifier() == null && "arguments".equals(x.getIdent())) { |
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.
Same here Objects.isNull
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.
Objects.equals() probably? I think that would be better than what I have here now, as a replacement for ==
. I've never seen this fail, but it certainly could, and then would break here, which is why I changed it.
Sorry if this is irrelevant: Is this patch close to the issue #9675 ? Or is it in a different part of the code? |
@vasvir This patch fixes JsMethod varargs methods, e.g. |
@vasvir I think the issue you linked has an accurate summary here - there isn't a nice way to do this outside of the ones you've presented. Getting into the details of "what does the Function object say about how you can call it" seem fraught - either we need an extra wrapper per function (Roberto's solution of a new wrapper per func), a huge runtime cost (your option 2 or 3 solution) or we need to be satisfied with only 0-10 args, and never preserving the name of the arguments (your option 1). While 0-10 (or maybe 0-5? 0-50?) might suffice for your own work, it would break JS projects/tools that inspect the names params would still break (angular does this for example for its DI feature). Another option not included in your list is to get rid of the runtime arg count check and instead have your 11 (or 51, etc) makeLambdaArgumentN functions in Runtime.java, and tweak GenerateJavaScriptAST.constructJsFunctionObject gwt/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java Lines 1010 to 1020 in a53bb51
to instead dispatch based on arg count, so the work is done at compile time. In theory this could be an optional feature this way, and the 99% of apps that have never wanted this don't need to be impacted by this case. |
Native jsinterop varargs calls of the form
instance.method(argsArray)
sometimes implemented usinginstance.method.apply(instance, argsArray)
, where instance represents some expression. JsUtils.createApplyInvocation assumes that ImplementJsVarargs has already created a local variable to ensure that no side effects can take place when cloning the "instance", but ImplementJsVarargs doesn't actually check if a side effect was able to happen.This fix only adds an additional check, creating a local variable in the case where the instance expression has side effects, and clarifies that a native JsProperty might have sideeffects (as it could call a JS property accessor).
An additional fix is made to JsSafeCloner, to let it successfully clone a qualified reference (e.g.
$wnd.console
) rather than produce an invalid JS AST. This ended up not being strictly required for the fix, but appears to be more correct and will at least generate code rather than a NullPointerException in a later location.Fixes #9932