Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(106)

Side by Side Diff: pkg/kernel/lib/transformations/method_call.dart

Issue 2688513004: [kernel] Rewrite method calls transformation (Closed)
Patch Set: Changes based on feedback Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « pkg/kernel/lib/transformations/empty.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2017, 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 library kernel.transformations.method_call;
6
7 import 'dart:math' as math;
8
9 import '../ast.dart';
10 import '../class_hierarchy.dart';
11 import '../core_types.dart';
12 import '../kernel.dart';
13 import '../visitor.dart';
14
15 /// Problems with the method rewrite transformation:
16 ///
17 /// * Cannot rewrite invocations to things called "call" because of tear-offs
18 /// and whatnot that when invoked turns into variableName.call(...).
19 ///
20 /// * Cannot rewrite invocations to things sharing a name with a field because
21 /// one could have called clazz.fieldName(...).
22 ///
23 /// * Rewrites will make stacktraces look weird.
24 ///
25 /// * Rewrites will make noSuchMethod look weird --- e.g. calling a non-existing
26 /// function foo(a: 42) turns into foo%0%a(42), i.e. the method name has
27 /// changed ("foo" vs "foo%0%a") and the arguments has changed (named "a" vs
28 /// positional).
29 /// NOTE: At least for now this can be fixed by changing the
30 /// invocation_mirror_patch file. Doing this I can make all dill, language
31 /// and co19 tests pass!
32 ///
33 /// Somewhat weird:
34 ///
35 /// * Inserts methods that redirect to the correct noSuchMethod invocation
36 /// so that program #1 example below will work.
37 /// The reason it otherwise wouldn't is that b.foo(499, named1: 88) is
38 /// rewritten to b.foo%1%named1(499, 88) which is not legal for class B
39 /// (thus the method would not be create there) but IS legal for Bs parent
40 /// class (A), so it would be created there. The call will thus go to the
41 /// super (A) but shouldn't as foo was overwritten in B.
42 ///
43 /// Program #1 example:
44 /// class A {
45 /// foo(required1, { named1: 499}) => print("Hello from class A");
46 /// }
47 ///
48 /// class B extends A {
49 /// foo(required1) => print("Hello from class B");
50 /// }
51 ///
52 /// main() {
53 /// var b = new B();
54 /// b.foo(499, named1: 88);
55 /// }
56 Program transformProgram(Program program, [debug = false]) {
57 new MethodCallTransformer(debug).visitProgram(program);
58 return program;
59 }
60
61 class MethodCallTransformer extends Transformer {
62 /// Keep track of "visited" procedures and constructors to not visit already
63 /// visited stuff, nor visit newly created stubs.
64 Set<Member> _visited = new Set<Member>();
65
66 /// Some things currently cannot be rewritten. Calls to methods called "call"
67 /// (because invoking tear-offs and closures and whatnot becomes .call)
68 /// as well as clashes with field names as a field can contain a function
69 /// and one can then do a clazz.fieldName(...).
70 Set<String> blacklistedSelectors = new Set<String>.from(["call"]);
71
72 /// Map from a "originally named" procedure to the "%original" procedure
73 /// for procedures that was moved.
74 Map<Procedure, Procedure> _movedBodies = {};
75
76 /// Map from a "originally named" constructor to the "%original"
77 /// constructor for constructors that was moved.
78 Map<Constructor, Constructor> _movedConstructors = {};
79
80 /// For static method transformations:
81 /// Maps a procedure to the mapping of argument signature to procedure stub.
82 Map<Procedure, Map<String, Procedure>> _staticProcedureCalls = {};
83
84 /// For constructor transformations:
85 /// Maps a constructor to a mapping of argument signature to constructor stub.
86 Map<Constructor, Map<String, Constructor>> _constructorCalls = {};
87
88 /// For non-static method transformations:
89 /// Maps from method name to the set of legal number of positional arguments.
90 Map<String, Set<int>> _methodToLegalPositionalArgumentCount = {};
91
92 /// For non-static method transformations:
93 /// Maps from name of method to the set of new target names seen for at least
94 /// one instance (i.e. rewriting has been performed from key to all values in
95 /// the mapped to set at least once).
96 Map<Name, Set<String>> _rewrittenMethods = {};
97
98 /// For non-static method transformations:
99 /// Maps a procedure to the mapping of argument signature to procedure stub.
100 Map<Procedure, Map<String, Procedure>> _superProcedureCalls = {};
101
102 /// Whether in debug mode, i.e. if we can insert extra print statements for
103 /// debugging purposes.
104 bool _debug;
105
106 /// For noSuchMethod calls.
107 ClassHierarchy hierarchy;
108 CoreTypes coreTypes;
109 Constructor _invocationMirrorConstructor; // cached
110 Procedure _listFrom; // cached
111
112 MethodCallTransformer(this._debug);
113
114 @override
115 TreeNode visitProgram(Program node) {
116 hierarchy = new ClassHierarchy(node);
117 coreTypes = new CoreTypes(node);
118
119 // First move body of all procedures that takes optional positional or named
120 // parameters and record which non-static procedure names have optional
121 // positional arguments.
122 // Do the same for constructors. Then also rewrite constructor initializers
123 // using LocalInitializer and sort named arguments in those initializers
124 for (final library in node.libraries) {
125 for (final procedure in new List<Procedure>.from(library.procedures)) {
126 _moveAndTransformProcedure(procedure);
127 }
128
129 for (final clazz in library.classes) {
130 for (final field in clazz.fields) {
131 blacklistedSelectors.add(field.name.name);
132 }
133
134 for (final procedure in new List<Procedure>.from(clazz.procedures)) {
135 // This call creates new procedures
136 _moveAndTransformProcedure(procedure);
137 _recordNonStaticProcedureAndVariableArguments(procedure);
138 }
139
140 for (final constructor
141 in new List<Constructor>.from(clazz.constructors)) {
142 // This call creates new constructors
143 _moveAndTransformConstructor(constructor);
144 }
145
146 for (final constructor in clazz.constructors) {
147 _rewriteConstructorInitializations(constructor);
148 }
149 }
150 }
151
152 // Rewrite calls
153 node.transformChildren(this);
154
155 // Now for all method calls that was rewritten, make sure those call
156 // destinations actually exist, i.e. for each method with a matching name
157 // where the called-with-arguments is legal, create a stub
158 for (final library in node.libraries) {
159 for (final clazz in library.classes) {
160 for (final procedure in new List<Procedure>.from(clazz.procedures)) {
161 // This call creates new procedures
162 _createNeededNonStaticStubs(procedure);
163 }
164 }
165 }
166
167 return node;
168 }
169
170 @override
171 TreeNode visitProcedure(Procedure node) {
172 if (!_visited.contains(node)) {
173 _visited.add(node);
174 node.transformChildren(this);
175 }
176 return node;
177 }
178
179 @override
180 TreeNode visitConstructor(Constructor node) {
181 if (!_visited.contains(node)) {
182 _visited.add(node);
183 node.transformChildren(this);
184 }
185 return node;
186 }
187
188 @override
189 TreeNode visitStaticInvocation(StaticInvocation node) {
190 node.transformChildren(this);
191 if (!_isMethod(node.target)) return node;
192 if (!_hasAnyOptionalParameters(node.target.function)) return node;
193 if (!_callIsLegal(node.target.function, node.arguments)) return node;
194
195 // Rewrite with let if needed (without named arguments it won't do anything)
196 Expression rewrittenNode = _rewriteWithLetAndSort(node, node.arguments);
197
198 // Create/lookup target and set it as the new target
199 node.target = _getNewTargetForStaticLikeInvocation(
200 node.target, node.arguments, _staticProcedureCalls);
201
202 // Now turn any named parameters into positional parameters
203 _turnNamedArgumentsIntoPositional(node.arguments);
204 return rewrittenNode;
205 }
206
207 @override
208 TreeNode visitDirectMethodInvocation(DirectMethodInvocation node) {
209 node.transformChildren(this);
210 if (!_isMethod(node.target)) return node;
211 if (!_hasAnyOptionalParameters(node.target.function)) return node;
212 if (!_callIsLegal(node.target.function, node.arguments)) return node;
213
214 // Rewrite with let if needed (without named arguments it won't do anything)
215 Expression rewrittenNode = _rewriteWithLetAndSort(node, node.arguments);
216
217 // Create/lookup target and set it as the new target
218 node.target = _getNewTargetForStaticLikeInvocation(
219 node.target, node.arguments, _superProcedureCalls);
220
221 // Now turn any named parameters into positional parameters instead
222 _turnNamedArgumentsIntoPositional(node.arguments);
223 return rewrittenNode;
224 }
225
226 @override
227 TreeNode visitSuperMethodInvocation(SuperMethodInvocation node) {
228 // SuperMethodInvocation was changed since I originally wrote this,
229 // and now it seems to never be called anyway.
230 throw "visitSuperMethodInvocation is not implemented!";
231 }
232
233 @override
234 TreeNode visitMethodInvocation(MethodInvocation node) {
235 node.transformChildren(this);
236 final name = node.name.name;
237
238 // Don't renamed calls to methods that clashes in name with a field
239 // or is called "call".
240 if (blacklistedSelectors.contains(name)) return node;
241
242 // Rewrite with let if needed (without named arguments it won't do anything)
243 Expression rewrittenNode = _rewriteWithLetAndSort(node, node.arguments);
244
245 String argumentsSignature = _createArgumentsSignature(node.arguments);
246 if (node.arguments.named.isEmpty) {
247 // Positional: Don't rewrite if no procedure with that name can be called
248 // with a variable number of arguments, or where the number of arguments
249 // called with here isn't a legal number of arguments to any such
250 // procedure.
251 // Note for named arguments: Named arguments are always rewritten
252 // (except for 'call' methods) so there's no such check
253 final okCounts = _methodToLegalPositionalArgumentCount[name];
254
255 if (okCounts == null ||
256 !okCounts.contains(node.arguments.positional.length)) {
257 return node;
258 }
259 }
260
261 // Rewrite this call
262 final originalName = node.name;
263 node.name = _createName(node.name, argumentsSignature);
264
265 // Remember that we rewrote this call
266 _rewrittenMethods
267 .putIfAbsent(originalName, () => new Set<String>())
268 .add(argumentsSignature);
269
270 // Now turn any named parameters into positional parameters instead
271 _turnNamedArgumentsIntoPositional(node.arguments);
272 return rewrittenNode;
273 }
274
275 @override
276 TreeNode visitConstructorInvocation(ConstructorInvocation node) {
277 node.transformChildren(this);
278 if (!_callIsLegal(node.target.function, node.arguments)) return node;
279
280 Expression rewrittenNode;
281 if (node.isConst) {
282 // Sort named arguments by name => it's const so there's no side-effects!
283 // but DO NOT rewrite with let!
284 node.arguments.named.sort((a, b) => a.name.compareTo(b.name));
285 rewrittenNode = node;
286 } else {
287 rewrittenNode = _rewriteWithLetAndSort(node, node.arguments);
288 }
289 node.target = _getNewTargetForConstructor(node.target, node.arguments);
290
291 // Now turn named parameters into positional parameters instead
292 _turnNamedArgumentsIntoPositional(node.arguments);
293 return rewrittenNode;
294 }
295
296 @override
297 TreeNode visitSuperInitializer(SuperInitializer node) {
298 // Note that sorting was done in _rewriteConstructorInitializations
299 node.transformChildren(this);
300 if (!_callIsLegal(node.target.function, node.arguments)) return node;
301
302 node.target = _getNewTargetForConstructor(node.target, node.arguments);
303
304 // Now turn named parameters into positional parameters instead
305 _turnNamedArgumentsIntoPositional(node.arguments);
306 return node;
307 }
308
309 @override
310 TreeNode visitRedirectingInitializer(RedirectingInitializer node) {
311 // Note that sorting was done in _rewriteConstructorInitializations
312 node.transformChildren(this);
313 if (!_callIsLegal(node.target.function, node.arguments)) return node;
314
315 node.target = _getNewTargetForConstructor(node.target, node.arguments);
316
317 // Now turn named parameters into positional parameters instead
318 _turnNamedArgumentsIntoPositional(node.arguments);
319 return node;
320 }
321
322 /// Gets the new target for an invocation, using cache or creating a new one.
323 ///
324 /// Assumes that any let-rewrite, named argument sorting etc has been done
325 /// already.
326 Procedure _getNewTargetForStaticLikeInvocation(Procedure target,
327 Arguments arguments, Map<Procedure, Map<String, Procedure>> cache) {
328 final createdProcedures = cache.putIfAbsent(target, () => {});
329
330 // Rewrite target
331 final argumentsSignature = _createArgumentsSignature(arguments);
332 return createdProcedures[argumentsSignature] ??
333 _createAndCacheInvocationProcedure(
334 argumentsSignature,
335 arguments.positional.length,
336 arguments.named.map((e) => e.name).toList(),
337 target,
338 _movedBodies[target],
339 createdProcedures,
340 true);
341 }
342
343 /// Rewrite the [Argument]s turning named arguments into positional arguments.
344 ///
345 /// Note that if the [Argument]s does not take any named parameters this
346 /// method does nothing.
347 void _turnNamedArgumentsIntoPositional(Arguments arguments) {
348 for (final named in arguments.named) {
349 arguments.positional.add(named.value..parent = arguments);
350 }
351 arguments.named.clear();
352 }
353
354 /// Gets the new target for an invocation, using cache or creating a new one.
355 ///
356 /// Assumes that any let-rewrite, named argument sorting etc has been done
357 /// already.
358 Constructor _getNewTargetForConstructor(
359 Constructor target, Arguments arguments) {
360 if (!_isNotExternal(target)) return target;
361 if (!_hasAnyOptionalParameters(target.function)) return target;
362
363 final argumentsSignature = _createArgumentsSignature(arguments);
364 final createdConstructor = _constructorCalls.putIfAbsent(target, () => {});
365 return createdConstructor[argumentsSignature] ??
366 _createAndCacheInvocationConstructor(
367 argumentsSignature,
368 arguments.positional.length,
369 arguments.named.map((e) => e.name).toList(),
370 target,
371 _movedConstructors[target],
372 createdConstructor,
373 true);
374 }
375
376 /// Create a signature for the [Arguments].
377 ///
378 /// Assumes that any needed sorting etc has already been done.
379 ///
380 /// Looks like x%positionalCount%named --- but it shouldn't matter if always
381 /// using these methods
382 String _createArgumentsSignature(Arguments arguments) {
383 String namedString = arguments.named.map((e) => e.name).join("%");
384 return "${arguments.positional.length}%$namedString";
385 }
386
387 /// Parse the argument signature.
388 ///
389 /// First element will be the string representation of the number of
390 /// positional arguments used.
391 /// The rest will be the named arguments, except that with no named arguments
392 /// there still is a 2nd entry: the empty string...
393 List<String> _parseArgumentsSignature(String argumentsSignature) {
394 return argumentsSignature.split("%");
395 }
396
397 /// Rewrites an expression with let, replacing expressions in the [Arguments].
398 ///
399 /// Sorts the named arguments after rewriting with let.
400 ///
401 /// Note that this method does nothing if there are no named arguments, or the
402 /// named arguments list contain only a single named argument as any sorting
403 /// would have no effect. As such, the let-rewrite will also have no effect.
404 /// In such a case the return value is [original].
405 Expression _rewriteWithLetAndSort(Expression original, Arguments arguments) {
406 final named = arguments.named;
407
408 // Only bother if names can be unordered
409 if (named.length < 2) return original;
410
411 // Rewrite named with let in given order
412 Let let;
413 for (int i = named.length - 1; i >= 0; i--) {
414 VariableDeclaration letDeclaration =
415 new VariableDeclaration.forValue(named[i].value);
416 named[i].value = new VariableGet(letDeclaration)..parent = arguments;
417 let = new Let(letDeclaration, let ?? original);
418 }
419
420 // Sort named arguments by name
421 named.sort((a, b) => a.name.compareTo(b.name));
422
423 // Now also add the given positional arguments into the let
424 final expressions = arguments.positional;
425 for (int i = expressions.length - 1; i >= 0; i--) {
426 VariableDeclaration letDeclaration =
427 new VariableDeclaration.forValue(expressions[i]);
428 expressions[i] = new VariableGet(letDeclaration)..parent = arguments;
429 let = new Let(letDeclaration, let ?? original);
430 }
431
432 return let;
433 }
434
435 /// Creates all needed stubs for non static procedures.
436 ///
437 /// More specifically: If calls have been made to a procedure with the same
438 /// name as this procedure with both 1 and 2 arguments, where both of these
439 /// are legal inputs to this procedure, create stubs for both of them,
440 /// each of which calls with whatever default parameter values are defined for
441 /// the non-given arguments.
442 void _createNeededNonStaticStubs(Procedure procedure) {
443 final incomingCalls = _rewrittenMethods[procedure.name];
444 if (incomingCalls != null &&
445 procedure.kind == ProcedureKind.Method &&
446 !procedure.isStatic) {
447 final createdOnSuper = _superProcedureCalls[procedure];
448 final names =
449 procedure.function.namedParameters.map((e) => e.name).toSet();
450
451 // A procedure with this name was called on at least one object with
452 // an argument signature like any in [incomingCalls]
453 nextArgumentSignature:
454 for (final argumentsSignature in incomingCalls) {
455 // Skip if it was created in a super call already
456 if (createdOnSuper != null &&
457 createdOnSuper.containsKey(argumentsSignature)) {
458 continue;
459 }
460
461 final elements = _parseArgumentsSignature(argumentsSignature);
462 int positional = int.parse(elements[0]);
463
464 if (positional < procedure.function.requiredParameterCount ||
465 positional > procedure.function.positionalParameters.length) {
466 // We don't take that number of positional parameters!
467 // Call noSuchMethod in case anyone called on object with wrong
468 // parameters, but where superclass does take these parameters.
469 _createNoSuchMethodStub(
470 argumentsSignature, positional, elements.sublist(1), procedure);
471 continue;
472 }
473
474 if (elements.length > 2 || elements[1] != "") {
475 // Named: Could the call be for this method?
476 for (int i = 1; i < elements.length; i++) {
477 String name = elements[i];
478 // Using a name that we don't have?
479 if (!names.contains(name)) {
480 // Call noSuchMethod in case anyone called on object with wrong
481 // parameters, but where superclass does take these parameters.
482 _createNoSuchMethodStub(argumentsSignature, positional,
483 elements.sublist(1), procedure);
484 continue nextArgumentSignature;
485 }
486 }
487 }
488
489 // Potential legal call => make stub
490 // Note the ?? here: E.g. contains on list doesn't take optionals so it
491 // wasn't moved, but calls were rewritten because contains on string
492 // takes either 1 or 2 arguments.
493 final destination = _movedBodies[procedure] ?? procedure;
494 _createAndCacheInvocationProcedure(argumentsSignature, positional,
495 elements.sublist(1), procedure, destination, {}, false);
496 }
497 }
498 }
499
500 /// Records how this procedure can be called (if it is non-static).
501 ///
502 /// More specifically: Assuming that the procedure given is non-static taking
503 /// a variable number of positional parameters, record all number of arguments
504 /// that is legal, e.g. foo(int a, [int b]) is legal for 1 and 2 parameters.
505 /// If it takes named parameters, remember how many positional there is so
506 /// we also know to rewrite calls without the named arguments.
507 void _recordNonStaticProcedureAndVariableArguments(Procedure procedure) {
508 if (_isMethod(procedure) &&
509 !procedure.isStatic &&
510 _hasAnyOptionalParameters(procedure.function)) {
511 final name = procedure.name.name;
512 final okCounts = _methodToLegalPositionalArgumentCount.putIfAbsent(
513 name, () => new Set<int>());
514 for (int i = procedure.function.requiredParameterCount;
515 i <= procedure.function.positionalParameters.length;
516 i++) {
517 okCounts.add(i);
518 }
519 }
520 }
521
522 /// Move body of procedure to new procedure and call that from this procedure.
523 ///
524 /// More specifically: For all procedures with optional positional parameters,
525 /// or named parameters, create a new procedure without optional positional
526 /// parameters and named parameters and move the body of the original
527 /// procedure into this new procedure.
528 /// Then make the body of the original procedure call the new procedure.
529 ///
530 /// The idea is that all rewrites should call the moved procedure instead,
531 /// bypassing the optional/named arguments entirely.
532 void _moveAndTransformProcedure(Procedure procedure) {
533 if (_isMethod(procedure) && _hasAnyOptionalParameters(procedure.function)) {
534 final function = procedure.function;
535
536 // Create variable lists
537 final newParameterDeclarations = <VariableDeclaration>[];
538 final newNamedParameterDeclarations = <VariableDeclaration>[];
539 final newParameterVariableGets = <Expression>[];
540 final targetParameters = function.positionalParameters;
541 final targetNamedParameters = function.namedParameters;
542 _moveVariableInitialization(
543 targetParameters,
544 targetNamedParameters,
545 newParameterDeclarations,
546 newNamedParameterDeclarations,
547 newParameterVariableGets,
548 procedure.function);
549
550 // Create new procedure looking like the old one
551 // (with the old body and parameters)
552 FunctionNode functionNode = _createShallowFunctionCopy(function);
553 final newProcedure = new Procedure(
554 _createOriginalName(procedure), ProcedureKind.Method, functionNode,
555 isAbstract: procedure.isAbstract,
556 isStatic: procedure.isStatic,
557 isConst: procedure.isConst,
558 fileUri: procedure.fileUri);
559
560 // Add procedure to the code
561 _addMember(procedure, newProcedure);
562
563 // Map moved body
564 _movedBodies[procedure] = newProcedure;
565
566 // Transform original procedure
567 if (procedure.isAbstract && procedure.function.body == null) {
568 // do basically nothing then
569 procedure.function.positionalParameters = newParameterDeclarations;
570 procedure.function.namedParameters = newNamedParameterDeclarations;
571 } else if (procedure.isStatic) {
572 final expression = new StaticInvocation(
573 newProcedure, new Arguments(newParameterVariableGets));
574 final statement = new ReturnStatement(expression)
575 ..parent = procedure.function;
576 procedure.function.body = statement;
577 procedure.function.positionalParameters = newParameterDeclarations;
578 procedure.function.namedParameters = newNamedParameterDeclarations;
579 } else {
580 final expression = new DirectMethodInvocation(new ThisExpression(),
581 newProcedure, new Arguments(newParameterVariableGets));
582 final statement = new ReturnStatement(expression)
583 ..parent = procedure.function;
584 procedure.function.body = statement;
585 procedure.function.positionalParameters = newParameterDeclarations;
586 procedure.function.namedParameters = newNamedParameterDeclarations;
587 }
588
589 if (_debug) {
590 // Debug flag set: Print something to the terminal before returning to
591 // easily detect if rewrites are missing.
592 Expression debugPrint = _getPrintExpression(
593 "DEBUG! Procedure shouldn't have been called...", procedure);
594 procedure.function.body = new Block(
595 [new ExpressionStatement(debugPrint), procedure.function.body])
596 ..parent = procedure.function;
597 }
598
599 // Mark original procedure as seen (i.e. don't transform it further)
600 _visited.add(procedure);
601 }
602 }
603
604 /// Rewrite constructor initializers by introducing variables and sorting.
605 ///
606 /// For any* [SuperInitializer] or [RedirectingInitializer], extract the
607 /// parameters, put them into variables, then sorting the named parameters.
608 /// The idea is to sort the named parameters without changing any invocation
609 /// order.
610 ///
611 /// * only with at least 2 named arguments, otherwise sorting would do nothing
612 void _rewriteConstructorInitializations(Constructor constructor) {
613 if (_isNotExternal(constructor)) {
614 // Basically copied from "super_calls.dart"
615 List<Initializer> initializers = constructor.initializers;
616 int foundIndex = -1;
617 Arguments arguments;
618 for (int i = initializers.length - 1; i >= 0; --i) {
619 Initializer initializer = initializers[i];
620 if (initializer is SuperInitializer) {
621 foundIndex = i;
622 arguments = initializer.arguments;
623 break;
624 } else if (initializer is RedirectingInitializer) {
625 foundIndex = i;
626 arguments = initializer.arguments;
627 break;
628 }
629 }
630 if (foundIndex == -1) return;
631
632 // Rewrite using variables if using named parameters (so we can sort them)
633 // (note that with 1 named it cannot be unsorted so we don't bother)
634 if (arguments.named.length < 2) return;
635
636 int argumentCount = arguments.positional.length + arguments.named.length;
637
638 // Make room for [argumentCount] [LocalInitializer]s before the
639 // super/redirector call.
640 initializers.length += argumentCount;
641 initializers.setRange(
642 foundIndex + argumentCount, // destination start (inclusive)
643 initializers.length, // destination end (exclusive)
644 initializers, // source list
645 foundIndex); // source start index
646
647 // Fill in the [argumentCount] reserved slots with the evaluation
648 // expressions of the arguments to the super/redirector constructor call
649 int storeIndex = foundIndex;
650 for (int i = 0; i < arguments.positional.length; ++i) {
651 var variable =
652 new VariableDeclaration.forValue(arguments.positional[i]);
653 arguments.positional[i] = new VariableGet(variable)..parent = arguments;
654 initializers[storeIndex++] = new LocalInitializer(variable)
655 ..parent = constructor;
656 }
657 for (int i = 0; i < arguments.named.length; ++i) {
658 NamedExpression argument = arguments.named[i];
659 var variable = new VariableDeclaration.forValue(argument.value);
660 arguments.named[i].value = new VariableGet(variable)..parent = argument;
661 initializers[storeIndex++] = new LocalInitializer(variable)
662 ..parent = constructor;
663 }
664
665 // Sort the named arguments
666 arguments.named.sort((a, b) => a.name.compareTo(b.name));
667 }
668 }
669
670 /// Move body of constructor to new one and call that from this one.
671 ///
672 /// More specifically: For all constructors with optional positional
673 /// parameters, or named parameters, create a new constructor without optional
674 /// positional parameters and named parameters, and move the body of the
675 /// original constructor into this new constructor.
676 /// Then make the original constructor redirect to the new constructor.
677 ///
678 /// The idea is that all rewrites should call the moved constructor instead,
679 /// bypassing the optional/named arguments entirely.
680 ///
681 /// This method is very similar to _moveAndTransformProcedure
682 void _moveAndTransformConstructor(Constructor constructor) {
683 if (_isNotExternal(constructor) &&
684 _hasAnyOptionalParameters(constructor.function)) {
685 final function = constructor.function;
686
687 // Create variable lists
688 final newParameterDeclarations = <VariableDeclaration>[];
689 final newNamedParameterDeclarations = <VariableDeclaration>[];
690 final newParameterVariableGets = <Expression>[];
691 final targetParameters = function.positionalParameters;
692 final targetNamedParameters = function.namedParameters;
693 _moveVariableInitialization(
694 targetParameters,
695 targetNamedParameters,
696 newParameterDeclarations,
697 newNamedParameterDeclarations,
698 newParameterVariableGets,
699 constructor.function);
700
701 // Create new constructor looking like the old one
702 // (with the old body, parameters and initializers)
703 FunctionNode functionNode = _createShallowFunctionCopy(function);
704 final newConstructor = new Constructor(functionNode,
705 name: _createOriginalName(constructor),
706 isConst: constructor.isConst,
707 isExternal: constructor.isExternal,
708 initializers: constructor.initializers);
709
710 // Add constructor to the code
711 _addMember(constructor, newConstructor);
712
713 // Map moved body
714 _movedConstructors[constructor] = newConstructor;
715
716 // Transform original constructor
717 constructor.function.body = null;
718 constructor.function.positionalParameters = newParameterDeclarations;
719 constructor.function.namedParameters = newNamedParameterDeclarations;
720 constructor.initializers = [
721 new RedirectingInitializer(
722 newConstructor, new Arguments(newParameterVariableGets))
723 ..parent = constructor
724 ];
725
726 if (_debug) {
727 // Debug flag set: Print something to the terminal before returning to
728 // easily detect if rewrites are missing.
729 Expression debugPrint = _getPrintExpression(
730 "DEBUG! Constructor shouldn't have been called...", constructor);
731 var variable = new VariableDeclaration.forValue(debugPrint);
732 final debugInitializer = new LocalInitializer(variable)
733 ..parent = constructor;
734 final redirector = constructor.initializers[0];
735 constructor.initializers = [debugInitializer, redirector];
736 }
737
738 // Mark original procedure as seen (i.e. don't transform it further)
739 _visited.add(constructor);
740 }
741 }
742
743 /// Creates a new [FunctionNode] based on the given one.
744 ///
745 /// Parameters are taken directly (i.e. after returning the parameters will
746 /// have a new parent (the returned value), but still be referenced in the
747 /// original [FunctionNode].
748 /// The same goes for the body of the function.
749 /// The caller should take steps to remedy this after this call.
750 ///
751 /// The parameters are no longer optional and named parameters have been
752 /// sorted and turned into regular parameters in the returned [FunctionNode].
753 FunctionNode _createShallowFunctionCopy(FunctionNode function) {
754 final newParameters =
755 new List<VariableDeclaration>.from(function.positionalParameters);
756 final named = new List<VariableDeclaration>.from(function.namedParameters);
757 named.sort((a, b) => a.name.compareTo(b.name));
758 newParameters.addAll(named);
759 final functionNode = new FunctionNode(function.body,
760 positionalParameters: newParameters,
761 namedParameters: [],
762 requiredParameterCount: newParameters.length,
763 returnType: function.returnType,
764 asyncMarker: function.asyncMarker);
765 return functionNode;
766 }
767
768 /// Creates new variables, moving old initializers into them
769 ///
770 /// Specifically: Given lists for output, create new variables based on
771 /// original parameters. Any new variable will receive the original variables
772 /// initializer, and the original variable will have its initializer set to
773 /// null.
774 /// Named parameters have been sorted in [newParameterVariableGets].
775 void _moveVariableInitialization(
776 List<VariableDeclaration> originalParameters,
777 List<VariableDeclaration> originalNamedParameters,
778 List<VariableDeclaration> newParameterDeclarations,
779 List<VariableDeclaration> newNamedParameterDeclarations,
780 List<Expression> newParameterVariableGets,
781 TreeNode newStuffParent) {
782 for (final orgVar in originalParameters) {
783 final variableDeclaration = new VariableDeclaration(orgVar.name,
784 initializer: orgVar.initializer,
785 type: orgVar.type,
786 isFinal: orgVar.isFinal,
787 isConst: orgVar.isConst)..parent = newStuffParent;
788 variableDeclaration.initializer?.parent = variableDeclaration;
789 newParameterDeclarations.add(variableDeclaration);
790 orgVar.initializer = null;
791 newParameterVariableGets.add(new VariableGet(variableDeclaration));
792 }
793
794 // Named expressions in newParameterVariableGets should be sorted
795 final tmp = new List<_Pair<String, Expression>>();
796 for (final orgVar in originalNamedParameters) {
797 final variableDeclaration = new VariableDeclaration(orgVar.name,
798 initializer: orgVar.initializer,
799 type: orgVar.type,
800 isFinal: orgVar.isFinal,
801 isConst: orgVar.isConst)..parent = newStuffParent;
802 variableDeclaration.initializer?.parent = variableDeclaration;
803 newNamedParameterDeclarations.add(variableDeclaration);
804 orgVar.initializer = null;
805 tmp.add(new _Pair(orgVar.name, new VariableGet(variableDeclaration)));
806 }
807 tmp.sort((a, b) => a.key.compareTo(b.key));
808 for (final item in tmp) {
809 newParameterVariableGets.add(item.value);
810 }
811 }
812
813 /// Creates a stub redirecting to noSuchMethod.
814 ///
815 /// Needed because if B extends A, both have a foo method, but taking
816 /// different optional parameters, a call on an instance of B with parameters
817 /// for A should actually result in a noSuchMethod call, but if only A has
818 /// the rewritten method name, that method will be called...
819 /// TODO: We only have to create these stubs for arguments that a procedures
820 /// super allows, otherwise it will become a noSuchMethod automatically!
821 Procedure _createNoSuchMethodStub(
822 String argumentsSignature,
823 int positionalCount,
824 List<String> givenNamedParameters,
825 Procedure existing) {
826 // Build parameter lists
827 final newParameterDeclarations = <VariableDeclaration>[];
828 final newParameterVariableGets = <Expression>[];
829 for (int i = 0; i < positionalCount + givenNamedParameters.length; i++) {
830 final variableDeclaration = new VariableDeclaration("v%$i");
831 newParameterDeclarations.add(variableDeclaration);
832 newParameterVariableGets.add(new VariableGet(variableDeclaration));
833 }
834
835 var procedureName = _createName(existing.name, argumentsSignature);
836
837 // Find noSuchMethod to call
838 Member noSuchMethod = hierarchy.getDispatchTarget(
839 existing.enclosingClass, new Name("noSuchMethod"));
840 Arguments argumentsToNoSuchMethod;
841
842 if (noSuchMethod.function.positionalParameters.length == 1 &&
843 noSuchMethod.function.namedParameters.isEmpty) {
844 // We have a correct noSuchMethod method.
845 ConstructorInvocation invocation = _createInvocation(
846 procedureName.name, new Arguments(newParameterVariableGets));
847 argumentsToNoSuchMethod = new Arguments([invocation]);
848 } else {
849 // Get noSuchMethod on Object then...
850 noSuchMethod = hierarchy.getDispatchTarget(
851 hierarchy.rootClass, new Name("noSuchMethod"));
852 ConstructorInvocation invocation = _createInvocation(
853 procedureName.name, new Arguments(newParameterVariableGets));
854 ConstructorInvocation invocationPrime =
855 _createInvocation("noSuchMethod", new Arguments([invocation]));
856 argumentsToNoSuchMethod = new Arguments([invocationPrime]);
857 }
858
859 // Create return statement to call noSuchMethod
860 ReturnStatement statement;
861 final expression = new DirectMethodInvocation(
862 new ThisExpression(), noSuchMethod, argumentsToNoSuchMethod);
863 statement = new ReturnStatement(expression);
864
865 // Build procedure
866 final functionNode = new FunctionNode(statement,
867 positionalParameters: newParameterDeclarations,
868 namedParameters: [],
869 requiredParameterCount: newParameterDeclarations.length,
870 returnType: existing.function.returnType,
871 asyncMarker: existing.function.asyncMarker);
872 final procedure = new Procedure(
873 procedureName, ProcedureKind.Method, functionNode,
874 isStatic: existing.isStatic, fileUri: existing.fileUri);
875
876 // Add procedure to the code
877 _addMember(existing, procedure);
878
879 // Mark the new procedure as visited already (i.e. don't rewrite it again!)
880 _visited.add(procedure);
881
882 return procedure;
883 }
884
885 /// Creates an "new _InvocationMirror(...)" invocation.
886 ConstructorInvocation _createInvocation(
887 String methodName, Arguments callArguments) {
888 if (_invocationMirrorConstructor == null) {
889 Class clazz = coreTypes.getCoreClass('dart:core', '_InvocationMirror');
890 _invocationMirrorConstructor = clazz.constructors[0];
891 }
892
893 // The _InvocationMirror constructor takes the following arguments:
894 // * Method name (a string).
895 // * An arguments descriptor - a list consisting of:
896 // - number of arguments (including receiver).
897 // - number of positional arguments (including receiver).
898 // - pairs (2 entries in the list) of
899 // * named arguments name.
900 // * index of named argument in arguments list.
901 // * A list of arguments, where the first ones are the positional arguments.
902 // * Whether it's a super invocation or not.
903
904 int numPositionalArguments = callArguments.positional.length + 1;
905 int numArguments = numPositionalArguments + callArguments.named.length;
906 List<Expression> argumentsDescriptor = [
907 new IntLiteral(numArguments),
908 new IntLiteral(numPositionalArguments)
909 ];
910 List<Expression> arguments = [];
911 arguments.add(new ThisExpression());
912 for (Expression pos in callArguments.positional) {
913 arguments.add(pos);
914 }
915 for (NamedExpression named in callArguments.named) {
916 argumentsDescriptor.add(new StringLiteral(named.name));
917 argumentsDescriptor.add(new IntLiteral(arguments.length));
918 arguments.add(named.value);
919 }
920
921 return new ConstructorInvocation(
922 _invocationMirrorConstructor,
923 new Arguments([
924 new StringLiteral(methodName),
925 _fixedLengthList(argumentsDescriptor),
926 _fixedLengthList(arguments),
927 new BoolLiteral(false)
928 ]));
929 }
930
931 /// Create a fixed length list containing given expressions.
932 Expression _fixedLengthList(List<Expression> list) {
933 if (_listFrom == null) {
934 Class clazz = coreTypes.getCoreClass('dart:core', 'List');
935 _listFrom = clazz.procedures.firstWhere((c) => c.name.name == "from");
936 }
937 return new StaticInvocation(
938 _listFrom,
939 new Arguments([new ListLiteral(list)],
940 named: [new NamedExpression("growable", new BoolLiteral(false))],
941 types: [const DynamicType()]));
942 }
943
944 /// Creates a new procedure taking given arguments, caching it.
945 ///
946 /// Copies any non-given default values for parameters into the new procedure
947 /// to be able to call the [realTarget] without using optionals and named
948 /// parameters.
949 Procedure _createAndCacheInvocationProcedure(
950 String argumentsSignature,
951 int positionalCount,
952 List<String> givenNamedParameters,
953 Procedure target,
954 Procedure realTarget,
955 Map<String, Procedure> createdProcedures,
956 bool doSpecialCaseForAllParameters) {
957 // Special case: Calling with all parameters
958 if (doSpecialCaseForAllParameters &&
959 positionalCount == target.function.positionalParameters.length &&
960 givenNamedParameters.length == target.function.namedParameters.length) {
961 // We don't cache this procedure as this could make it look like
962 // something with name argumentsSignature actually exists
963 // while it doesn't (which is bad as we could then decide that we don't
964 // need to create a stub even though we do!)
965 return realTarget;
966 }
967
968 // Create and cache (save) constructor
969
970 // Build parameter lists
971 final newParameterDeclarations = <VariableDeclaration>[];
972 final newParameterVariableGets = <Expression>[];
973 _extractAndCreateParameters(positionalCount, newParameterDeclarations,
974 newParameterVariableGets, target, givenNamedParameters);
975
976 // Create return statement to call real target
977 ReturnStatement statement;
978 if (target.isAbstract && target.function?.body == null) {
979 // statement should just be null then
980 } else if (target.isStatic) {
981 final expression = new StaticInvocation(
982 realTarget, new Arguments(newParameterVariableGets));
983 statement = new ReturnStatement(expression);
984 } else {
985 final expression = new DirectMethodInvocation(new ThisExpression(),
986 realTarget, new Arguments(newParameterVariableGets));
987 statement = new ReturnStatement(expression);
988 }
989
990 // Build procedure
991 final functionNode = new FunctionNode(statement,
992 positionalParameters: newParameterDeclarations,
993 namedParameters: [],
994 requiredParameterCount: newParameterDeclarations.length,
995 returnType: target.function.returnType,
996 asyncMarker: target.function.asyncMarker);
997 final procedure = new Procedure(
998 _createName(target.name, argumentsSignature),
999 ProcedureKind.Method,
1000 functionNode,
1001 isAbstract: target.isAbstract,
1002 isStatic: target.isStatic,
1003 isConst: target.isConst,
1004 fileUri: target.fileUri);
1005
1006 // Add procedure to the code
1007 _addMember(target, procedure);
1008
1009 // Cache it for future reference
1010 createdProcedures[argumentsSignature] = procedure;
1011
1012 // Mark the new procedure as visited already (i.e. don't rewrite it again!)
1013 _visited.add(procedure);
1014
1015 return procedure;
1016 }
1017
1018 /// Creates a new constructor taking given arguments, caching it.
1019 ///
1020 /// Copies any non-given default values for parameters into the new
1021 /// constructor to be able to call the [realTarget] without using optionals
1022 /// and named parameters.
1023 Constructor _createAndCacheInvocationConstructor(
1024 String argumentsSignature,
1025 int positionalCount,
1026 List<String> givenNamedParameters,
1027 Constructor target,
1028 Constructor realTarget,
1029 Map<String, Constructor> createdConstructor,
1030 bool doSpecialCaseForAllParameters) {
1031 // Special case: Calling with all parameters
1032 if (doSpecialCaseForAllParameters &&
1033 positionalCount == target.function.positionalParameters.length &&
1034 givenNamedParameters.length == target.function.namedParameters.length) {
1035 createdConstructor[argumentsSignature] = realTarget;
1036 return realTarget;
1037 }
1038
1039 // Create and cache (save) constructor
1040
1041 // Build parameter lists
1042 final newParameterDeclarations = <VariableDeclaration>[];
1043 final newParameterVariableGets = <Expression>[];
1044 _extractAndCreateParameters(positionalCount, newParameterDeclarations,
1045 newParameterVariableGets, target, givenNamedParameters);
1046
1047 // Build constructor
1048 final functionNode = new FunctionNode(null,
1049 positionalParameters: newParameterDeclarations,
1050 namedParameters: [],
1051 requiredParameterCount: newParameterDeclarations.length,
1052 returnType: target.function.returnType,
1053 asyncMarker: target.function.asyncMarker);
1054 final constructor = new Constructor(functionNode,
1055 name: _createName(target.name, argumentsSignature),
1056 isConst: target.isConst,
1057 isExternal: target.isExternal,
1058 initializers: [
1059 new RedirectingInitializer(
1060 realTarget, new Arguments(newParameterVariableGets))
1061 ]);
1062
1063 // Add procedure to the code
1064 _addMember(target, constructor);
1065
1066 // Cache it for future reference
1067 createdConstructor[argumentsSignature] = constructor;
1068
1069 // Mark the new procedure as visited already (i.e. don't rewrite it again!)
1070 _visited.add(constructor);
1071
1072 return constructor;
1073 }
1074
1075 /// Extracts and creates parameters into the first two given lists.
1076 ///
1077 /// What is done:
1078 /// Step 1: Re-create the parameters given (i.e. the non-optional positional
1079 /// ones) - i.e. create a new variable with the same name etc, put it in
1080 /// [newParameterDeclarations]; create VariableGet for that and put it in
1081 /// [newParameterVariableGets]
1082 /// Step 2: Re-create the positional parameters NOT given, i.e. insert
1083 /// defaults and add to [newParameterVariableGets] only.
1084 /// Step 3: Re-create the named arguments (in sorted order). For actually
1085 /// given named parameters, do as in step 1, for not-given named parameters
1086 /// do as in step 2.
1087 ///
1088 /// NOTE: [newParameterDeclarations] and [newParameterVariableGets] are OUTPUT
1089 /// lists.
1090 void _extractAndCreateParameters(
1091 int positionalCount,
1092 List<VariableDeclaration> newParameterDeclarations,
1093 List<Expression> newParameterVariableGets,
1094 Member target,
1095 List<String> givenNamedParameters) {
1096 // First re-create the parameters given (i.e. the non-optional positional on es)
1097 final targetParameters = target.function.positionalParameters;
1098 positionalCount = math.min(positionalCount, targetParameters.length);
1099 for (int i = 0; i < positionalCount; i++) {
1100 final orgVar = targetParameters[i];
1101 final variableDeclaration = new VariableDeclaration(orgVar.name,
1102 type: orgVar.type, isFinal: orgVar.isFinal, isConst: orgVar.isConst);
1103 newParameterDeclarations.add(variableDeclaration);
1104 newParameterVariableGets.add(new VariableGet(variableDeclaration));
1105 }
1106
1107 // Default parameters for the rest of them
1108 _fillInPositionalParameters(
1109 positionalCount, target, newParameterVariableGets);
1110
1111 // Then all named parameters (given here or not)
1112 final orgNamed =
1113 new List<VariableDeclaration>.from(target.function.namedParameters);
1114 orgNamed.sort((a, b) => a.name.compareTo(b.name));
1115 final givenArgumentsIterator = givenNamedParameters.iterator;
1116 givenArgumentsIterator.moveNext();
1117 for (VariableDeclaration named in orgNamed) {
1118 if (givenArgumentsIterator.current == named.name) {
1119 // We have that one: Use it and move the iterator
1120 final variableDeclaration = new VariableDeclaration(named.name);
1121 newParameterDeclarations.add(variableDeclaration);
1122 newParameterVariableGets.add(new VariableGet(variableDeclaration));
1123 givenArgumentsIterator.moveNext();
1124 } else {
1125 // We don't have that one: Fill it in
1126 _fillInSingleParameter(named, newParameterVariableGets, target);
1127 }
1128 }
1129 }
1130
1131 /// Adds the new member the same place as the existing member
1132 void _addMember(Member existingMember, Member newMember) {
1133 if (existingMember.enclosingClass != null) {
1134 existingMember.enclosingClass.addMember(newMember);
1135 } else {
1136 existingMember.enclosingLibrary.addMember(newMember);
1137 }
1138 }
1139
1140 /// Create expressions based on the default values from the given [Member].
1141 ///
1142 /// More specifically: static gets and nulls will be "copied" whereas other
1143 /// things (e.g. literals or things like "a+b") will be moved from the
1144 /// original member as argument initializers to const fields and both the
1145 /// original member and the expression-copy will use static gets to these.
1146 void _fillInPositionalParameters(
1147 int startFrom, Member copyFrom, List<Expression> fillInto) {
1148 final targetParameters = copyFrom.function.positionalParameters;
1149 for (int i = startFrom; i < targetParameters.length; i++) {
1150 final parameter = targetParameters[i];
1151 _fillInSingleParameter(parameter, fillInto, copyFrom);
1152 }
1153 }
1154
1155 /// Create expression based on the default values from the given variable.
1156 ///
1157 /// More specifically: a static get or null will be "copied" whereas other
1158 /// things (e.g. literals or things like "a+b") will be moved from the
1159 /// original member as an argument initializer to a const field and both the
1160 /// original member and the expression-copy will use a static get to it.
1161 void _fillInSingleParameter(VariableDeclaration parameter,
1162 List<Expression> fillInto, Member copyFrom) {
1163 if (parameter.initializer is StaticGet) {
1164 // Reference to const => recreate it
1165 StaticGet staticGet = parameter.initializer;
1166 fillInto.add(new StaticGet(staticGet.target));
1167 } else if (parameter.initializer == null) {
1168 // No default given => output null
1169 fillInto.add(new NullLiteral());
1170 } else if (parameter.initializer is IntLiteral) {
1171 // Int literal => recreate (or else class ByteBuffer in typed_data will
1172 // get 2 fields and the C++ code will complain!)
1173 IntLiteral value = parameter.initializer;
1174 fillInto.add(new IntLiteral(value.value));
1175 } else {
1176 // Advanced stuff => move to static const field and reference that
1177 final initializer = parameter.initializer;
1178 final f = new Field(
1179 new Name('${copyFrom.name.name}%_${parameter.name}',
1180 copyFrom.enclosingLibrary),
1181 type: parameter.type,
1182 initializer: initializer,
1183 isFinal: false,
1184 isConst: true,
1185 isStatic: true,
1186 fileUri: copyFrom.enclosingClass?.fileUri ??
1187 copyFrom.enclosingLibrary.fileUri);
1188 initializer.parent = f;
1189
1190 // Add field to the code
1191 if (copyFrom.enclosingClass != null) {
1192 copyFrom.enclosingClass.addMember(f);
1193 } else {
1194 copyFrom.enclosingLibrary.addMember(f);
1195 }
1196
1197 // Use it at the call site
1198 fillInto.add(new StaticGet(f));
1199
1200 // Now replace the initializer in the method to a StaticGet
1201 parameter.initializer = new StaticGet(f)..parent = parameter;
1202 }
1203 }
1204
1205 /// Create an "original name" for a member.
1206 ///
1207 /// Specifically, for a member "x" just returns "x%original";
1208 Name _createOriginalName(Member member) {
1209 return new Name("${member.name.name}%original", member.enclosingLibrary);
1210 }
1211
1212 /// Create a [Name] based on current name and argument signature.
1213 Name _createName(Name name, String argumentsSignature) {
1214 String nameString = '${name.name}%$argumentsSignature';
1215 return new Name(nameString, name.library);
1216 }
1217
1218 /// Is the procedure a method?
1219 bool _isMethod(Procedure procedure) => procedure.kind == ProcedureKind.Method;
1220
1221 /// Is the procedure NOT marked as external?
1222 bool _isNotExternal(Constructor constructor) => !constructor.isExternal;
1223
1224 /// Does the target function have any optional arguments? (positional/named)
1225 bool _hasAnyOptionalParameters(FunctionNode targetFunction) =>
1226 _hasOptionalParameters(targetFunction) ||
1227 _hasNamedParameters(targetFunction);
1228
1229 /// Does the target function have optional positional arguments?
1230 bool _hasOptionalParameters(FunctionNode targetFunction) =>
1231 targetFunction.positionalParameters.length >
1232 targetFunction.requiredParameterCount;
1233
1234 /// Does the target function have named parameters?
1235 bool _hasNamedParameters(FunctionNode targetFunction) =>
1236 targetFunction.namedParameters.isNotEmpty;
1237
1238 bool _callIsLegal(FunctionNode targetFunction, Arguments arguments) {
1239 if ((targetFunction.requiredParameterCount > arguments.positional.length) ||
1240 (targetFunction.positionalParameters.length <
1241 arguments.positional.length)) {
1242 // Given too few or too many positional arguments
1243 return false;
1244 }
1245
1246 // Do we give named that we don't take?
1247 Set<String> givenNamed = arguments.named.map((v) => v.name).toSet();
1248 Set<String> takenNamed =
1249 targetFunction.namedParameters.map((v) => v.name).toSet();
1250 givenNamed.removeAll(takenNamed);
1251 return givenNamed.isEmpty;
1252 }
1253
1254 // Below methods used to add debug prints etc
1255
1256 Library _getDartCoreLibrary(Program program) {
1257 if (program == null) return null;
1258 return program.libraries.firstWhere((lib) =>
1259 lib.importUri.scheme == 'dart' && lib.importUri.path == 'core');
1260 }
1261
1262 Procedure _getProcedureInLib(Library lib, String name) {
1263 if (lib == null) return null;
1264 return lib.procedures
1265 .firstWhere((procedure) => procedure.name.name == name);
1266 }
1267
1268 Procedure _getProcedureInClassInLib(
1269 Library lib, String className, String procedureName) {
1270 if (lib == null) return null;
1271 Class clazz = lib.classes.firstWhere((clazz) => clazz.name == className);
1272 return clazz.procedures
1273 .firstWhere((procedure) => procedure.name.name == procedureName);
1274 }
1275
1276 Expression _getPrintExpression(String msg, TreeNode treeNode) {
1277 TreeNode program = treeNode;
1278 while (program is! Program) program = program.parent;
1279 var finalMsg = msg;
1280 if (treeNode is Member) {
1281 finalMsg += " [ ${treeNode.name.name} ]";
1282 if (treeNode.enclosingClass != null) {
1283 finalMsg += " [ class ${treeNode.enclosingClass.name} ]";
1284 }
1285 if (treeNode.enclosingLibrary != null) {
1286 finalMsg += " [ lib ${treeNode.enclosingLibrary.name} ]";
1287 }
1288 }
1289
1290 var stacktrace = new StaticGet(_getProcedureInClassInLib(
1291 _getDartCoreLibrary(program), 'StackTrace', 'current'));
1292 var printStackTrace = new StaticInvocation(
1293 _getProcedureInLib(_getDartCoreLibrary(program), 'print'),
1294 new Arguments([
1295 new StringConcatenation([
1296 new StringLiteral(finalMsg),
1297 new StringLiteral("\n"),
1298 stacktrace,
1299 new StringLiteral("\n")
1300 ])
1301 ]));
1302
1303 return printStackTrace;
1304 }
1305 }
1306
1307 class _Pair<K, V> {
1308 final K key;
1309 final V value;
1310
1311 _Pair(this.key, this.value);
1312 }
OLDNEW
« no previous file with comments | « pkg/kernel/lib/transformations/empty.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698