@@ -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
311328dcall (f, args, [@undefined named]) =>
0 commit comments