OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /// Exception thrown when a function receives the wrong number of arguments at | |
6 /// runtime. Overridden to provide a more informative error message. | |
7 // TODO(jmesserly): should the base class support a message? | |
8 class _ArgumentMismatchException extends ClosureArgumentMismatchException { | |
9 final String _message; | |
10 const _ArgumentMismatchException(this._message); | |
11 String toString() => "Closure argument mismatch: $_message"; | |
12 } | |
13 | |
14 /// Implementation details for [Function] | |
15 /// Note: we don't expose this because it has no useful API. It's just some | |
16 /// helpers for our dynamic calling convention. Maybe in the future there will | |
17 /// be more stuff here, though. | |
18 class _FunctionImplementation implements Function native "Function" { | |
19 /** | |
20 * Generates a dynamic call stub for a function. | |
21 * Our goal is to create a stub method like this on-the-fly: | |
22 * function($0, $1, capture) { return this($0, $1, true, capture); } | |
23 * | |
24 * This stub then replaces the dynamic one on Function, with one that is | |
25 * specialized for that particular function, taking into account its default | |
26 * arguments. | |
27 */ | |
28 _genStub(argsLength, [names]) native @''' | |
29 // Fast path #1: if no named arguments and arg count matches. | |
30 var thisLength = this.$length || this.length; | |
31 if (thisLength == argsLength && !names) { | |
32 return this; | |
33 } | |
34 | |
35 var paramsNamed = this.$optional ? (this.$optional.length / 2) : 0; | |
36 var paramsBare = thisLength - paramsNamed; | |
37 var argsNamed = names ? names.length : 0; | |
38 var argsBare = argsLength - argsNamed; | |
39 | |
40 // Check we got the right number of arguments | |
41 if (argsBare < paramsBare || argsLength > thisLength || | |
42 argsNamed > paramsNamed) { | |
43 return function() { | |
44 $throw(new _ArgumentMismatchException( | |
45 'Wrong number of arguments to function. Expected ' + paramsBare + | |
46 ' positional arguments and at most ' + paramsNamed + | |
47 ' named arguments, but got ' + argsBare + | |
48 ' positional arguments and ' + argsNamed + ' named arguments.')); | |
49 }; | |
50 } | |
51 | |
52 // First, fill in all of the default values | |
53 var p = new Array(paramsBare); | |
54 if (paramsNamed) { | |
55 p = p.concat(this.$optional.slice(paramsNamed)); | |
56 } | |
57 // Fill in positional args | |
58 var a = new Array(argsLength); | |
59 for (var i = 0; i < argsBare; i++) { | |
60 p[i] = a[i] = '$' + i; | |
61 } | |
62 // Then overwrite with supplied values for optional args | |
63 var lastParameterIndex; | |
64 var namesInOrder = true; | |
65 for (var i = 0; i < argsNamed; i++) { | |
66 var name = names[i]; | |
67 a[i + argsBare] = name; | |
68 var j = this.$optional.indexOf(name); | |
69 if (j < 0 || j >= paramsNamed) { | |
70 return function() { | |
71 $throw(new _ArgumentMismatchException( | |
72 'Named argument "' + name + '" was not expected by function.' + | |
73 ' Did you forget to mark the function parameter [optional]?')); | |
74 }; | |
75 } else if (lastParameterIndex && lastParameterIndex > j) { | |
76 namesInOrder = false; | |
77 } | |
78 p[j + paramsBare] = name; | |
79 lastParameterIndex = j; | |
80 } | |
81 | |
82 if (thisLength == argsLength && namesInOrder) { | |
83 // Fast path #2: named arguments, but they're in order and all supplied. | |
84 return this; | |
85 } | |
86 | |
87 // Note: using Function instead of 'eval' to get a clean scope. | |
88 // TODO(jmesserly): evaluate the performance of these stubs. | |
89 var f = 'function(' + a.join(',') + '){return $f(' + p.join(',') + ');}'; | |
90 return new Function('$f', 'return ' + f + '').call(null, this); | |
91 ''' { | |
92 throw new _ArgumentMismatchException(''); | |
93 } | |
94 } | |
OLD | NEW |