-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
CFE: issues with noSuchMethod forwarders for private members from other libs #33665
Comments
@kmillikin - I'm guessing this is a CFE issue, but let me know if that's not the case. We are landing a workaround in dart2js here: https://dart-review.googlesource.com/c/sdk/+/62711, but we should remove it once the CFE is fixd. |
This lets dart2js correctly handle nsmForwarders with private names (see #33665 for details). Change-Id: Ic34bc8c2dbfd570f35f2f6a4c5a84b3da6ddb1f5 Reviewed-on: https://dart-review.googlesource.com/62711 Reviewed-by: Stephen Adams <sra@google.com> Commit-Queue: Sigmund Cherem <sigmund@google.com>
We'll figure out what we want in this case and report back here. |
Warning: a rather lengthy explanation ahead :) TL;DR I think, it's a non-issue. The noSuchMethod forwarders generated from The first example above is compiled to the following:
Note that This is how, I believe, is done in the VM that currently prints Now, let's modify the example a little, so that it matters that the noSuchMethod forwarders are generated for the private fields: // ======== a.dart ========
import 'b.dart';
class A implements I {
int _field = 1;
noSuchMethod(Invocation m) => 2;
}
class B implements I {
noSuchMethod(Invocation m) => 3;
// Doesn't have a `_field`, but has the forwarders for `I._field`, private to `b.dart`.
}
main() {
print(new A()._field);
// print(new B()._field);
foo(new B());
}
// ======== b.dart ========
class I {
int _field = 0;
}
void foo(I i) {
print("${i._field}");
} When run with the VM, it prints out the following: $ out/ReleaseX64/dart /tmp/a.dart
1
3 Here Note that if the commented line in $ out/ReleaseX64/dart /tmp/a.dart
file:///tmp/a.dart:14:17: Error: The getter '_field' isn't defined for the class '#lib1::B'.
Try correcting the name to the name of an existing getter, or defining a getter or field named '_field'.
print(new B()._field); That is, the getter is not visible in |
@sigmundch, I agree with your analysis. If we were to allow an nSM-forwarder to implement an inaccessible method signature (that is, the method signature of a private method declared in a different library) then we would again position With the support for So However, @stefantsov, you describe how the treatment of private names in CFE is such that a private name The consistent treatment would be to ensure that "external" code (looking from a library L1, it would be code from a different library L2) cannot specify private names (from L1), not even indirectly via nSM forwarders. |
Having discovered (with a little help from my friends ;-) that we cannot avoid giving |
Kernel doesn't have private names. It has some names that are strings and it has some names that are strings paired with an opaque token. This doesn't have anything to do with privacy in Kernel. It's just a mechanism for a class to have more than one member with the same textual name. In the first example, class A has a field named the equivalent of The property get As @stefantsov says, we really do want these NSM forwarders in class A because you can pass an instance of A into the library b.dart as an I. Dart2js will need a mechanism to allow these to be distinct members. It probably works to decorate the name with the token (it's a library reference in fact) somehow. |
I just created an area-language & area-specification issue about this, #33725.
Right, but from the perspective of Dart there is no way in library L1 to express a private name _n declared in L2 distinct from L1, and the ability to specify the behavior of an instance method with that inaccessible name via However, I just realized that there is no way we can avoid it, short of outlawing such classes, so we should of course maintain the underlying invariant that every statically checked invocation will find a corresponding method implementation to invoke—no matter whether it's a violation or not, we can't avoid it. |
Thank you so much everyone for the healthy discussion and clarifications! I filed #33732 to ensure dart2js preserves the right semantics, I'm closing this bug as there is no other action needed from the CFE team. Thanks again! |
Not sure if this was by design or not: the CFE is including nsm-forwarders for private members that originate in other libraries. It appears we can workaround this on the dart2js side by not generating the code for some nsm-forwarders, but it seems to me like we don't want to generate them in the IR in the first place.
Here are some examples.
Example 1: a private field:
a.dart:
b.dart:
The CFE generates a nsm-forwarder for _field defined in b.dart. As a result, dart2js prints '2' instead of '1' on this program.
I discovered this from a failing test of one internal customer.
Example 2: other private members, from same and different libraries.
This example just highlights that we do want nsm-forwarders for private members of the same library though.
a2.dart:
b2.dart:
The text was updated successfully, but these errors were encountered: