Skip to content

Commit 5490675

Browse files
stereotype441Commit Queue
authored and
Commit Queue
committed
Don't delegate foreign private names to noSuchMethod.
If a concrete class implements an interface containing a name that's private to a different library, any attempt to invoke that name will result in an exception getting thrown. Previously, such attempts would result in the call being diverted to noSuchMethod. This change closes a loophole in Dart's privacy system, and paves the way for a future implementation of promotion for private final fields (see dart-lang/language#2020). Bug: #49687 Change-Id: Ie55805e0fc77dc39713761a80a42c28bd0504722 Tested: language tests Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/255640 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Paul Berry <[email protected]> Reviewed-by: Lasse Nielsen <[email protected]>
1 parent 1ab0414 commit 5490675

File tree

185 files changed

+3089
-1355
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

185 files changed

+3089
-1355
lines changed

CHANGELOG.md

+15
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@
3939
[#49635]: https://github.com/dart-lang/sdk/issues/49635
4040
[#49878]: https://github.com/dart-lang/sdk/issues/49878
4141

42+
- **Breaking Change** [#49687][]: Don't delegate inaccessible private names to
43+
`noSuchMethod`. If a concrete class implements an interface containing a
44+
member with a name that's private to different library, and does not inherit
45+
an implementation of that interface member, a invocation of that member will
46+
result in an exception getting thrown. Previously, such attempts would result
47+
in the call being diverted to the `noSuchMethod` method.
48+
49+
This change closes a loophole in Dart's privacy system, where another library
50+
can provide a different implementation of a supposedly private member using
51+
`noSuchMethod`, and paves the way for a future implementation of promotion for
52+
private final fields (see [#2020][]).
53+
54+
[#49687]: https://github.com/dart-lang/sdk/issues/49687
55+
[#2020]: https://github.com/dart-lang/language/issues/2020
56+
4257
### Libraries
4358

4459
#### `dart:convert`

pkg/dart2wasm/lib/target.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ class WasmTarget extends Target {
263263
bool isStatic = false,
264264
bool isConstructor = false,
265265
bool isTopLevel = false}) {
266-
return ConstructorInvocation(
266+
return StaticInvocation(
267267
coreTypes.noSuchMethodErrorDefaultConstructor,
268268
Arguments(
269269
[receiver, _instantiateInvocation(coreTypes, name, arguments)]));

pkg/front_end/lib/src/fasta/source/source_class_builder.dart

+43-14
Original file line numberDiff line numberDiff line change
@@ -1483,8 +1483,12 @@ class SourceClassBuilder extends ClassBuilderImpl
14831483
_transformProcedureToNoSuchMethodForwarder(
14841484
noSuchMethod, target, member as Procedure);
14851485
} else {
1486-
Procedure memberSignature =
1487-
combinedMemberSignature.createMemberFromSignature()!;
1486+
// The reason we don't copy the location is to ensure that the stack
1487+
// trace for throwing no-such-method forwarders point to the class
1488+
// in which they are inserted and not to the declaration of the
1489+
// missing member.
1490+
Procedure memberSignature = combinedMemberSignature
1491+
.createMemberFromSignature(copyLocation: false)!;
14881492
_transformProcedureToNoSuchMethodForwarder(
14891493
noSuchMethod, target, memberSignature);
14901494
cls.procedures.add(memberSignature);
@@ -1593,31 +1597,56 @@ class SourceClassBuilder extends ClassBuilderImpl
15931597
Procedure noSuchMethodInterface,
15941598
KernelTarget target,
15951599
Procedure procedure) {
1600+
bool shouldThrow = false;
1601+
Name procedureName = procedure.name;
1602+
if (procedureName.isPrivate) {
1603+
Library procedureNameLibrary = procedureName.library!;
1604+
// If the name is defined in a different library than the library we're
1605+
// synthesizing a forwarder for, then the forwarder must throw. This
1606+
// avoids surprising users by ensuring that all non-throwing
1607+
// implementations of a private name can be found solely by looking at the
1608+
// library in which the name is defined; it also avoids soundness holes in
1609+
// field promotion.
1610+
if (procedureNameLibrary.compareTo(procedure.enclosingLibrary) != 0) {
1611+
shouldThrow = true;
1612+
}
1613+
}
1614+
Expression result;
15961615
String prefix = procedure.isGetter
15971616
? 'get:'
15981617
: procedure.isSetter
15991618
? 'set:'
16001619
: '';
1601-
String invocationName = prefix + procedure.name.text;
1620+
String invocationName = prefix + procedureName.text;
16021621
if (procedure.isSetter) invocationName += '=';
1622+
CoreTypes coreTypes = target.loader.coreTypes;
16031623
Expression invocation = target.backendTarget.instantiateInvocation(
1604-
target.loader.coreTypes,
1624+
coreTypes,
16051625
new ThisExpression(),
16061626
invocationName,
16071627
new Arguments.forwarded(procedure.function, libraryBuilder.library),
16081628
procedure.fileOffset,
16091629
/*isSuper=*/ false);
1610-
Expression result = new InstanceInvocation(InstanceAccessKind.Instance,
1611-
new ThisExpression(), noSuchMethodName, new Arguments([invocation]),
1612-
functionType: noSuchMethodInterface.getterType as FunctionType,
1613-
interfaceTarget: noSuchMethodInterface)
1614-
..fileOffset = procedure.fileOffset;
1615-
if (procedure.function.returnType is! VoidType) {
1616-
result = new AsExpression(result, procedure.function.returnType)
1617-
..isTypeError = true
1618-
..isForDynamic = true
1619-
..isForNonNullableByDefault = libraryBuilder.isNonNullableByDefault
1630+
if (shouldThrow) {
1631+
// Build `throw new NoSuchMethodError(this, invocation)`.
1632+
result = new Throw(new StaticInvocation(
1633+
coreTypes.noSuchMethodErrorDefaultConstructor,
1634+
new Arguments([new ThisExpression(), invocation])))
16201635
..fileOffset = procedure.fileOffset;
1636+
} else {
1637+
// Build `this.noSuchMethod(invocation)`.
1638+
result = new InstanceInvocation(InstanceAccessKind.Instance,
1639+
new ThisExpression(), noSuchMethodName, new Arguments([invocation]),
1640+
functionType: noSuchMethodInterface.getterType as FunctionType,
1641+
interfaceTarget: noSuchMethodInterface)
1642+
..fileOffset = procedure.fileOffset;
1643+
if (procedure.function.returnType is! VoidType) {
1644+
result = new AsExpression(result, procedure.function.returnType)
1645+
..isTypeError = true
1646+
..isForDynamic = true
1647+
..isForNonNullableByDefault = libraryBuilder.isNonNullableByDefault
1648+
..fileOffset = procedure.fileOffset;
1649+
}
16211650
}
16221651
procedure.function.body = new ReturnStatement(result)
16231652
..fileOffset = procedure.fileOffset

pkg/front_end/lib/src/fasta/source/source_loader.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -2706,7 +2706,7 @@ abstract class pragma {
27062706
class AbstractClassInstantiationError {}
27072707
27082708
class NoSuchMethodError {
2709-
NoSuchMethodError.withInvocation(receiver, invocation);
2709+
factory NoSuchMethodError.withInvocation(receiver, invocation) => throw '';
27102710
}
27112711
27122712
class StackTrace {}

pkg/front_end/test/spell_checking_list_code.txt

+2
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@ histogram
618618
history
619619
hit
620620
hoc
621+
holes
621622
hook
622623
hopefully
623624
horizontal
@@ -1393,6 +1394,7 @@ superinterfaces
13931394
supernode
13941395
supers
13951396
suppose
1397+
surprising
13961398
suspended
13971399
svg
13981400
sw

pkg/front_end/test/spell_checking_list_tests.txt

+7
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ cafebabe
102102
calloc
103103
camel
104104
capitalized
105+
carefully
105106
categorization
106107
causal
107108
cb
@@ -362,12 +363,14 @@ insights
362363
instrument
363364
insufficient
364365
intdiv
366+
interactions
365367
interactive
366368
interchangeable
367369
internet
368370
interpolate
369371
introducer
370372
inv
373+
involves
371374
ioo
372375
ish
373376
it'll
@@ -486,6 +489,7 @@ partfoo
486489
party
487490
pause
488491
paused
492+
pays
489493
pc
490494
periodic
491495
periodically
@@ -560,6 +564,7 @@ risky
560564
rk
561565
row
562566
rows
567+
ruin
563568
runtimes
564569
rv
565570
sanitize
@@ -598,6 +603,7 @@ smoke
598603
snull
599604
somehow
600605
sorter
606+
soundly
601607
spans
602608
spell
603609
spellcheck
@@ -694,6 +700,7 @@ untransformed
694700
untrimmed
695701
unusual
696702
unversioned
703+
upgrade
697704
upload
698705
upward
699706
uuid

pkg/front_end/testcases/dart2js/generic_usage_type_variable.dart.weak.outline.expect

+9-9
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,15 @@ static method main() → dynamic
8282
Extra constant evaluation status:
8383
Evaluated: ListLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:22:35 -> ListConstant(const <List<dynamic>*>[])
8484
Evaluated: ConstructorTearOff @ org-dartlang-testcase:///generic_usage_type_variable.dart:18:16 -> ConstructorTearOffConstant(C.name2)
85-
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:26:17 -> MapConstant(const <String*, dynamic>{})
86-
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:32:22 -> MapConstant(const <String*, dynamic>{})
87-
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:25:14 -> MapConstant(const <String*, dynamic>{})
88-
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:24:9 -> MapConstant(const <String*, dynamic>{})
89-
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:35:19 -> MapConstant(const <String*, dynamic>{})
90-
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:33:35 -> MapConstant(const <String*, dynamic>{})
91-
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:25:14 -> MapConstant(const <String*, dynamic>{})
92-
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:24:9 -> MapConstant(const <String*, dynamic>{})
93-
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:34:7 -> MapConstant(const <String*, dynamic>{})
85+
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:39:7 -> MapConstant(const <String*, dynamic>{})
86+
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:39:7 -> MapConstant(const <String*, dynamic>{})
87+
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:39:7 -> MapConstant(const <String*, dynamic>{})
88+
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:39:7 -> MapConstant(const <String*, dynamic>{})
89+
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:39:7 -> MapConstant(const <String*, dynamic>{})
90+
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:39:7 -> MapConstant(const <String*, dynamic>{})
91+
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:39:7 -> MapConstant(const <String*, dynamic>{})
92+
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:39:7 -> MapConstant(const <String*, dynamic>{})
93+
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:39:7 -> MapConstant(const <String*, dynamic>{})
9494
Evaluated: ListLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:15:23 -> ListConstant(const <C*>[])
9595
Evaluated: MapLiteral @ org-dartlang-testcase:///generic_usage_type_variable.dart:16:24 -> MapConstant(const <Type*, Type*>{dynamic: dynamic})
9696
Extra constant evaluation: evaluated: 93, effectively constant: 13

pkg/front_end/testcases/enhanced_enums/enum_as_supertype_error.dart.strong.expect

+10-10
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,17 @@ class A extends core::Enum {
9898
;
9999
get foo() → core::int
100100
return this.{core::Enum::index}{core::int};
101-
no-such-method-forwarder get /* from org-dartlang-sdk:///sdk/lib/core/enum.dart */ _name() → core::String
102-
return this.{core::Object::noSuchMethod}(new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4))){(core::Invocation) → dynamic} as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
101+
no-such-method-forwarder get _name() → core::String
102+
return throw core::NoSuchMethodError::withInvocation(this, new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4)));
103103
}
104104
class B extends core::Object implements core::Enum {
105105
synthetic constructor •() → self::B
106106
: super core::Object::•()
107107
;
108108
get foo() → core::int
109109
return this.{core::Enum::index}{core::int};
110-
no-such-method-forwarder get /* from org-dartlang-sdk:///sdk/lib/core/enum.dart */ _name() → core::String
111-
return this.{core::Object::noSuchMethod}(new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4))){(core::Invocation) → dynamic} as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
110+
no-such-method-forwarder get _name() → core::String
111+
return throw core::NoSuchMethodError::withInvocation(this, new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4)));
112112
}
113113
abstract class EnumInterface extends core::Object implements core::Enum {
114114
synthetic constructor •() → self::EnumInterface
@@ -121,8 +121,8 @@ class EnumClass extends self::EnumInterface {
121121
;
122122
get index() → core::int
123123
return 0;
124-
no-such-method-forwarder get /* from org-dartlang-sdk:///sdk/lib/core/enum.dart */ _name() → core::String
125-
return this.{core::Object::noSuchMethod}(new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4))){(core::Invocation) → dynamic} as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
124+
no-such-method-forwarder get _name() → core::String
125+
return throw core::NoSuchMethodError::withInvocation(this, new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4)));
126126
}
127127
abstract class AbstractEnumClass extends self::EnumInterface {
128128
synthetic constructor •() → self::AbstractEnumClass
@@ -133,8 +133,8 @@ class EnumClass2 extends self::AbstractEnumClass {
133133
synthetic constructor •() → self::EnumClass2
134134
: super self::AbstractEnumClass::•()
135135
;
136-
no-such-method-forwarder get /* from org-dartlang-sdk:///sdk/lib/core/enum.dart */ _name() → core::String
137-
return this.{core::Object::noSuchMethod}(new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4))){(core::Invocation) → dynamic} as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
136+
no-such-method-forwarder get _name() → core::String
137+
return throw core::NoSuchMethodError::withInvocation(this, new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4)));
138138
}
139139
abstract class EnumMixin extends core::Enum /*isMixinDeclaration*/ {
140140
}
@@ -152,8 +152,8 @@ class EnumClass3 extends self::AbstractEnumClass2 {
152152
synthetic constructor •() → self::EnumClass3
153153
: super self::AbstractEnumClass2::•()
154154
;
155-
no-such-method-forwarder get /* from org-dartlang-sdk:///sdk/lib/core/enum.dart */ _name() → core::String
156-
return this.{core::Object::noSuchMethod}(new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4))){(core::Invocation) → dynamic} as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
155+
no-such-method-forwarder get _name() → core::String
156+
return throw core::NoSuchMethodError::withInvocation(this, new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4)));
157157
}
158158
static method main() → dynamic {}
159159

