1 /* <lambda>null2 * Copyright (C) 2019 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.android.protolog.tool 18 19 import com.android.internal.protolog.common.LogLevel 20 import com.github.javaparser.ast.CompilationUnit 21 import com.github.javaparser.ast.expr.Expression 22 import com.github.javaparser.ast.expr.FieldAccessExpr 23 import com.github.javaparser.ast.expr.MethodCallExpr 24 import com.github.javaparser.ast.expr.NameExpr 25 26 /** 27 * Helper class for visiting all ProtoLog calls. 28 * For every valid call in the given {@code CompilationUnit} a {@code ProtoLogCallVisitor} callback 29 * is executed. 30 */ 31 class ProtoLogCallProcessorImpl( 32 private val protoLogClassName: String, 33 private val protoLogGroupClassName: String, 34 private val groupMap: Map<String, LogGroup> 35 ) : ProtoLogCallProcessor { 36 private val protoLogSimpleClassName = protoLogClassName.substringAfterLast('.') 37 private val protoLogGroupSimpleClassName = protoLogGroupClassName.substringAfterLast('.') 38 39 private fun getLogGroupName( 40 expr: Expression, 41 isClassImported: Boolean, 42 staticImports: Set<String>, 43 fileName: String 44 ): String { 45 val context = ParsingContext(fileName, expr) 46 return when (expr) { 47 is NameExpr -> when { 48 expr.nameAsString in staticImports -> expr.nameAsString 49 else -> 50 throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup: $expr", 51 context) 52 } 53 is FieldAccessExpr -> when { 54 expr.scope.toString() == protoLogGroupClassName || isClassImported && 55 expr.scope.toString() == protoLogGroupSimpleClassName -> expr.nameAsString 56 else -> 57 throw InvalidProtoLogCallException("Unknown/not imported ProtoLogGroup: $expr", 58 context) 59 } 60 else -> throw InvalidProtoLogCallException("Invalid group argument " + 61 "- must be ProtoLogGroup enum member reference: $expr", context) 62 } 63 } 64 65 private fun isProtoCall( 66 call: MethodCallExpr, 67 isLogClassImported: Boolean, 68 staticLogImports: Collection<String> 69 ): Boolean { 70 return call.scope.isPresent && call.scope.get().toString() == protoLogClassName || 71 isLogClassImported && call.scope.isPresent && 72 call.scope.get().toString() == protoLogSimpleClassName || 73 !call.scope.isPresent && staticLogImports.contains(call.name.toString()) 74 } 75 76 fun process(code: CompilationUnit, logCallVisitor: ProtoLogCallVisitor?, fileName: String): 77 CompilationUnit { 78 return process(code, logCallVisitor, null, fileName) 79 } 80 81 override fun process( 82 code: CompilationUnit, 83 logCallVisitor: ProtoLogCallVisitor?, 84 otherCallVisitor: MethodCallVisitor?, 85 fileName: String 86 ): CompilationUnit { 87 CodeUtils.checkWildcardStaticImported(code, protoLogClassName, fileName) 88 CodeUtils.checkWildcardStaticImported(code, protoLogGroupClassName, fileName) 89 90 val isLogClassImported = CodeUtils.isClassImportedOrSamePackage(code, protoLogClassName) 91 val staticLogImports = CodeUtils.staticallyImportedMethods(code, protoLogClassName) 92 val isGroupClassImported = CodeUtils.isClassImportedOrSamePackage(code, 93 protoLogGroupClassName) 94 val staticGroupImports = CodeUtils.staticallyImportedMethods(code, protoLogGroupClassName) 95 96 code.findAll(MethodCallExpr::class.java) 97 .filter { call -> 98 isProtoCall(call, isLogClassImported, staticLogImports) 99 }.forEach { call -> 100 val context = ParsingContext(fileName, call) 101 102 val logMethods = LogLevel.entries.map { it.shortCode } 103 if (logMethods.contains(call.name.id)) { 104 // Process a log call 105 if (call.arguments.size < 2) { 106 throw InvalidProtoLogCallException("Method signature does not match " + 107 "any ProtoLog method: $call", context) 108 } 109 110 val messageString = CodeUtils.concatMultilineString(call.getArgument(1), 111 context) 112 val groupNameArg = call.getArgument(0) 113 val groupName = 114 getLogGroupName(groupNameArg, isGroupClassImported, 115 staticGroupImports, fileName) 116 if (groupName !in groupMap) { 117 throw InvalidProtoLogCallException("Unknown group argument " + 118 "- not a ProtoLogGroup enum member: $call", context) 119 } 120 121 logCallVisitor?.processCall(call, messageString, getLevelForMethodName( 122 call.name.toString(), call, context), groupMap.getValue(groupName)) 123 } else { 124 // Process non-log message calls 125 otherCallVisitor?.processCall(call) 126 } 127 } 128 return code 129 } 130 131 private fun getLevelForMethodName( 132 name: String, 133 node: MethodCallExpr, 134 context: ParsingContext 135 ): LogLevel = when (name) { 136 "d" -> LogLevel.DEBUG 137 "v" -> LogLevel.VERBOSE 138 "i" -> LogLevel.INFO 139 "w" -> LogLevel.WARN 140 "e" -> LogLevel.ERROR 141 "wtf" -> LogLevel.WTF 142 else -> 143 throw InvalidProtoLogCallException("Unknown log level $name in $node", context) 144 } 145 } 146