Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 694699c

Browse files
nshahancommit-bot@chromium.org
authored andcommitted
[dartdevc] Improve NSM errors when making dynamic calls
Add specific error text for the dynamic call failure cases: * Calling null. * Calling an object instance with a null `call()` method. * Passing too few or too many arguments. * Passing incorrect named arguments. * Passing too few or too many type arguments. * Passing type arguments to a non-generic method. This does not address the issue of a missing name. The errors still just reference 'call'. Tagging a name could be added in a future change. Issue: #36165 Change-Id: I21592ef506908559da0bfe9aac5ed5bae7fcb84e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/103645 Reviewed-by: Nicholas Shahan <nshahan@google.com> Reviewed-by: Vijay Menon <vsm@google.com> Commit-Queue: Nicholas Shahan <nshahan@google.com>
1 parent a332c41 commit 694699c

File tree

2 files changed

+43
-19
lines changed

2 files changed

+43
-19
lines changed

pkg/dev_compiler/tool/input_sdk/patch/core_patch.dart

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ class NoSuchMethodError {
645645
final List _arguments;
646646
final Map<Symbol, dynamic> _namedArguments;
647647
final List _existingArgumentNames;
648+
final Invocation _invocation;
648649

649650
@patch
650651
NoSuchMethodError(Object receiver, Symbol memberName,
@@ -654,15 +655,17 @@ class NoSuchMethodError {
654655
_memberName = memberName,
655656
_arguments = positionalArguments,
656657
_namedArguments = namedArguments,
657-
_existingArgumentNames = existingArgumentNames;
658+
_existingArgumentNames = existingArgumentNames,
659+
_invocation = null;
658660

659661
@patch
660662
NoSuchMethodError.withInvocation(Object receiver, Invocation invocation)
661663
: _receiver = receiver,
662664
_memberName = invocation.memberName,
663665
_arguments = invocation.positionalArguments,
664666
_namedArguments = invocation.namedArguments,
665-
_existingArgumentNames = null;
667+
_existingArgumentNames = null,
668+
_invocation = invocation;
666669

667670
@patch
668671
String toString() {
@@ -687,8 +690,12 @@ class NoSuchMethodError {
687690
String memberName = _symbolToString(_memberName);
688691
String receiverText = Error.safeToString(_receiver);
689692
String actualParameters = '$sb';
693+
var failureMessage = (_invocation is dart.InvocationImpl)
694+
? (_invocation as dart.InvocationImpl).failureMessage
695+
: 'method not found';
690696
if (_existingArgumentNames == null) {
691-
return "NoSuchMethodError: method not found: '$memberName'\n"
697+
return "NoSuchMethodError: '$memberName'\n"
698+
"$failureMessage\n"
692699
"Receiver: ${receiverText}\n"
693700
"Arguments: [$actualParameters]";
694701
} else {

pkg/dev_compiler/tool/input_sdk/private/ddc_runtime/operations.dart

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ class InvocationImpl extends Invocation {
1515
final bool isMethod;
1616
final bool isGetter;
1717
final bool isSetter;
18+
final String failureMessage;
1819

1920
InvocationImpl(memberName, List<Object> positionalArguments,
2021
{namedArguments,
2122
List typeArguments,
2223
this.isMethod = false,
2324
this.isGetter = false,
24-
this.isSetter = false})
25+
this.isSetter = false,
26+
this.failureMessage = 'method not found'})
2527
: memberName =
2628
isSetter ? _setterSymbol(memberName) : _dartSymbol(memberName),
2729
positionalArguments = List.unmodifiable(positionalArguments),
@@ -156,27 +158,37 @@ dput(obj, field, value, [@undefined mirrors]) {
156158
return value;
157159
}
158160

159-
/// Check that a function of a given type can be applied to
160-
/// actuals.
161-
bool _checkApply(FunctionType type, List actuals, namedActuals) {
161+
/// Returns an error message if function of a given [type] can't be applied to
162+
/// [actuals] and [namedActuals].
163+
///
164+
/// Returns `null` if all checks pass.
165+
String _argumentErrors(FunctionType type, List actuals, namedActuals) {
162166
// Check for too few required arguments.
163167
int actualsCount = JS('!', '#.length', actuals);
164168
var required = type.args;
165169
int requiredCount = JS('!', '#.length', required);
166-
if (actualsCount < requiredCount) return false;
170+
if (actualsCount < requiredCount) {
171+
return 'Dynamic call with too few arguments. '
172+
'Expected: $requiredCount Actual: $actualsCount';
173+
}
167174

168175
// Check for too many postional arguments.
169176
var extras = actualsCount - requiredCount;
170177
var optionals = type.optionals;
171-
if (extras > JS<int>('!', '#.length', optionals)) return false;
178+
if (extras > JS<int>('!', '#.length', optionals)) {
179+
return 'Dynamic call with too many arguments. '
180+
'Expected: $requiredCount Actual: $actualsCount';
181+
}
172182

173183
// Check if we have invalid named arguments.
174184
Iterable names;
175185
var named = type.named;
176186
if (namedActuals != null) {
177187
names = getOwnPropertyNames(namedActuals);
178188
for (var name in names) {
179-
if (!JS('!', '#.hasOwnProperty(#)', named, name)) return false;
189+
if (!JS('!', '#.hasOwnProperty(#)', named, name)) {
190+
return "Dynamic call with unexpected named argument '$name'.";
191+
}
180192
}
181193
}
182194
// Now that we know the signature matches, we can perform type checks.
@@ -191,7 +203,7 @@ bool _checkApply(FunctionType type, List actuals, namedActuals) {
191203
JS('', '#[#]._check(#[#])', named, name, namedActuals, name);
192204
}
193205
}
194-
return true;
206+
return null;
195207
}
196208

197209
_toSymbolName(symbol) => JS('', '''(() => {
@@ -242,14 +254,16 @@ _checkAndCall(f, ftype, obj, typeArgs, args, named, displayName) =>
242254
243255
let originalTarget = obj === void 0 ? f : obj;
244256
245-
function callNSM() {
257+
function callNSM(errorMessage) {
246258
return $noSuchMethod(originalTarget, new $InvocationImpl.new(
247259
$displayName, $args, {
248260
namedArguments: $named,
249261
typeArguments: $typeArgs,
250-
isMethod: true
262+
isMethod: true,
263+
failureMessage: errorMessage
251264
}));
252265
}
266+
if ($f == null) return callNSM('Dynamic call of null.');
253267
if (!($f instanceof Function)) {
254268
// We're not a function (and hence not a method either)
255269
// Grab the `call` method if it's not a function.
@@ -261,7 +275,8 @@ _checkAndCall(f, ftype, obj, typeArgs, args, named, displayName) =>
261275
$f = ${bindCall(f, _canonicalMember(f, 'call'))};
262276
$ftype = null;
263277
}
264-
if ($f == null) return callNSM();
278+
if ($f == null) return callNSM(
279+
"Dynamic call of object has no instance method 'call'.");
265280
}
266281
// If f is a function, but not a method (no method type)
267282
// then it should have been a function valued field, so
@@ -291,21 +306,23 @@ _checkAndCall(f, ftype, obj, typeArgs, args, named, displayName) =>
291306
if ($typeArgs == null) {
292307
$typeArgs = $ftype.instantiateDefaultBounds();
293308
} else if ($typeArgs.length != formalCount) {
294-
return callNSM();
309+
return callNSM('Dynamic call with incorrect number of type arguments. ' +
310+
'Expected: ' + formalCount + ' Actual: ' + $typeArgs.length);
295311
} else {
296312
$ftype.checkBounds($typeArgs);
297313
}
298314
$ftype = $ftype.instantiate($typeArgs);
299315
} else if ($typeArgs != null) {
300-
return callNSM();
316+
return callNSM('Dynamic call with unexpected type arguments. ' +
317+
'Expected: 0 Actual: ' + $typeArgs.length);
301318
}
302-
303-
if ($_checkApply($ftype, $args, $named)) {
319+
let errorMessage = $_argumentErrors($ftype, $args, $named);
320+
if (errorMessage == null) {
304321
if ($typeArgs != null) $args = $typeArgs.concat($args);
305322
if ($named != null) $args.push($named);
306323
return $f.apply($obj, $args);
307324
}
308-
return callNSM();
325+
return callNSM(errorMessage);
309326
})()''');
310327

311328
dcall(f, args, [@undefined named]) =>

0 commit comments

Comments
 (0)