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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/kernel/lib/transformations/empty.dart ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/kernel/lib/transformations/method_call.dart
diff --git a/pkg/kernel/lib/transformations/method_call.dart b/pkg/kernel/lib/transformations/method_call.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6784dd353edcca3efcefde07fdba29687c90f649
--- /dev/null
+++ b/pkg/kernel/lib/transformations/method_call.dart
@@ -0,0 +1,1312 @@
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library kernel.transformations.method_call;
+
+import 'dart:math' as math;
+
+import '../ast.dart';
+import '../class_hierarchy.dart';
+import '../core_types.dart';
+import '../kernel.dart';
+import '../visitor.dart';
+
+/// Problems with the method rewrite transformation:
+///
+/// * Cannot rewrite invocations to things called "call" because of tear-offs
+/// and whatnot that when invoked turns into variableName.call(...).
+///
+/// * Cannot rewrite invocations to things sharing a name with a field because
+/// one could have called clazz.fieldName(...).
+///
+/// * Rewrites will make stacktraces look weird.
+///
+/// * Rewrites will make noSuchMethod look weird --- e.g. calling a non-existing
+/// function foo(a: 42) turns into foo%0%a(42), i.e. the method name has
+/// changed ("foo" vs "foo%0%a") and the arguments has changed (named "a" vs
+/// positional).
+/// NOTE: At least for now this can be fixed by changing the
+/// invocation_mirror_patch file. Doing this I can make all dill, language
+/// and co19 tests pass!
+///
+/// Somewhat weird:
+///
+/// * Inserts methods that redirect to the correct noSuchMethod invocation
+/// so that program #1 example below will work.
+/// The reason it otherwise wouldn't is that b.foo(499, named1: 88) is
+/// rewritten to b.foo%1%named1(499, 88) which is not legal for class B
+/// (thus the method would not be create there) but IS legal for Bs parent
+/// class (A), so it would be created there. The call will thus go to the
+/// super (A) but shouldn't as foo was overwritten in B.
+///
+/// Program #1 example:
+/// class A {
+/// foo(required1, { named1: 499}) => print("Hello from class A");
+/// }
+///
+/// class B extends A {
+/// foo(required1) => print("Hello from class B");
+/// }
+///
+/// main() {
+/// var b = new B();
+/// b.foo(499, named1: 88);
+/// }
+Program transformProgram(Program program, [debug = false]) {
+ new MethodCallTransformer(debug).visitProgram(program);
+ return program;
+}
+
+class MethodCallTransformer extends Transformer {
+ /// Keep track of "visited" procedures and constructors to not visit already
+ /// visited stuff, nor visit newly created stubs.
+ Set<Member> _visited = new Set<Member>();
+
+ /// Some things currently cannot be rewritten. Calls to methods called "call"
+ /// (because invoking tear-offs and closures and whatnot becomes .call)
+ /// as well as clashes with field names as a field can contain a function
+ /// and one can then do a clazz.fieldName(...).
+ Set<String> blacklistedSelectors = new Set<String>.from(["call"]);
+
+ /// Map from a "originally named" procedure to the "%original" procedure
+ /// for procedures that was moved.
+ Map<Procedure, Procedure> _movedBodies = {};
+
+ /// Map from a "originally named" constructor to the "%original"
+ /// constructor for constructors that was moved.
+ Map<Constructor, Constructor> _movedConstructors = {};
+
+ /// For static method transformations:
+ /// Maps a procedure to the mapping of argument signature to procedure stub.
+ Map<Procedure, Map<String, Procedure>> _staticProcedureCalls = {};
+
+ /// For constructor transformations:
+ /// Maps a constructor to a mapping of argument signature to constructor stub.
+ Map<Constructor, Map<String, Constructor>> _constructorCalls = {};
+
+ /// For non-static method transformations:
+ /// Maps from method name to the set of legal number of positional arguments.
+ Map<String, Set<int>> _methodToLegalPositionalArgumentCount = {};
+
+ /// For non-static method transformations:
+ /// Maps from name of method to the set of new target names seen for at least
+ /// one instance (i.e. rewriting has been performed from key to all values in
+ /// the mapped to set at least once).
+ Map<Name, Set<String>> _rewrittenMethods = {};
+
+ /// For non-static method transformations:
+ /// Maps a procedure to the mapping of argument signature to procedure stub.
+ Map<Procedure, Map<String, Procedure>> _superProcedureCalls = {};
+
+ /// Whether in debug mode, i.e. if we can insert extra print statements for
+ /// debugging purposes.
+ bool _debug;
+
+ /// For noSuchMethod calls.
+ ClassHierarchy hierarchy;
+ CoreTypes coreTypes;
+ Constructor _invocationMirrorConstructor; // cached
+ Procedure _listFrom; // cached
+
+ MethodCallTransformer(this._debug);
+
+ @override
+ TreeNode visitProgram(Program node) {
+ hierarchy = new ClassHierarchy(node);
+ coreTypes = new CoreTypes(node);
+
+ // First move body of all procedures that takes optional positional or named
+ // parameters and record which non-static procedure names have optional
+ // positional arguments.
+ // Do the same for constructors. Then also rewrite constructor initializers
+ // using LocalInitializer and sort named arguments in those initializers
+ for (final library in node.libraries) {
+ for (final procedure in new List<Procedure>.from(library.procedures)) {
+ _moveAndTransformProcedure(procedure);
+ }
+
+ for (final clazz in library.classes) {
+ for (final field in clazz.fields) {
+ blacklistedSelectors.add(field.name.name);
+ }
+
+ for (final procedure in new List<Procedure>.from(clazz.procedures)) {
+ // This call creates new procedures
+ _moveAndTransformProcedure(procedure);
+ _recordNonStaticProcedureAndVariableArguments(procedure);
+ }
+
+ for (final constructor
+ in new List<Constructor>.from(clazz.constructors)) {
+ // This call creates new constructors
+ _moveAndTransformConstructor(constructor);
+ }
+
+ for (final constructor in clazz.constructors) {
+ _rewriteConstructorInitializations(constructor);
+ }
+ }
+ }
+
+ // Rewrite calls
+ node.transformChildren(this);
+
+ // Now for all method calls that was rewritten, make sure those call
+ // destinations actually exist, i.e. for each method with a matching name
+ // where the called-with-arguments is legal, create a stub
+ for (final library in node.libraries) {
+ for (final clazz in library.classes) {
+ for (final procedure in new List<Procedure>.from(clazz.procedures)) {
+ // This call creates new procedures
+ _createNeededNonStaticStubs(procedure);
+ }
+ }
+ }
+
+ return node;
+ }
+
+ @override
+ TreeNode visitProcedure(Procedure node) {
+ if (!_visited.contains(node)) {
+ _visited.add(node);
+ node.transformChildren(this);
+ }
+ return node;
+ }
+
+ @override
+ TreeNode visitConstructor(Constructor node) {
+ if (!_visited.contains(node)) {
+ _visited.add(node);
+ node.transformChildren(this);
+ }
+ return node;
+ }
+
+ @override
+ TreeNode visitStaticInvocation(StaticInvocation node) {
+ node.transformChildren(this);
+ if (!_isMethod(node.target)) return node;
+ if (!_hasAnyOptionalParameters(node.target.function)) return node;
+ if (!_callIsLegal(node.target.function, node.arguments)) return node;
+
+ // Rewrite with let if needed (without named arguments it won't do anything)
+ Expression rewrittenNode = _rewriteWithLetAndSort(node, node.arguments);
+
+ // Create/lookup target and set it as the new target
+ node.target = _getNewTargetForStaticLikeInvocation(
+ node.target, node.arguments, _staticProcedureCalls);
+
+ // Now turn any named parameters into positional parameters
+ _turnNamedArgumentsIntoPositional(node.arguments);
+ return rewrittenNode;
+ }
+
+ @override
+ TreeNode visitDirectMethodInvocation(DirectMethodInvocation node) {
+ node.transformChildren(this);
+ if (!_isMethod(node.target)) return node;
+ if (!_hasAnyOptionalParameters(node.target.function)) return node;
+ if (!_callIsLegal(node.target.function, node.arguments)) return node;
+
+ // Rewrite with let if needed (without named arguments it won't do anything)
+ Expression rewrittenNode = _rewriteWithLetAndSort(node, node.arguments);
+
+ // Create/lookup target and set it as the new target
+ node.target = _getNewTargetForStaticLikeInvocation(
+ node.target, node.arguments, _superProcedureCalls);
+
+ // Now turn any named parameters into positional parameters instead
+ _turnNamedArgumentsIntoPositional(node.arguments);
+ return rewrittenNode;
+ }
+
+ @override
+ TreeNode visitSuperMethodInvocation(SuperMethodInvocation node) {
+ // SuperMethodInvocation was changed since I originally wrote this,
+ // and now it seems to never be called anyway.
+ throw "visitSuperMethodInvocation is not implemented!";
+ }
+
+ @override
+ TreeNode visitMethodInvocation(MethodInvocation node) {
+ node.transformChildren(this);
+ final name = node.name.name;
+
+ // Don't renamed calls to methods that clashes in name with a field
+ // or is called "call".
+ if (blacklistedSelectors.contains(name)) return node;
+
+ // Rewrite with let if needed (without named arguments it won't do anything)
+ Expression rewrittenNode = _rewriteWithLetAndSort(node, node.arguments);
+
+ String argumentsSignature = _createArgumentsSignature(node.arguments);
+ if (node.arguments.named.isEmpty) {
+ // Positional: Don't rewrite if no procedure with that name can be called
+ // with a variable number of arguments, or where the number of arguments
+ // called with here isn't a legal number of arguments to any such
+ // procedure.
+ // Note for named arguments: Named arguments are always rewritten
+ // (except for 'call' methods) so there's no such check
+ final okCounts = _methodToLegalPositionalArgumentCount[name];
+
+ if (okCounts == null ||
+ !okCounts.contains(node.arguments.positional.length)) {
+ return node;
+ }
+ }
+
+ // Rewrite this call
+ final originalName = node.name;
+ node.name = _createName(node.name, argumentsSignature);
+
+ // Remember that we rewrote this call
+ _rewrittenMethods
+ .putIfAbsent(originalName, () => new Set<String>())
+ .add(argumentsSignature);
+
+ // Now turn any named parameters into positional parameters instead
+ _turnNamedArgumentsIntoPositional(node.arguments);
+ return rewrittenNode;
+ }
+
+ @override
+ TreeNode visitConstructorInvocation(ConstructorInvocation node) {
+ node.transformChildren(this);
+ if (!_callIsLegal(node.target.function, node.arguments)) return node;
+
+ Expression rewrittenNode;
+ if (node.isConst) {
+ // Sort named arguments by name => it's const so there's no side-effects!
+ // but DO NOT rewrite with let!
+ node.arguments.named.sort((a, b) => a.name.compareTo(b.name));
+ rewrittenNode = node;
+ } else {
+ rewrittenNode = _rewriteWithLetAndSort(node, node.arguments);
+ }
+ node.target = _getNewTargetForConstructor(node.target, node.arguments);
+
+ // Now turn named parameters into positional parameters instead
+ _turnNamedArgumentsIntoPositional(node.arguments);
+ return rewrittenNode;
+ }
+
+ @override
+ TreeNode visitSuperInitializer(SuperInitializer node) {
+ // Note that sorting was done in _rewriteConstructorInitializations
+ node.transformChildren(this);
+ if (!_callIsLegal(node.target.function, node.arguments)) return node;
+
+ node.target = _getNewTargetForConstructor(node.target, node.arguments);
+
+ // Now turn named parameters into positional parameters instead
+ _turnNamedArgumentsIntoPositional(node.arguments);
+ return node;
+ }
+
+ @override
+ TreeNode visitRedirectingInitializer(RedirectingInitializer node) {
+ // Note that sorting was done in _rewriteConstructorInitializations
+ node.transformChildren(this);
+ if (!_callIsLegal(node.target.function, node.arguments)) return node;
+
+ node.target = _getNewTargetForConstructor(node.target, node.arguments);
+
+ // Now turn named parameters into positional parameters instead
+ _turnNamedArgumentsIntoPositional(node.arguments);
+ return node;
+ }
+
+ /// Gets the new target for an invocation, using cache or creating a new one.
+ ///
+ /// Assumes that any let-rewrite, named argument sorting etc has been done
+ /// already.
+ Procedure _getNewTargetForStaticLikeInvocation(Procedure target,
+ Arguments arguments, Map<Procedure, Map<String, Procedure>> cache) {
+ final createdProcedures = cache.putIfAbsent(target, () => {});
+
+ // Rewrite target
+ final argumentsSignature = _createArgumentsSignature(arguments);
+ return createdProcedures[argumentsSignature] ??
+ _createAndCacheInvocationProcedure(
+ argumentsSignature,
+ arguments.positional.length,
+ arguments.named.map((e) => e.name).toList(),
+ target,
+ _movedBodies[target],
+ createdProcedures,
+ true);
+ }
+
+ /// Rewrite the [Argument]s turning named arguments into positional arguments.
+ ///
+ /// Note that if the [Argument]s does not take any named parameters this
+ /// method does nothing.
+ void _turnNamedArgumentsIntoPositional(Arguments arguments) {
+ for (final named in arguments.named) {
+ arguments.positional.add(named.value..parent = arguments);
+ }
+ arguments.named.clear();
+ }
+
+ /// Gets the new target for an invocation, using cache or creating a new one.
+ ///
+ /// Assumes that any let-rewrite, named argument sorting etc has been done
+ /// already.
+ Constructor _getNewTargetForConstructor(
+ Constructor target, Arguments arguments) {
+ if (!_isNotExternal(target)) return target;
+ if (!_hasAnyOptionalParameters(target.function)) return target;
+
+ final argumentsSignature = _createArgumentsSignature(arguments);
+ final createdConstructor = _constructorCalls.putIfAbsent(target, () => {});
+ return createdConstructor[argumentsSignature] ??
+ _createAndCacheInvocationConstructor(
+ argumentsSignature,
+ arguments.positional.length,
+ arguments.named.map((e) => e.name).toList(),
+ target,
+ _movedConstructors[target],
+ createdConstructor,
+ true);
+ }
+
+ /// Create a signature for the [Arguments].
+ ///
+ /// Assumes that any needed sorting etc has already been done.
+ ///
+ /// Looks like x%positionalCount%named --- but it shouldn't matter if always
+ /// using these methods
+ String _createArgumentsSignature(Arguments arguments) {
+ String namedString = arguments.named.map((e) => e.name).join("%");
+ return "${arguments.positional.length}%$namedString";
+ }
+
+ /// Parse the argument signature.
+ ///
+ /// First element will be the string representation of the number of
+ /// positional arguments used.
+ /// The rest will be the named arguments, except that with no named arguments
+ /// there still is a 2nd entry: the empty string...
+ List<String> _parseArgumentsSignature(String argumentsSignature) {
+ return argumentsSignature.split("%");
+ }
+
+ /// Rewrites an expression with let, replacing expressions in the [Arguments].
+ ///
+ /// Sorts the named arguments after rewriting with let.
+ ///
+ /// Note that this method does nothing if there are no named arguments, or the
+ /// named arguments list contain only a single named argument as any sorting
+ /// would have no effect. As such, the let-rewrite will also have no effect.
+ /// In such a case the return value is [original].
+ Expression _rewriteWithLetAndSort(Expression original, Arguments arguments) {
+ final named = arguments.named;
+
+ // Only bother if names can be unordered
+ if (named.length < 2) return original;
+
+ // Rewrite named with let in given order
+ Let let;
+ for (int i = named.length - 1; i >= 0; i--) {
+ VariableDeclaration letDeclaration =
+ new VariableDeclaration.forValue(named[i].value);
+ named[i].value = new VariableGet(letDeclaration)..parent = arguments;
+ let = new Let(letDeclaration, let ?? original);
+ }
+
+ // Sort named arguments by name
+ named.sort((a, b) => a.name.compareTo(b.name));
+
+ // Now also add the given positional arguments into the let
+ final expressions = arguments.positional;
+ for (int i = expressions.length - 1; i >= 0; i--) {
+ VariableDeclaration letDeclaration =
+ new VariableDeclaration.forValue(expressions[i]);
+ expressions[i] = new VariableGet(letDeclaration)..parent = arguments;
+ let = new Let(letDeclaration, let ?? original);
+ }
+
+ return let;
+ }
+
+ /// Creates all needed stubs for non static procedures.
+ ///
+ /// More specifically: If calls have been made to a procedure with the same
+ /// name as this procedure with both 1 and 2 arguments, where both of these
+ /// are legal inputs to this procedure, create stubs for both of them,
+ /// each of which calls with whatever default parameter values are defined for
+ /// the non-given arguments.
+ void _createNeededNonStaticStubs(Procedure procedure) {
+ final incomingCalls = _rewrittenMethods[procedure.name];
+ if (incomingCalls != null &&
+ procedure.kind == ProcedureKind.Method &&
+ !procedure.isStatic) {
+ final createdOnSuper = _superProcedureCalls[procedure];
+ final names =
+ procedure.function.namedParameters.map((e) => e.name).toSet();
+
+ // A procedure with this name was called on at least one object with
+ // an argument signature like any in [incomingCalls]
+ nextArgumentSignature:
+ for (final argumentsSignature in incomingCalls) {
+ // Skip if it was created in a super call already
+ if (createdOnSuper != null &&
+ createdOnSuper.containsKey(argumentsSignature)) {
+ continue;
+ }
+
+ final elements = _parseArgumentsSignature(argumentsSignature);
+ int positional = int.parse(elements[0]);
+
+ if (positional < procedure.function.requiredParameterCount ||
+ positional > procedure.function.positionalParameters.length) {
+ // We don't take that number of positional parameters!
+ // Call noSuchMethod in case anyone called on object with wrong
+ // parameters, but where superclass does take these parameters.
+ _createNoSuchMethodStub(
+ argumentsSignature, positional, elements.sublist(1), procedure);
+ continue;
+ }
+
+ if (elements.length > 2 || elements[1] != "") {
+ // Named: Could the call be for this method?
+ for (int i = 1; i < elements.length; i++) {
+ String name = elements[i];
+ // Using a name that we don't have?
+ if (!names.contains(name)) {
+ // Call noSuchMethod in case anyone called on object with wrong
+ // parameters, but where superclass does take these parameters.
+ _createNoSuchMethodStub(argumentsSignature, positional,
+ elements.sublist(1), procedure);
+ continue nextArgumentSignature;
+ }
+ }
+ }
+
+ // Potential legal call => make stub
+ // Note the ?? here: E.g. contains on list doesn't take optionals so it
+ // wasn't moved, but calls were rewritten because contains on string
+ // takes either 1 or 2 arguments.
+ final destination = _movedBodies[procedure] ?? procedure;
+ _createAndCacheInvocationProcedure(argumentsSignature, positional,
+ elements.sublist(1), procedure, destination, {}, false);
+ }
+ }
+ }
+
+ /// Records how this procedure can be called (if it is non-static).
+ ///
+ /// More specifically: Assuming that the procedure given is non-static taking
+ /// a variable number of positional parameters, record all number of arguments
+ /// that is legal, e.g. foo(int a, [int b]) is legal for 1 and 2 parameters.
+ /// If it takes named parameters, remember how many positional there is so
+ /// we also know to rewrite calls without the named arguments.
+ void _recordNonStaticProcedureAndVariableArguments(Procedure procedure) {
+ if (_isMethod(procedure) &&
+ !procedure.isStatic &&
+ _hasAnyOptionalParameters(procedure.function)) {
+ final name = procedure.name.name;
+ final okCounts = _methodToLegalPositionalArgumentCount.putIfAbsent(
+ name, () => new Set<int>());
+ for (int i = procedure.function.requiredParameterCount;
+ i <= procedure.function.positionalParameters.length;
+ i++) {
+ okCounts.add(i);
+ }
+ }
+ }
+
+ /// Move body of procedure to new procedure and call that from this procedure.
+ ///
+ /// More specifically: For all procedures with optional positional parameters,
+ /// or named parameters, create a new procedure without optional positional
+ /// parameters and named parameters and move the body of the original
+ /// procedure into this new procedure.
+ /// Then make the body of the original procedure call the new procedure.
+ ///
+ /// The idea is that all rewrites should call the moved procedure instead,
+ /// bypassing the optional/named arguments entirely.
+ void _moveAndTransformProcedure(Procedure procedure) {
+ if (_isMethod(procedure) && _hasAnyOptionalParameters(procedure.function)) {
+ final function = procedure.function;
+
+ // Create variable lists
+ final newParameterDeclarations = <VariableDeclaration>[];
+ final newNamedParameterDeclarations = <VariableDeclaration>[];
+ final newParameterVariableGets = <Expression>[];
+ final targetParameters = function.positionalParameters;
+ final targetNamedParameters = function.namedParameters;
+ _moveVariableInitialization(
+ targetParameters,
+ targetNamedParameters,
+ newParameterDeclarations,
+ newNamedParameterDeclarations,
+ newParameterVariableGets,
+ procedure.function);
+
+ // Create new procedure looking like the old one
+ // (with the old body and parameters)
+ FunctionNode functionNode = _createShallowFunctionCopy(function);
+ final newProcedure = new Procedure(
+ _createOriginalName(procedure), ProcedureKind.Method, functionNode,
+ isAbstract: procedure.isAbstract,
+ isStatic: procedure.isStatic,
+ isConst: procedure.isConst,
+ fileUri: procedure.fileUri);
+
+ // Add procedure to the code
+ _addMember(procedure, newProcedure);
+
+ // Map moved body
+ _movedBodies[procedure] = newProcedure;
+
+ // Transform original procedure
+ if (procedure.isAbstract && procedure.function.body == null) {
+ // do basically nothing then
+ procedure.function.positionalParameters = newParameterDeclarations;
+ procedure.function.namedParameters = newNamedParameterDeclarations;
+ } else if (procedure.isStatic) {
+ final expression = new StaticInvocation(
+ newProcedure, new Arguments(newParameterVariableGets));
+ final statement = new ReturnStatement(expression)
+ ..parent = procedure.function;
+ procedure.function.body = statement;
+ procedure.function.positionalParameters = newParameterDeclarations;
+ procedure.function.namedParameters = newNamedParameterDeclarations;
+ } else {
+ final expression = new DirectMethodInvocation(new ThisExpression(),
+ newProcedure, new Arguments(newParameterVariableGets));
+ final statement = new ReturnStatement(expression)
+ ..parent = procedure.function;
+ procedure.function.body = statement;
+ procedure.function.positionalParameters = newParameterDeclarations;
+ procedure.function.namedParameters = newNamedParameterDeclarations;
+ }
+
+ if (_debug) {
+ // Debug flag set: Print something to the terminal before returning to
+ // easily detect if rewrites are missing.
+ Expression debugPrint = _getPrintExpression(
+ "DEBUG! Procedure shouldn't have been called...", procedure);
+ procedure.function.body = new Block(
+ [new ExpressionStatement(debugPrint), procedure.function.body])
+ ..parent = procedure.function;
+ }
+
+ // Mark original procedure as seen (i.e. don't transform it further)
+ _visited.add(procedure);
+ }
+ }
+
+ /// Rewrite constructor initializers by introducing variables and sorting.
+ ///
+ /// For any* [SuperInitializer] or [RedirectingInitializer], extract the
+ /// parameters, put them into variables, then sorting the named parameters.
+ /// The idea is to sort the named parameters without changing any invocation
+ /// order.
+ ///
+ /// * only with at least 2 named arguments, otherwise sorting would do nothing
+ void _rewriteConstructorInitializations(Constructor constructor) {
+ if (_isNotExternal(constructor)) {
+ // Basically copied from "super_calls.dart"
+ List<Initializer> initializers = constructor.initializers;
+ int foundIndex = -1;
+ Arguments arguments;
+ for (int i = initializers.length - 1; i >= 0; --i) {
+ Initializer initializer = initializers[i];
+ if (initializer is SuperInitializer) {
+ foundIndex = i;
+ arguments = initializer.arguments;
+ break;
+ } else if (initializer is RedirectingInitializer) {
+ foundIndex = i;
+ arguments = initializer.arguments;
+ break;
+ }
+ }
+ if (foundIndex == -1) return;
+
+ // Rewrite using variables if using named parameters (so we can sort them)
+ // (note that with 1 named it cannot be unsorted so we don't bother)
+ if (arguments.named.length < 2) return;
+
+ int argumentCount = arguments.positional.length + arguments.named.length;
+
+ // Make room for [argumentCount] [LocalInitializer]s before the
+ // super/redirector call.
+ initializers.length += argumentCount;
+ initializers.setRange(
+ foundIndex + argumentCount, // destination start (inclusive)
+ initializers.length, // destination end (exclusive)
+ initializers, // source list
+ foundIndex); // source start index
+
+ // Fill in the [argumentCount] reserved slots with the evaluation
+ // expressions of the arguments to the super/redirector constructor call
+ int storeIndex = foundIndex;
+ for (int i = 0; i < arguments.positional.length; ++i) {
+ var variable =
+ new VariableDeclaration.forValue(arguments.positional[i]);
+ arguments.positional[i] = new VariableGet(variable)..parent = arguments;
+ initializers[storeIndex++] = new LocalInitializer(variable)
+ ..parent = constructor;
+ }
+ for (int i = 0; i < arguments.named.length; ++i) {
+ NamedExpression argument = arguments.named[i];
+ var variable = new VariableDeclaration.forValue(argument.value);
+ arguments.named[i].value = new VariableGet(variable)..parent = argument;
+ initializers[storeIndex++] = new LocalInitializer(variable)
+ ..parent = constructor;
+ }
+
+ // Sort the named arguments
+ arguments.named.sort((a, b) => a.name.compareTo(b.name));
+ }
+ }
+
+ /// Move body of constructor to new one and call that from this one.
+ ///
+ /// More specifically: For all constructors with optional positional
+ /// parameters, or named parameters, create a new constructor without optional
+ /// positional parameters and named parameters, and move the body of the
+ /// original constructor into this new constructor.
+ /// Then make the original constructor redirect to the new constructor.
+ ///
+ /// The idea is that all rewrites should call the moved constructor instead,
+ /// bypassing the optional/named arguments entirely.
+ ///
+ /// This method is very similar to _moveAndTransformProcedure
+ void _moveAndTransformConstructor(Constructor constructor) {
+ if (_isNotExternal(constructor) &&
+ _hasAnyOptionalParameters(constructor.function)) {
+ final function = constructor.function;
+
+ // Create variable lists
+ final newParameterDeclarations = <VariableDeclaration>[];
+ final newNamedParameterDeclarations = <VariableDeclaration>[];
+ final newParameterVariableGets = <Expression>[];
+ final targetParameters = function.positionalParameters;
+ final targetNamedParameters = function.namedParameters;
+ _moveVariableInitialization(
+ targetParameters,
+ targetNamedParameters,
+ newParameterDeclarations,
+ newNamedParameterDeclarations,
+ newParameterVariableGets,
+ constructor.function);
+
+ // Create new constructor looking like the old one
+ // (with the old body, parameters and initializers)
+ FunctionNode functionNode = _createShallowFunctionCopy(function);
+ final newConstructor = new Constructor(functionNode,
+ name: _createOriginalName(constructor),
+ isConst: constructor.isConst,
+ isExternal: constructor.isExternal,
+ initializers: constructor.initializers);
+
+ // Add constructor to the code
+ _addMember(constructor, newConstructor);
+
+ // Map moved body
+ _movedConstructors[constructor] = newConstructor;
+
+ // Transform original constructor
+ constructor.function.body = null;
+ constructor.function.positionalParameters = newParameterDeclarations;
+ constructor.function.namedParameters = newNamedParameterDeclarations;
+ constructor.initializers = [
+ new RedirectingInitializer(
+ newConstructor, new Arguments(newParameterVariableGets))
+ ..parent = constructor
+ ];
+
+ if (_debug) {
+ // Debug flag set: Print something to the terminal before returning to
+ // easily detect if rewrites are missing.
+ Expression debugPrint = _getPrintExpression(
+ "DEBUG! Constructor shouldn't have been called...", constructor);
+ var variable = new VariableDeclaration.forValue(debugPrint);
+ final debugInitializer = new LocalInitializer(variable)
+ ..parent = constructor;
+ final redirector = constructor.initializers[0];
+ constructor.initializers = [debugInitializer, redirector];
+ }
+
+ // Mark original procedure as seen (i.e. don't transform it further)
+ _visited.add(constructor);
+ }
+ }
+
+ /// Creates a new [FunctionNode] based on the given one.
+ ///
+ /// Parameters are taken directly (i.e. after returning the parameters will
+ /// have a new parent (the returned value), but still be referenced in the
+ /// original [FunctionNode].
+ /// The same goes for the body of the function.
+ /// The caller should take steps to remedy this after this call.
+ ///
+ /// The parameters are no longer optional and named parameters have been
+ /// sorted and turned into regular parameters in the returned [FunctionNode].
+ FunctionNode _createShallowFunctionCopy(FunctionNode function) {
+ final newParameters =
+ new List<VariableDeclaration>.from(function.positionalParameters);
+ final named = new List<VariableDeclaration>.from(function.namedParameters);
+ named.sort((a, b) => a.name.compareTo(b.name));
+ newParameters.addAll(named);
+ final functionNode = new FunctionNode(function.body,
+ positionalParameters: newParameters,
+ namedParameters: [],
+ requiredParameterCount: newParameters.length,
+ returnType: function.returnType,
+ asyncMarker: function.asyncMarker);
+ return functionNode;
+ }
+
+ /// Creates new variables, moving old initializers into them
+ ///
+ /// Specifically: Given lists for output, create new variables based on
+ /// original parameters. Any new variable will receive the original variables
+ /// initializer, and the original variable will have its initializer set to
+ /// null.
+ /// Named parameters have been sorted in [newParameterVariableGets].
+ void _moveVariableInitialization(
+ List<VariableDeclaration> originalParameters,
+ List<VariableDeclaration> originalNamedParameters,
+ List<VariableDeclaration> newParameterDeclarations,
+ List<VariableDeclaration> newNamedParameterDeclarations,
+ List<Expression> newParameterVariableGets,
+ TreeNode newStuffParent) {
+ for (final orgVar in originalParameters) {
+ final variableDeclaration = new VariableDeclaration(orgVar.name,
+ initializer: orgVar.initializer,
+ type: orgVar.type,
+ isFinal: orgVar.isFinal,
+ isConst: orgVar.isConst)..parent = newStuffParent;
+ variableDeclaration.initializer?.parent = variableDeclaration;
+ newParameterDeclarations.add(variableDeclaration);
+ orgVar.initializer = null;
+ newParameterVariableGets.add(new VariableGet(variableDeclaration));
+ }
+
+ // Named expressions in newParameterVariableGets should be sorted
+ final tmp = new List<_Pair<String, Expression>>();
+ for (final orgVar in originalNamedParameters) {
+ final variableDeclaration = new VariableDeclaration(orgVar.name,
+ initializer: orgVar.initializer,
+ type: orgVar.type,
+ isFinal: orgVar.isFinal,
+ isConst: orgVar.isConst)..parent = newStuffParent;
+ variableDeclaration.initializer?.parent = variableDeclaration;
+ newNamedParameterDeclarations.add(variableDeclaration);
+ orgVar.initializer = null;
+ tmp.add(new _Pair(orgVar.name, new VariableGet(variableDeclaration)));
+ }
+ tmp.sort((a, b) => a.key.compareTo(b.key));
+ for (final item in tmp) {
+ newParameterVariableGets.add(item.value);
+ }
+ }
+
+ /// Creates a stub redirecting to noSuchMethod.
+ ///
+ /// Needed because if B extends A, both have a foo method, but taking
+ /// different optional parameters, a call on an instance of B with parameters
+ /// for A should actually result in a noSuchMethod call, but if only A has
+ /// the rewritten method name, that method will be called...
+ /// TODO: We only have to create these stubs for arguments that a procedures
+ /// super allows, otherwise it will become a noSuchMethod automatically!
+ Procedure _createNoSuchMethodStub(
+ String argumentsSignature,
+ int positionalCount,
+ List<String> givenNamedParameters,
+ Procedure existing) {
+ // Build parameter lists
+ final newParameterDeclarations = <VariableDeclaration>[];
+ final newParameterVariableGets = <Expression>[];
+ for (int i = 0; i < positionalCount + givenNamedParameters.length; i++) {
+ final variableDeclaration = new VariableDeclaration("v%$i");
+ newParameterDeclarations.add(variableDeclaration);
+ newParameterVariableGets.add(new VariableGet(variableDeclaration));
+ }
+
+ var procedureName = _createName(existing.name, argumentsSignature);
+
+ // Find noSuchMethod to call
+ Member noSuchMethod = hierarchy.getDispatchTarget(
+ existing.enclosingClass, new Name("noSuchMethod"));
+ Arguments argumentsToNoSuchMethod;
+
+ if (noSuchMethod.function.positionalParameters.length == 1 &&
+ noSuchMethod.function.namedParameters.isEmpty) {
+ // We have a correct noSuchMethod method.
+ ConstructorInvocation invocation = _createInvocation(
+ procedureName.name, new Arguments(newParameterVariableGets));
+ argumentsToNoSuchMethod = new Arguments([invocation]);
+ } else {
+ // Get noSuchMethod on Object then...
+ noSuchMethod = hierarchy.getDispatchTarget(
+ hierarchy.rootClass, new Name("noSuchMethod"));
+ ConstructorInvocation invocation = _createInvocation(
+ procedureName.name, new Arguments(newParameterVariableGets));
+ ConstructorInvocation invocationPrime =
+ _createInvocation("noSuchMethod", new Arguments([invocation]));
+ argumentsToNoSuchMethod = new Arguments([invocationPrime]);
+ }
+
+ // Create return statement to call noSuchMethod
+ ReturnStatement statement;
+ final expression = new DirectMethodInvocation(
+ new ThisExpression(), noSuchMethod, argumentsToNoSuchMethod);
+ statement = new ReturnStatement(expression);
+
+ // Build procedure
+ final functionNode = new FunctionNode(statement,
+ positionalParameters: newParameterDeclarations,
+ namedParameters: [],
+ requiredParameterCount: newParameterDeclarations.length,
+ returnType: existing.function.returnType,
+ asyncMarker: existing.function.asyncMarker);
+ final procedure = new Procedure(
+ procedureName, ProcedureKind.Method, functionNode,
+ isStatic: existing.isStatic, fileUri: existing.fileUri);
+
+ // Add procedure to the code
+ _addMember(existing, procedure);
+
+ // Mark the new procedure as visited already (i.e. don't rewrite it again!)
+ _visited.add(procedure);
+
+ return procedure;
+ }
+
+ /// Creates an "new _InvocationMirror(...)" invocation.
+ ConstructorInvocation _createInvocation(
+ String methodName, Arguments callArguments) {
+ if (_invocationMirrorConstructor == null) {
+ Class clazz = coreTypes.getCoreClass('dart:core', '_InvocationMirror');
+ _invocationMirrorConstructor = clazz.constructors[0];
+ }
+
+ // The _InvocationMirror constructor takes the following arguments:
+ // * Method name (a string).
+ // * An arguments descriptor - a list consisting of:
+ // - number of arguments (including receiver).
+ // - number of positional arguments (including receiver).
+ // - pairs (2 entries in the list) of
+ // * named arguments name.
+ // * index of named argument in arguments list.
+ // * A list of arguments, where the first ones are the positional arguments.
+ // * Whether it's a super invocation or not.
+
+ int numPositionalArguments = callArguments.positional.length + 1;
+ int numArguments = numPositionalArguments + callArguments.named.length;
+ List<Expression> argumentsDescriptor = [
+ new IntLiteral(numArguments),
+ new IntLiteral(numPositionalArguments)
+ ];
+ List<Expression> arguments = [];
+ arguments.add(new ThisExpression());
+ for (Expression pos in callArguments.positional) {
+ arguments.add(pos);
+ }
+ for (NamedExpression named in callArguments.named) {
+ argumentsDescriptor.add(new StringLiteral(named.name));
+ argumentsDescriptor.add(new IntLiteral(arguments.length));
+ arguments.add(named.value);
+ }
+
+ return new ConstructorInvocation(
+ _invocationMirrorConstructor,
+ new Arguments([
+ new StringLiteral(methodName),
+ _fixedLengthList(argumentsDescriptor),
+ _fixedLengthList(arguments),
+ new BoolLiteral(false)
+ ]));
+ }
+
+ /// Create a fixed length list containing given expressions.
+ Expression _fixedLengthList(List<Expression> list) {
+ if (_listFrom == null) {
+ Class clazz = coreTypes.getCoreClass('dart:core', 'List');
+ _listFrom = clazz.procedures.firstWhere((c) => c.name.name == "from");
+ }
+ return new StaticInvocation(
+ _listFrom,
+ new Arguments([new ListLiteral(list)],
+ named: [new NamedExpression("growable", new BoolLiteral(false))],
+ types: [const DynamicType()]));
+ }
+
+ /// Creates a new procedure taking given arguments, caching it.
+ ///
+ /// Copies any non-given default values for parameters into the new procedure
+ /// to be able to call the [realTarget] without using optionals and named
+ /// parameters.
+ Procedure _createAndCacheInvocationProcedure(
+ String argumentsSignature,
+ int positionalCount,
+ List<String> givenNamedParameters,
+ Procedure target,
+ Procedure realTarget,
+ Map<String, Procedure> createdProcedures,
+ bool doSpecialCaseForAllParameters) {
+ // Special case: Calling with all parameters
+ if (doSpecialCaseForAllParameters &&
+ positionalCount == target.function.positionalParameters.length &&
+ givenNamedParameters.length == target.function.namedParameters.length) {
+ // We don't cache this procedure as this could make it look like
+ // something with name argumentsSignature actually exists
+ // while it doesn't (which is bad as we could then decide that we don't
+ // need to create a stub even though we do!)
+ return realTarget;
+ }
+
+ // Create and cache (save) constructor
+
+ // Build parameter lists
+ final newParameterDeclarations = <VariableDeclaration>[];
+ final newParameterVariableGets = <Expression>[];
+ _extractAndCreateParameters(positionalCount, newParameterDeclarations,
+ newParameterVariableGets, target, givenNamedParameters);
+
+ // Create return statement to call real target
+ ReturnStatement statement;
+ if (target.isAbstract && target.function?.body == null) {
+ // statement should just be null then
+ } else if (target.isStatic) {
+ final expression = new StaticInvocation(
+ realTarget, new Arguments(newParameterVariableGets));
+ statement = new ReturnStatement(expression);
+ } else {
+ final expression = new DirectMethodInvocation(new ThisExpression(),
+ realTarget, new Arguments(newParameterVariableGets));
+ statement = new ReturnStatement(expression);
+ }
+
+ // Build procedure
+ final functionNode = new FunctionNode(statement,
+ positionalParameters: newParameterDeclarations,
+ namedParameters: [],
+ requiredParameterCount: newParameterDeclarations.length,
+ returnType: target.function.returnType,
+ asyncMarker: target.function.asyncMarker);
+ final procedure = new Procedure(
+ _createName(target.name, argumentsSignature),
+ ProcedureKind.Method,
+ functionNode,
+ isAbstract: target.isAbstract,
+ isStatic: target.isStatic,
+ isConst: target.isConst,
+ fileUri: target.fileUri);
+
+ // Add procedure to the code
+ _addMember(target, procedure);
+
+ // Cache it for future reference
+ createdProcedures[argumentsSignature] = procedure;
+
+ // Mark the new procedure as visited already (i.e. don't rewrite it again!)
+ _visited.add(procedure);
+
+ return procedure;
+ }
+
+ /// Creates a new constructor taking given arguments, caching it.
+ ///
+ /// Copies any non-given default values for parameters into the new
+ /// constructor to be able to call the [realTarget] without using optionals
+ /// and named parameters.
+ Constructor _createAndCacheInvocationConstructor(
+ String argumentsSignature,
+ int positionalCount,
+ List<String> givenNamedParameters,
+ Constructor target,
+ Constructor realTarget,
+ Map<String, Constructor> createdConstructor,
+ bool doSpecialCaseForAllParameters) {
+ // Special case: Calling with all parameters
+ if (doSpecialCaseForAllParameters &&
+ positionalCount == target.function.positionalParameters.length &&
+ givenNamedParameters.length == target.function.namedParameters.length) {
+ createdConstructor[argumentsSignature] = realTarget;
+ return realTarget;
+ }
+
+ // Create and cache (save) constructor
+
+ // Build parameter lists
+ final newParameterDeclarations = <VariableDeclaration>[];
+ final newParameterVariableGets = <Expression>[];
+ _extractAndCreateParameters(positionalCount, newParameterDeclarations,
+ newParameterVariableGets, target, givenNamedParameters);
+
+ // Build constructor
+ final functionNode = new FunctionNode(null,
+ positionalParameters: newParameterDeclarations,
+ namedParameters: [],
+ requiredParameterCount: newParameterDeclarations.length,
+ returnType: target.function.returnType,
+ asyncMarker: target.function.asyncMarker);
+ final constructor = new Constructor(functionNode,
+ name: _createName(target.name, argumentsSignature),
+ isConst: target.isConst,
+ isExternal: target.isExternal,
+ initializers: [
+ new RedirectingInitializer(
+ realTarget, new Arguments(newParameterVariableGets))
+ ]);
+
+ // Add procedure to the code
+ _addMember(target, constructor);
+
+ // Cache it for future reference
+ createdConstructor[argumentsSignature] = constructor;
+
+ // Mark the new procedure as visited already (i.e. don't rewrite it again!)
+ _visited.add(constructor);
+
+ return constructor;
+ }
+
+ /// Extracts and creates parameters into the first two given lists.
+ ///
+ /// What is done:
+ /// Step 1: Re-create the parameters given (i.e. the non-optional positional
+ /// ones) - i.e. create a new variable with the same name etc, put it in
+ /// [newParameterDeclarations]; create VariableGet for that and put it in
+ /// [newParameterVariableGets]
+ /// Step 2: Re-create the positional parameters NOT given, i.e. insert
+ /// defaults and add to [newParameterVariableGets] only.
+ /// Step 3: Re-create the named arguments (in sorted order). For actually
+ /// given named parameters, do as in step 1, for not-given named parameters
+ /// do as in step 2.
+ ///
+ /// NOTE: [newParameterDeclarations] and [newParameterVariableGets] are OUTPUT
+ /// lists.
+ void _extractAndCreateParameters(
+ int positionalCount,
+ List<VariableDeclaration> newParameterDeclarations,
+ List<Expression> newParameterVariableGets,
+ Member target,
+ List<String> givenNamedParameters) {
+ // First re-create the parameters given (i.e. the non-optional positional ones)
+ final targetParameters = target.function.positionalParameters;
+ positionalCount = math.min(positionalCount, targetParameters.length);
+ for (int i = 0; i < positionalCount; i++) {
+ final orgVar = targetParameters[i];
+ final variableDeclaration = new VariableDeclaration(orgVar.name,
+ type: orgVar.type, isFinal: orgVar.isFinal, isConst: orgVar.isConst);
+ newParameterDeclarations.add(variableDeclaration);
+ newParameterVariableGets.add(new VariableGet(variableDeclaration));
+ }
+
+ // Default parameters for the rest of them
+ _fillInPositionalParameters(
+ positionalCount, target, newParameterVariableGets);
+
+ // Then all named parameters (given here or not)
+ final orgNamed =
+ new List<VariableDeclaration>.from(target.function.namedParameters);
+ orgNamed.sort((a, b) => a.name.compareTo(b.name));
+ final givenArgumentsIterator = givenNamedParameters.iterator;
+ givenArgumentsIterator.moveNext();
+ for (VariableDeclaration named in orgNamed) {
+ if (givenArgumentsIterator.current == named.name) {
+ // We have that one: Use it and move the iterator
+ final variableDeclaration = new VariableDeclaration(named.name);
+ newParameterDeclarations.add(variableDeclaration);
+ newParameterVariableGets.add(new VariableGet(variableDeclaration));
+ givenArgumentsIterator.moveNext();
+ } else {
+ // We don't have that one: Fill it in
+ _fillInSingleParameter(named, newParameterVariableGets, target);
+ }
+ }
+ }
+
+ /// Adds the new member the same place as the existing member
+ void _addMember(Member existingMember, Member newMember) {
+ if (existingMember.enclosingClass != null) {
+ existingMember.enclosingClass.addMember(newMember);
+ } else {
+ existingMember.enclosingLibrary.addMember(newMember);
+ }
+ }
+
+ /// Create expressions based on the default values from the given [Member].
+ ///
+ /// More specifically: static gets and nulls will be "copied" whereas other
+ /// things (e.g. literals or things like "a+b") will be moved from the
+ /// original member as argument initializers to const fields and both the
+ /// original member and the expression-copy will use static gets to these.
+ void _fillInPositionalParameters(
+ int startFrom, Member copyFrom, List<Expression> fillInto) {
+ final targetParameters = copyFrom.function.positionalParameters;
+ for (int i = startFrom; i < targetParameters.length; i++) {
+ final parameter = targetParameters[i];
+ _fillInSingleParameter(parameter, fillInto, copyFrom);
+ }
+ }
+
+ /// Create expression based on the default values from the given variable.
+ ///
+ /// More specifically: a static get or null will be "copied" whereas other
+ /// things (e.g. literals or things like "a+b") will be moved from the
+ /// original member as an argument initializer to a const field and both the
+ /// original member and the expression-copy will use a static get to it.
+ void _fillInSingleParameter(VariableDeclaration parameter,
+ List<Expression> fillInto, Member copyFrom) {
+ if (parameter.initializer is StaticGet) {
+ // Reference to const => recreate it
+ StaticGet staticGet = parameter.initializer;
+ fillInto.add(new StaticGet(staticGet.target));
+ } else if (parameter.initializer == null) {
+ // No default given => output null
+ fillInto.add(new NullLiteral());
+ } else if (parameter.initializer is IntLiteral) {
+ // Int literal => recreate (or else class ByteBuffer in typed_data will
+ // get 2 fields and the C++ code will complain!)
+ IntLiteral value = parameter.initializer;
+ fillInto.add(new IntLiteral(value.value));
+ } else {
+ // Advanced stuff => move to static const field and reference that
+ final initializer = parameter.initializer;
+ final f = new Field(
+ new Name('${copyFrom.name.name}%_${parameter.name}',
+ copyFrom.enclosingLibrary),
+ type: parameter.type,
+ initializer: initializer,
+ isFinal: false,
+ isConst: true,
+ isStatic: true,
+ fileUri: copyFrom.enclosingClass?.fileUri ??
+ copyFrom.enclosingLibrary.fileUri);
+ initializer.parent = f;
+
+ // Add field to the code
+ if (copyFrom.enclosingClass != null) {
+ copyFrom.enclosingClass.addMember(f);
+ } else {
+ copyFrom.enclosingLibrary.addMember(f);
+ }
+
+ // Use it at the call site
+ fillInto.add(new StaticGet(f));
+
+ // Now replace the initializer in the method to a StaticGet
+ parameter.initializer = new StaticGet(f)..parent = parameter;
+ }
+ }
+
+ /// Create an "original name" for a member.
+ ///
+ /// Specifically, for a member "x" just returns "x%original";
+ Name _createOriginalName(Member member) {
+ return new Name("${member.name.name}%original", member.enclosingLibrary);
+ }
+
+ /// Create a [Name] based on current name and argument signature.
+ Name _createName(Name name, String argumentsSignature) {
+ String nameString = '${name.name}%$argumentsSignature';
+ return new Name(nameString, name.library);
+ }
+
+ /// Is the procedure a method?
+ bool _isMethod(Procedure procedure) => procedure.kind == ProcedureKind.Method;
+
+ /// Is the procedure NOT marked as external?
+ bool _isNotExternal(Constructor constructor) => !constructor.isExternal;
+
+ /// Does the target function have any optional arguments? (positional/named)
+ bool _hasAnyOptionalParameters(FunctionNode targetFunction) =>
+ _hasOptionalParameters(targetFunction) ||
+ _hasNamedParameters(targetFunction);
+
+ /// Does the target function have optional positional arguments?
+ bool _hasOptionalParameters(FunctionNode targetFunction) =>
+ targetFunction.positionalParameters.length >
+ targetFunction.requiredParameterCount;
+
+ /// Does the target function have named parameters?
+ bool _hasNamedParameters(FunctionNode targetFunction) =>
+ targetFunction.namedParameters.isNotEmpty;
+
+ bool _callIsLegal(FunctionNode targetFunction, Arguments arguments) {
+ if ((targetFunction.requiredParameterCount > arguments.positional.length) ||
+ (targetFunction.positionalParameters.length <
+ arguments.positional.length)) {
+ // Given too few or too many positional arguments
+ return false;
+ }
+
+ // Do we give named that we don't take?
+ Set<String> givenNamed = arguments.named.map((v) => v.name).toSet();
+ Set<String> takenNamed =
+ targetFunction.namedParameters.map((v) => v.name).toSet();
+ givenNamed.removeAll(takenNamed);
+ return givenNamed.isEmpty;
+ }
+
+ // Below methods used to add debug prints etc
+
+ Library _getDartCoreLibrary(Program program) {
+ if (program == null) return null;
+ return program.libraries.firstWhere((lib) =>
+ lib.importUri.scheme == 'dart' && lib.importUri.path == 'core');
+ }
+
+ Procedure _getProcedureInLib(Library lib, String name) {
+ if (lib == null) return null;
+ return lib.procedures
+ .firstWhere((procedure) => procedure.name.name == name);
+ }
+
+ Procedure _getProcedureInClassInLib(
+ Library lib, String className, String procedureName) {
+ if (lib == null) return null;
+ Class clazz = lib.classes.firstWhere((clazz) => clazz.name == className);
+ return clazz.procedures
+ .firstWhere((procedure) => procedure.name.name == procedureName);
+ }
+
+ Expression _getPrintExpression(String msg, TreeNode treeNode) {
+ TreeNode program = treeNode;
+ while (program is! Program) program = program.parent;
+ var finalMsg = msg;
+ if (treeNode is Member) {
+ finalMsg += " [ ${treeNode.name.name} ]";
+ if (treeNode.enclosingClass != null) {
+ finalMsg += " [ class ${treeNode.enclosingClass.name} ]";
+ }
+ if (treeNode.enclosingLibrary != null) {
+ finalMsg += " [ lib ${treeNode.enclosingLibrary.name} ]";
+ }
+ }
+
+ var stacktrace = new StaticGet(_getProcedureInClassInLib(
+ _getDartCoreLibrary(program), 'StackTrace', 'current'));
+ var printStackTrace = new StaticInvocation(
+ _getProcedureInLib(_getDartCoreLibrary(program), 'print'),
+ new Arguments([
+ new StringConcatenation([
+ new StringLiteral(finalMsg),
+ new StringLiteral("\n"),
+ stacktrace,
+ new StringLiteral("\n")
+ ])
+ ]));
+
+ return printStackTrace;
+ }
+}
+
+class _Pair<K, V> {
+ final K key;
+ final V value;
+
+ _Pair(this.key, this.value);
+}
« 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