|
| 1 | +// Copyright (c) 2020, 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 | +import 'package:kernel/ast.dart'; |
| 6 | +import 'package:kernel/visitor.dart'; |
| 7 | +import '../frontend_server.dart'; |
| 8 | + |
| 9 | +// Transformer/visitor for toString |
| 10 | +// If we add any more of these, they really should go into a separate library. |
| 11 | + |
| 12 | +/// A [RecursiveVisitor] that replaces [Object.toString] overrides with |
| 13 | +/// `super.toString()`. |
| 14 | +class ToStringVisitor extends RecursiveVisitor<void> { |
| 15 | + /// The [packageUris] must not be null. |
| 16 | + ToStringVisitor(this._packageUris) : assert(_packageUris != null); |
| 17 | + |
| 18 | + /// A set of package URIs to apply this transformer to, e.g. 'dart:ui' and |
| 19 | + /// 'package:flutter/foundation.dart'. |
| 20 | + final Set<String> _packageUris; |
| 21 | + |
| 22 | + /// Turn 'dart:ui' into 'dart:ui', or |
| 23 | + /// 'package:flutter/src/semantics_event.dart' into 'package:flutter'. |
| 24 | + String _importUriToPackage(Uri importUri) => |
| 25 | + '${importUri.scheme}:${importUri.pathSegments.first}'; |
| 26 | + |
| 27 | + bool _isInTargetPackage(Procedure node) { |
| 28 | + return _packageUris |
| 29 | + .contains(_importUriToPackage(node.enclosingLibrary.importUri)); |
| 30 | + } |
| 31 | + |
| 32 | + bool _hasKeepAnnotation(Procedure node) { |
| 33 | + for (ConstantExpression expression |
| 34 | + in node.annotations.whereType<ConstantExpression>()) { |
| 35 | + if (expression.constant is! InstanceConstant) { |
| 36 | + continue; |
| 37 | + } |
| 38 | + final InstanceConstant constant = expression.constant as InstanceConstant; |
| 39 | + if (constant.classNode.name == '_KeepToString' && |
| 40 | + constant.classNode.enclosingLibrary.importUri.toString() == |
| 41 | + 'dart:ui') { |
| 42 | + return true; |
| 43 | + } |
| 44 | + } |
| 45 | + return false; |
| 46 | + } |
| 47 | + |
| 48 | + @override |
| 49 | + void visitProcedure(Procedure node) { |
| 50 | + if (node.name.text == 'toString' && |
| 51 | + node.enclosingClass != null && |
| 52 | + node.enclosingLibrary != null && |
| 53 | + !node.isStatic && |
| 54 | + !node.isAbstract && |
| 55 | + !node.enclosingClass.isEnum && |
| 56 | + _isInTargetPackage(node) && |
| 57 | + !_hasKeepAnnotation(node)) { |
| 58 | + node.function.body.replaceWith( |
| 59 | + ReturnStatement( |
| 60 | + SuperMethodInvocation( |
| 61 | + node.name, |
| 62 | + Arguments(<Expression>[]), |
| 63 | + ), |
| 64 | + ), |
| 65 | + ); |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + @override |
| 70 | + void defaultMember(Member node) {} |
| 71 | +} |
| 72 | + |
| 73 | +/// Replaces [Object.toString] overrides with calls to super for the specified |
| 74 | +/// [packageUris]. |
| 75 | +class ToStringTransformer extends ProgramTransformer { |
| 76 | + /// The [packageUris] parameter must not be null, but may be empty. |
| 77 | + ToStringTransformer(this._child, this._packageUris) |
| 78 | + : assert(_packageUris != null); |
| 79 | + |
| 80 | + final ProgramTransformer _child; |
| 81 | + |
| 82 | + /// A set of package URIs to apply this transformer to, e.g. 'dart:ui' and |
| 83 | + /// 'package:flutter/foundation.dart'. |
| 84 | + final Set<String> _packageUris; |
| 85 | + |
| 86 | + @override |
| 87 | + void transform(Component component) { |
| 88 | + assert(_child is! ToStringTransformer); |
| 89 | + if (_packageUris.isNotEmpty) { |
| 90 | + component.visitChildren(ToStringVisitor(_packageUris)); |
| 91 | + } |
| 92 | + _child?.transform(component); |
| 93 | + } |
| 94 | +} |
0 commit comments