pkg/front_end/testcases/enhanced_enums/enum_as_supertype_error.dart.strong.transformed.expect

+10-10
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,17 @@ class A extends core::Enum {
9898
;
9999
get foo() → core::int
100100
return this.{core::Enum::index}{core::int};
101-
no-such-method-forwarder get /* from org-dartlang-sdk:///sdk/lib/core/enum.dart */ _name() → core::String
102-
return this.{core::Object::noSuchMethod}(new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4))){(core::Invocation) → dynamic} as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
101+
no-such-method-forwarder get _name() → core::String
102+
return throw core::NoSuchMethodError::withInvocation(this, new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4)));
103103
}
104104
class B extends core::Object implements core::Enum {
105105
synthetic constructor •() → self::B
106106
: super core::Object::•()
107107
;
108108
get foo() → core::int
109109
return this.{core::Enum::index}{core::int};
110-
no-such-method-forwarder get /* from org-dartlang-sdk:///sdk/lib/core/enum.dart */ _name() → core::String
111-
return this.{core::Object::noSuchMethod}(new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4))){(core::Invocation) → dynamic} as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
110+
no-such-method-forwarder get _name() → core::String
111+
return throw core::NoSuchMethodError::withInvocation(this, new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4)));
112112
}
113113
abstract class EnumInterface extends core::Object implements core::Enum {
114114
synthetic constructor •() → self::EnumInterface
@@ -121,8 +121,8 @@ class EnumClass extends self::EnumInterface {
121121
;
122122
get index() → core::int
123123
return 0;
124-
no-such-method-forwarder get /* from org-dartlang-sdk:///sdk/lib/core/enum.dart */ _name() → core::String
125-
return this.{core::Object::noSuchMethod}(new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4))){(core::Invocation) → dynamic} as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
124+
no-such-method-forwarder get _name() → core::String
125+
return throw core::NoSuchMethodError::withInvocation(this, new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4)));
126126
}
127127
abstract class AbstractEnumClass extends self::EnumInterface {
128128
synthetic constructor •() → self::AbstractEnumClass
@@ -133,8 +133,8 @@ class EnumClass2 extends self::AbstractEnumClass {
133133
synthetic constructor •() → self::EnumClass2
134134
: super self::AbstractEnumClass::•()
135135
;
136-
no-such-method-forwarder get /* from org-dartlang-sdk:///sdk/lib/core/enum.dart */ _name() → core::String
137-
return this.{core::Object::noSuchMethod}(new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4))){(core::Invocation) → dynamic} as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
136+
no-such-method-forwarder get _name() → core::String
137+
return throw core::NoSuchMethodError::withInvocation(this, new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4)));
138138
}
139139
abstract class EnumMixin extends core::Enum /*isMixinDeclaration*/ {
140140
}
@@ -152,8 +152,8 @@ class EnumClass3 extends self::AbstractEnumClass2 {
152152
synthetic constructor •() → self::EnumClass3
153153
: super self::AbstractEnumClass2::•()
154154
;
155-
no-such-method-forwarder get /* from org-dartlang-sdk:///sdk/lib/core/enum.dart */ _name() → core::String
156-
return this.{core::Object::noSuchMethod}(new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4))){(core::Invocation) → dynamic} as{TypeError,ForDynamic,ForNonNullableByDefault} core::String;
155+
no-such-method-forwarder get _name() → core::String
156+
return throw core::NoSuchMethodError::withInvocation(this, new core::_InvocationMirror::_withType(#C1, 1, #C2, #C3, core::Map::unmodifiable<core::Symbol*, dynamic>(#C4)));
157157
}
158158
static method main() → dynamic {}
159159

0 commit comments

Comments
 (0)