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

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

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

Powered by Google App Engine
This is Rietveld 408576698