1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.errorprone.bugpatterns.android; 18 19 import static com.google.errorprone.BugPattern.LinkType.NONE; 20 import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; 21 import static com.google.errorprone.matchers.Matchers.allOf; 22 import static com.google.errorprone.matchers.Matchers.enclosingClass; 23 import static com.google.errorprone.matchers.Matchers.enclosingMethod; 24 import static com.google.errorprone.matchers.Matchers.instanceMethod; 25 import static com.google.errorprone.matchers.Matchers.isSubtypeOf; 26 import static com.google.errorprone.matchers.Matchers.methodInvocation; 27 import static com.google.errorprone.matchers.Matchers.methodIsNamed; 28 29 import com.google.auto.service.AutoService; 30 import com.google.errorprone.BugPattern; 31 import com.google.errorprone.VisitorState; 32 import com.google.errorprone.bugpatterns.BugChecker; 33 import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher; 34 import com.google.errorprone.matchers.Description; 35 import com.google.errorprone.matchers.Matcher; 36 import com.sun.source.tree.ExpressionTree; 37 import com.sun.source.tree.MethodInvocationTree; 38 import com.sun.source.tree.Tree; 39 40 /** 41 * Parcelable data can be transported in many ways (some of which can be very 42 * inefficient) so this checker guides developers towards using high-performance 43 * best-practices. 44 */ 45 @AutoService(BugChecker.class) 46 @BugPattern( 47 name = "AndroidFrameworkEfficientParcelable", 48 summary = "Verifies Parcelable performance best-practices", 49 linkType = NONE, 50 severity = WARNING) 51 public final class EfficientParcelableChecker extends BugChecker 52 implements MethodInvocationTreeMatcher { 53 private static final Matcher<Tree> INSIDE_WRITE_TO_PARCEL = allOf( 54 enclosingClass(isSubtypeOf("android.os.Parcelable")), 55 enclosingMethod(methodIsNamed("writeToParcel"))); 56 57 private static final Matcher<ExpressionTree> WRITE_STRING = methodInvocation( 58 instanceMethod().onExactClass("android.os.Parcel").named("writeString")); 59 private static final Matcher<ExpressionTree> WRITE_STRING_ARRAY = methodInvocation( 60 instanceMethod().onExactClass("android.os.Parcel").named("writeStringArray")); 61 62 private static final Matcher<ExpressionTree> WRITE_VALUE = methodInvocation( 63 instanceMethod().onExactClass("android.os.Parcel").named("writeValue")); 64 private static final Matcher<ExpressionTree> WRITE_PARCELABLE = methodInvocation( 65 instanceMethod().onExactClass("android.os.Parcel").named("writeParcelable")); 66 67 private static final Matcher<ExpressionTree> WRITE_LIST = methodInvocation( 68 instanceMethod().onExactClass("android.os.Parcel").named("writeList")); 69 private static final Matcher<ExpressionTree> WRITE_PARCELABLE_LIST = methodInvocation( 70 instanceMethod().onExactClass("android.os.Parcel").named("writeParcelableList")); 71 private static final Matcher<ExpressionTree> WRITE_PARCELABLE_ARRAY = methodInvocation( 72 instanceMethod().onExactClass("android.os.Parcel").named("writeParcelableArray")); 73 74 @Override matchMethodInvocation(MethodInvocationTree tree, VisitorState state)75 public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { 76 if (INSIDE_WRITE_TO_PARCEL.matches(tree, state)) { 77 if (WRITE_STRING.matches(tree, state)) { 78 return buildDescription(tree) 79 .setMessage("Recommended to use 'writeString8()' to improve " 80 + "efficiency; sending as UTF-8 can double throughput") 81 .build(); 82 } 83 if (WRITE_STRING_ARRAY.matches(tree, state)) { 84 return buildDescription(tree) 85 .setMessage("Recommended to use 'writeString8Array()' to improve " 86 + "efficiency; sending as UTF-8 can double throughput") 87 .build(); 88 } 89 90 if (WRITE_VALUE.matches(tree, state)) { 91 return buildDescription(tree) 92 .setMessage("Recommended to use strongly-typed methods to improve " 93 + "efficiency; saves 4 bytes for type and overhead of " 94 + "Parcelable class name") 95 .build(); 96 } 97 if (WRITE_PARCELABLE.matches(tree, state)) { 98 return buildDescription(tree) 99 .setMessage("Recommended to use 'item.writeToParcel()' to improve " 100 + "efficiency; saves overhead of Parcelable class name") 101 .build(); 102 } 103 104 if (WRITE_LIST.matches(tree, state)) { 105 return buildDescription(tree) 106 .setMessage("Recommended to use 'writeTypedList()' to improve " 107 + "efficiency; saves overhead of repeated Parcelable class name") 108 .build(); 109 } 110 if (WRITE_PARCELABLE_LIST.matches(tree, state)) { 111 return buildDescription(tree) 112 .setMessage("Recommended to use 'writeTypedList()' to improve " 113 + "efficiency; saves overhead of repeated Parcelable class name") 114 .build(); 115 } 116 if (WRITE_PARCELABLE_ARRAY.matches(tree, state)) { 117 return buildDescription(tree) 118 .setMessage("Recommended to use 'writeTypedArray()' to improve " 119 + "efficiency; saves overhead of repeated Parcelable class name") 120 .build(); 121 } 122 } 123 return Description.NO_MATCH; 124 } 125 } 126