mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:09:49 +00:00
9d933d1281
Tested: No new tests. Change-Id: Idf19ce8b6743b221841e6cef6b2a80e8ab37860e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/354260 Auto-Submit: Lasse Nielsen <lrn@google.com> Reviewed-by: Johnni Winther <johnniwinther@google.com> Reviewed-by: Nate Bosch <nbosch@google.com> Reviewed-by: Daco Harkes <dacoharkes@google.com> Commit-Queue: Lasse Nielsen <lrn@google.com>
412 lines
14 KiB
Dart
412 lines
14 KiB
Dart
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
// Tests that the behavior of forwarding stubs to call noSuchMethod is
|
|
// restricted to public members and members from the same library; forwarding
|
|
// stubs for private members from other libraries always throw.
|
|
//
|
|
// Without this, field promotion would not be sound. For example, in the code
|
|
// below, each attempt to get `_i` from an instance of class `B` would result in
|
|
// an invocation of `B.noSuchMethod`, so evaluation of `a._i` at (1) would yield
|
|
// `0`, and evaluation of `a._i` at (2) would yield `null`.
|
|
//
|
|
// main.dart:
|
|
// import 'other.dart';
|
|
// class B implements A {
|
|
// bool b = false;
|
|
// @override
|
|
// dynamic noSuchMethod(Invocation invocation) => (b = !b) ? 0 : null;
|
|
// }
|
|
// main() => foo(new B());
|
|
// other.dart:
|
|
// class A {
|
|
// final int? _i;
|
|
// A(this._i);
|
|
// }
|
|
// void foo(A a) {
|
|
// if (a._i != null) { // (1)
|
|
// print(a._i + 1); // (2)
|
|
// }
|
|
// }
|
|
//
|
|
// Whereas with the restriction, any attempt to get `_i` from an instance of
|
|
// class `B` would result in an exception, so the code would never reach (2).
|
|
//
|
|
// Since this behavior involves interactions among libraries, we have to think
|
|
// carefully about how it is affected by language versioning. There are two
|
|
// issues to consider:
|
|
//
|
|
// 1. Should the forwarding stub throw even if the language feature
|
|
// "inference-update-2" is disabled in the library containing the class
|
|
// that's causing the forwarding stub to be generated? We need to answer
|
|
// this question with a "yes", otherwise a class declaration in a library
|
|
// with an older language version could still ruin the soundness of field
|
|
// promotion in some other library.
|
|
//
|
|
// 2. Should the forwarding stub throw even if the language feature
|
|
// "inference-update-2" is disabled in the library containing the private
|
|
// member in question? Our answer to this question doesn't affect soundness,
|
|
// because field promotion can't happen in libraries for which the language
|
|
// feature is disabled. However, we still answer "yes", because otherwise an
|
|
// attempt to upgrade to a newer language version in one library might cause
|
|
// unexpected behavior changes in other libraries that import it, and we
|
|
// don't want that to happen.
|
|
//
|
|
// This file covers cases where the language feature "inference-update-2" is
|
|
// enabled.
|
|
|
|
import 'package:expect/expect.dart';
|
|
|
|
import 'no_such_method_restriction_enabled_lib.dart' as lib;
|
|
|
|
class Interface {
|
|
static int interfaceCount = 0;
|
|
|
|
int _privateField = 100;
|
|
|
|
int get _privateGetter {
|
|
interfaceCount++;
|
|
return 101;
|
|
}
|
|
|
|
set _privateSetter(int value) {
|
|
interfaceCount++;
|
|
}
|
|
|
|
int _privateMethod() {
|
|
interfaceCount++;
|
|
return 102;
|
|
}
|
|
|
|
int publicField = 103;
|
|
|
|
int get publicGetter {
|
|
interfaceCount++;
|
|
return 104;
|
|
}
|
|
|
|
set publicSetter(int value) {
|
|
interfaceCount++;
|
|
}
|
|
|
|
int publicMethod() {
|
|
interfaceCount++;
|
|
return 105;
|
|
}
|
|
|
|
static int getPrivateField(Interface x) => x._privateField;
|
|
|
|
static void setPrivateField(Interface x) => x._privateField = 106;
|
|
|
|
static int callPrivateGetter(Interface x) => x._privateGetter;
|
|
|
|
static void callPrivateSetter(Interface x) => x._privateSetter = 107;
|
|
|
|
static int callPrivateMethod(Interface x) => x._privateMethod();
|
|
|
|
static int getPublicField(Interface x) => x.publicField;
|
|
|
|
static void setPublicField(Interface x) => x.publicField = 108;
|
|
|
|
static int callPublicGetter(Interface x) => x.publicGetter;
|
|
|
|
static void callPublicSetter(Interface x) => x.publicSetter = 109;
|
|
|
|
static int callPublicMethod(Interface x) => x.publicMethod();
|
|
}
|
|
|
|
class Dynamic {
|
|
static int getPrivateField(dynamic x) => x._privateField;
|
|
|
|
static void setPrivateField(dynamic x) => x._privateField = 103;
|
|
|
|
static int callPrivateGetter(dynamic x) => x._privateGetter;
|
|
|
|
static void callPrivateSetter(dynamic x) => x._privateSetter = 104;
|
|
|
|
static int callPrivateMethod(dynamic x) => x._privateMethod();
|
|
|
|
static int getPublicField(dynamic x) => x.publicField;
|
|
|
|
static void setPublicField(dynamic x) => x.publicField = 108;
|
|
|
|
static int callPublicGetter(dynamic x) => x.publicGetter;
|
|
|
|
static void callPublicSetter(dynamic x) => x.publicSetter = 109;
|
|
|
|
static int callPublicMethod(dynamic x) => x.publicMethod();
|
|
}
|
|
|
|
/// The tests in this class cover the case where the members are in the same
|
|
/// library as the forwarding stubs. All member invocations should be
|
|
/// dispatched to noSuchMethod.
|
|
class Local implements Interface {
|
|
int _localNsmCount = 0;
|
|
|
|
@override
|
|
noSuchMethod(Invocation invocation) {
|
|
return _localNsmCount++;
|
|
}
|
|
|
|
static void testPrivate() {
|
|
var x = Local();
|
|
Expect.equals(0, Interface.getPrivateField(x));
|
|
Expect.equals(1, x._localNsmCount);
|
|
Interface.setPrivateField(x);
|
|
Expect.equals(2, x._localNsmCount);
|
|
Expect.equals(2, Interface.callPrivateGetter(x));
|
|
Expect.equals(3, x._localNsmCount);
|
|
Interface.callPrivateSetter(x);
|
|
Expect.equals(4, x._localNsmCount);
|
|
Expect.equals(4, Interface.callPrivateMethod(x));
|
|
Expect.equals(5, x._localNsmCount);
|
|
Expect.equals(0, Interface.interfaceCount);
|
|
}
|
|
|
|
static void testPublic() {
|
|
var x = Local();
|
|
Expect.equals(0, Interface.getPublicField(x));
|
|
Expect.equals(1, x._localNsmCount);
|
|
Interface.setPublicField(x);
|
|
Expect.equals(2, x._localNsmCount);
|
|
Expect.equals(2, Interface.callPublicGetter(x));
|
|
Expect.equals(3, x._localNsmCount);
|
|
Interface.callPublicSetter(x);
|
|
Expect.equals(4, x._localNsmCount);
|
|
Expect.equals(4, Interface.callPublicMethod(x));
|
|
Expect.equals(5, x._localNsmCount);
|
|
Expect.equals(0, Interface.interfaceCount);
|
|
}
|
|
|
|
static void testPrivateDynamic() {
|
|
var x = Local();
|
|
Expect.equals(0, Dynamic.getPrivateField(x));
|
|
Expect.equals(1, x._localNsmCount);
|
|
Dynamic.setPrivateField(x);
|
|
Expect.equals(2, x._localNsmCount);
|
|
Expect.equals(2, Dynamic.callPrivateGetter(x));
|
|
Expect.equals(3, x._localNsmCount);
|
|
Dynamic.callPrivateSetter(x);
|
|
Expect.equals(4, x._localNsmCount);
|
|
Expect.equals(4, Dynamic.callPrivateMethod(x));
|
|
Expect.equals(5, x._localNsmCount);
|
|
}
|
|
|
|
static void testPublicDynamic() {
|
|
var x = Local();
|
|
Expect.equals(0, Dynamic.getPublicField(x));
|
|
Expect.equals(1, x._localNsmCount);
|
|
Dynamic.setPublicField(x);
|
|
Expect.equals(2, x._localNsmCount);
|
|
Expect.equals(2, Dynamic.callPublicGetter(x));
|
|
Expect.equals(3, x._localNsmCount);
|
|
Dynamic.callPublicSetter(x);
|
|
Expect.equals(4, x._localNsmCount);
|
|
Expect.equals(4, Dynamic.callPublicMethod(x));
|
|
Expect.equals(5, x._localNsmCount);
|
|
}
|
|
}
|
|
|
|
/// The tests in this class cover the case where both noSuchMethod and the other
|
|
/// members are in the same library as the forwarding stubs, but that library is
|
|
/// not the same as the library containing this class. All member invocations
|
|
/// should be dispatched to noSuchMethod.
|
|
class RemoteStubs extends lib.Stubs {
|
|
static void testPrivate() {
|
|
var x = RemoteStubs();
|
|
Expect.equals(0, lib.Interface.getPrivateField(x));
|
|
Expect.equals(1, x.stubsNsmCount);
|
|
lib.Interface.setPrivateField(x);
|
|
Expect.equals(2, x.stubsNsmCount);
|
|
Expect.equals(2, lib.Interface.callPrivateGetter(x));
|
|
Expect.equals(3, x.stubsNsmCount);
|
|
lib.Interface.callPrivateSetter(x);
|
|
Expect.equals(4, x.stubsNsmCount);
|
|
Expect.equals(4, lib.Interface.callPrivateMethod(x));
|
|
Expect.equals(5, x.stubsNsmCount);
|
|
Expect.equals(0, lib.Interface.interfaceCount);
|
|
}
|
|
|
|
static void testPublic() {
|
|
var x = RemoteStubs();
|
|
Expect.equals(0, lib.Interface.getPublicField(x));
|
|
Expect.equals(1, x.stubsNsmCount);
|
|
lib.Interface.setPublicField(x);
|
|
Expect.equals(2, x.stubsNsmCount);
|
|
Expect.equals(2, lib.Interface.callPublicGetter(x));
|
|
Expect.equals(3, x.stubsNsmCount);
|
|
lib.Interface.callPublicSetter(x);
|
|
Expect.equals(4, x.stubsNsmCount);
|
|
Expect.equals(4, lib.Interface.callPublicMethod(x));
|
|
Expect.equals(5, x.stubsNsmCount);
|
|
Expect.equals(0, lib.Interface.interfaceCount);
|
|
}
|
|
|
|
static void testPrivateDynamic() {
|
|
var x = RemoteStubs();
|
|
Expect.equals(0, lib.Dynamic.getPrivateField(x));
|
|
Expect.equals(1, x.stubsNsmCount);
|
|
lib.Dynamic.setPrivateField(x);
|
|
Expect.equals(2, x.stubsNsmCount);
|
|
Expect.equals(2, lib.Dynamic.callPrivateGetter(x));
|
|
Expect.equals(3, x.stubsNsmCount);
|
|
lib.Dynamic.callPrivateSetter(x);
|
|
Expect.equals(4, x.stubsNsmCount);
|
|
Expect.equals(4, lib.Dynamic.callPrivateMethod(x));
|
|
Expect.equals(5, x.stubsNsmCount);
|
|
}
|
|
|
|
static void testPublicDynamic() {
|
|
var x = RemoteStubs();
|
|
Expect.equals(0, lib.Dynamic.getPublicField(x));
|
|
Expect.equals(1, x.stubsNsmCount);
|
|
lib.Dynamic.setPublicField(x);
|
|
Expect.equals(2, x.stubsNsmCount);
|
|
Expect.equals(2, lib.Dynamic.callPublicGetter(x));
|
|
Expect.equals(3, x.stubsNsmCount);
|
|
lib.Dynamic.callPublicSetter(x);
|
|
Expect.equals(4, x.stubsNsmCount);
|
|
Expect.equals(4, lib.Dynamic.callPublicMethod(x));
|
|
Expect.equals(5, x.stubsNsmCount);
|
|
}
|
|
}
|
|
|
|
/// The tests in this class cover the case where noSuchMember is local, but the
|
|
/// other members are in a different library from the forwarding stubs. All
|
|
/// public member invocations should be dispatched to noSuchMethod; all private
|
|
/// member invocations should throw.
|
|
class LocalNsm implements lib.Interface {
|
|
int _localNsmCount = 0;
|
|
|
|
@override
|
|
noSuchMethod(Invocation invocation) {
|
|
return _localNsmCount++;
|
|
}
|
|
|
|
static void testPrivate() {
|
|
var x = LocalNsm();
|
|
Expect.throwsNoSuchMethodError(() => lib.Interface.getPrivateField(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Interface.setPrivateField(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Interface.callPrivateGetter(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Interface.callPrivateSetter(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Interface.callPrivateMethod(x));
|
|
Expect.equals(0, x._localNsmCount);
|
|
Expect.equals(0, lib.Interface.interfaceCount);
|
|
}
|
|
|
|
static void testPublic() {
|
|
var x = LocalNsm();
|
|
Expect.equals(0, lib.Interface.getPublicField(x));
|
|
Expect.equals(1, x._localNsmCount);
|
|
lib.Interface.setPublicField(x);
|
|
Expect.equals(2, x._localNsmCount);
|
|
Expect.equals(2, lib.Interface.callPublicGetter(x));
|
|
Expect.equals(3, x._localNsmCount);
|
|
lib.Interface.callPublicSetter(x);
|
|
Expect.equals(4, x._localNsmCount);
|
|
Expect.equals(4, lib.Interface.callPublicMethod(x));
|
|
Expect.equals(5, x._localNsmCount);
|
|
Expect.equals(0, lib.Interface.interfaceCount);
|
|
}
|
|
|
|
static void testPrivateDynamic() {
|
|
var x = LocalNsm();
|
|
Expect.throwsNoSuchMethodError(() => lib.Dynamic.getPrivateField(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Dynamic.setPrivateField(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Dynamic.callPrivateGetter(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Dynamic.callPrivateSetter(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Dynamic.callPrivateMethod(x));
|
|
Expect.equals(0, x._localNsmCount);
|
|
}
|
|
|
|
static void testPublicDynamic() {
|
|
var x = LocalNsm();
|
|
Expect.equals(0, lib.Dynamic.getPublicField(x));
|
|
Expect.equals(1, x._localNsmCount);
|
|
lib.Dynamic.setPublicField(x);
|
|
Expect.equals(2, x._localNsmCount);
|
|
Expect.equals(2, lib.Dynamic.callPublicGetter(x));
|
|
Expect.equals(3, x._localNsmCount);
|
|
lib.Dynamic.callPublicSetter(x);
|
|
Expect.equals(4, x._localNsmCount);
|
|
Expect.equals(4, lib.Dynamic.callPublicMethod(x));
|
|
Expect.equals(5, x._localNsmCount);
|
|
}
|
|
}
|
|
|
|
/// The tests in this class cover the case where both noSuchMember and the other
|
|
/// members are in a different library from the forwarding stubs. All public
|
|
/// member invocations should be dispatched to noSuchMethod; all private member
|
|
/// invocations should throw.
|
|
class RemoteNsm extends lib.Nsm implements lib.Interface {
|
|
static void testPrivate() {
|
|
var x = RemoteNsm();
|
|
Expect.throwsNoSuchMethodError(() => lib.Interface.getPrivateField(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Interface.setPrivateField(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Interface.callPrivateGetter(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Interface.callPrivateSetter(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Interface.callPrivateMethod(x));
|
|
Expect.equals(0, x.otherNsmCount);
|
|
Expect.equals(0, lib.Interface.interfaceCount);
|
|
}
|
|
|
|
static void testPublic() {
|
|
var x = RemoteNsm();
|
|
Expect.equals(0, lib.Interface.getPublicField(x));
|
|
Expect.equals(1, x.otherNsmCount);
|
|
lib.Interface.setPublicField(x);
|
|
Expect.equals(2, x.otherNsmCount);
|
|
Expect.equals(2, lib.Interface.callPublicGetter(x));
|
|
Expect.equals(3, x.otherNsmCount);
|
|
lib.Interface.callPublicSetter(x);
|
|
Expect.equals(4, x.otherNsmCount);
|
|
Expect.equals(4, lib.Interface.callPublicMethod(x));
|
|
Expect.equals(5, x.otherNsmCount);
|
|
Expect.equals(0, lib.Interface.interfaceCount);
|
|
}
|
|
|
|
static void testPrivateDynamic() {
|
|
var x = RemoteNsm();
|
|
Expect.throwsNoSuchMethodError(() => lib.Dynamic.getPrivateField(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Dynamic.setPrivateField(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Dynamic.callPrivateGetter(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Dynamic.callPrivateSetter(x));
|
|
Expect.throwsNoSuchMethodError(() => lib.Dynamic.callPrivateMethod(x));
|
|
Expect.equals(0, x.otherNsmCount);
|
|
}
|
|
|
|
static void testPublicDynamic() {
|
|
var x = RemoteNsm();
|
|
Expect.equals(0, lib.Dynamic.getPublicField(x));
|
|
Expect.equals(1, x.otherNsmCount);
|
|
lib.Dynamic.setPublicField(x);
|
|
Expect.equals(2, x.otherNsmCount);
|
|
Expect.equals(2, lib.Dynamic.callPublicGetter(x));
|
|
Expect.equals(3, x.otherNsmCount);
|
|
lib.Dynamic.callPublicSetter(x);
|
|
Expect.equals(4, x.otherNsmCount);
|
|
Expect.equals(4, lib.Dynamic.callPublicMethod(x));
|
|
Expect.equals(5, x.otherNsmCount);
|
|
}
|
|
}
|
|
|
|
main() {
|
|
Local.testPrivate();
|
|
Local.testPublic();
|
|
Local.testPrivateDynamic();
|
|
Local.testPublicDynamic();
|
|
RemoteStubs.testPrivate();
|
|
RemoteStubs.testPublic();
|
|
RemoteStubs.testPrivateDynamic();
|
|
RemoteStubs.testPublicDynamic();
|
|
LocalNsm.testPrivate();
|
|
LocalNsm.testPublic();
|
|
LocalNsm.testPrivateDynamic();
|
|
LocalNsm.testPublicDynamic();
|
|
RemoteNsm.testPrivate();
|
|
RemoteNsm.testPublic();
|
|
RemoteNsm.testPrivateDynamic();
|
|
RemoteNsm.testPublicDynamic();
|
|
}
|