001/* 002 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.debug; 024 025import java.util.*; 026import java.util.regex.*; 027 028import jdk.internal.jvmci.meta.*; 029 030/** 031 * This class implements a method filter that can filter based on class name, method name and 032 * parameters. The syntax for the source pattern that is passed to the constructor is as follows: 033 * 034 * <pre> 035 * SourcePatterns = SourcePattern ["," SourcePatterns] . 036 * SourcePattern = [ Class "." ] method [ "(" [ Parameter { ";" Parameter } ] ")" ] . 037 * Parameter = Class | "int" | "long" | "float" | "double" | "short" | "char" | "boolean" . 038 * Class = { package "." } class . 039 * </pre> 040 * 041 * 042 * Glob pattern matching (*, ?) is allowed in all parts of the source pattern. Examples for valid 043 * filters are: 044 * 045 * <ul> 046 * <li> 047 * 048 * <pre> 049 * visit(Argument;BlockScope) 050 * </pre> 051 * 052 * Matches all methods named "visit", with the first parameter of type "Argument", and the second 053 * parameter of type "BlockScope". The packages of the parameter types are irrelevant.</li> 054 * <li> 055 * 056 * <pre> 057 * arraycopy(Object;;;;) 058 * </pre> 059 * 060 * Matches all methods named "arraycopy", with the first parameter of type "Object", and four more 061 * parameters of any type. The packages of the parameter types are irrelevant.</li> 062 * <li> 063 * 064 * <pre> 065 * com.oracle.graal.compiler.graph.PostOrderNodeIterator.* 066 * </pre> 067 * 068 * Matches all methods in the class "com.oracle.graal.compiler.graph.PostOrderNodeIterator".</li> 069 * <li> 070 * 071 * <pre> 072 * * 073 * </pre> 074 * 075 * Matches all methods in all classes</li> 076 * <li> 077 * 078 * <pre> 079 * com.oracle.graal.compiler.graph.*.visit 080 * </pre> 081 * 082 * Matches all methods named "visit" in classes in the package "com.oracle.graal.compiler.graph". 083 * <li> 084 * 085 * <pre> 086 * arraycopy,toString 087 * </pre> 088 * 089 * Matches all methods named "arraycopy" or "toString", meaning that ',' acts as an <i>or</i> 090 * operator.</li> 091 * </ul> 092 */ 093public class MethodFilter { 094 095 private final Pattern clazz; 096 private final Pattern methodName; 097 private final Pattern[] signature; 098 099 /** 100 * Parses a string containing list of comma separated filter patterns into an array of 101 * {@link MethodFilter}s. 102 */ 103 public static MethodFilter[] parse(String commaSeparatedPatterns) { 104 String[] filters = commaSeparatedPatterns.split(","); 105 MethodFilter[] methodFilters = new MethodFilter[filters.length]; 106 for (int i = 0; i < filters.length; i++) { 107 methodFilters[i] = new MethodFilter(filters[i]); 108 } 109 return methodFilters; 110 } 111 112 /** 113 * Determines if a given method is matched by a given array of filters. 114 */ 115 public static boolean matches(MethodFilter[] filters, JavaMethod method) { 116 for (MethodFilter filter : filters) { 117 if (filter.matches(method)) { 118 return true; 119 } 120 } 121 return false; 122 } 123 124 /** 125 * Determines if a given class name is matched by a given array of filters. 126 */ 127 public static boolean matchesClassName(MethodFilter[] filters, String className) { 128 for (MethodFilter filter : filters) { 129 if (filter.matchesClassName(className)) { 130 return true; 131 } 132 } 133 return false; 134 } 135 136 public MethodFilter(String sourcePattern) { 137 String pattern = sourcePattern.trim(); 138 139 // extract parameter part 140 int pos = pattern.indexOf('('); 141 if (pos != -1) { 142 if (pattern.charAt(pattern.length() - 1) != ')') { 143 throw new IllegalArgumentException("missing ')' at end of method filter pattern: " + pattern); 144 } 145 String[] signatureClasses = pattern.substring(pos + 1, pattern.length() - 1).split(";", -1); 146 signature = new Pattern[signatureClasses.length]; 147 for (int i = 0; i < signatureClasses.length; i++) { 148 signature[i] = createClassGlobPattern(signatureClasses[i].trim()); 149 } 150 pattern = pattern.substring(0, pos); 151 } else { 152 signature = null; 153 } 154 155 // If there is at least one "." then everything before the last "." is the class name. 156 // Otherwise, the pattern contains only the method name. 157 pos = pattern.lastIndexOf('.'); 158 if (pos != -1) { 159 clazz = createClassGlobPattern(pattern.substring(0, pos)); 160 methodName = Pattern.compile(createGlobString(pattern.substring(pos + 1))); 161 } else { 162 clazz = null; 163 methodName = Pattern.compile(createGlobString(pattern)); 164 } 165 } 166 167 public static String createGlobString(String pattern) { 168 return Pattern.quote(pattern).replace("?", "\\E.\\Q").replace("*", "\\E.*\\Q"); 169 } 170 171 private static Pattern createClassGlobPattern(String pattern) { 172 if (pattern.length() == 0) { 173 return null; 174 } else if (pattern.contains(".")) { 175 return Pattern.compile(createGlobString(pattern)); 176 } else { 177 return Pattern.compile("([^\\.\\$]*[\\.\\$])*" + createGlobString(pattern)); 178 } 179 } 180 181 /** 182 * Determines if the class part of this filter matches a given class name. 183 */ 184 public boolean matchesClassName(String className) { 185 return clazz == null || clazz.matcher(className).matches(); 186 } 187 188 public boolean matches(JavaMethod o) { 189 // check method name first, since MetaUtil.toJavaName is expensive 190 if (methodName != null && !methodName.matcher(o.getName()).matches()) { 191 return false; 192 } 193 if (clazz != null && !clazz.matcher(o.getDeclaringClass().toJavaName()).matches()) { 194 return false; 195 } 196 if (signature != null) { 197 Signature sig = o.getSignature(); 198 if (sig.getParameterCount(false) != signature.length) { 199 return false; 200 } 201 for (int i = 0; i < signature.length; i++) { 202 JavaType type = sig.getParameterType(i, null); 203 String javaName = type.toJavaName(); 204 if (signature[i] != null && !signature[i].matcher(javaName).matches()) { 205 return false; 206 } 207 } 208 } 209 return true; 210 } 211 212 @Override 213 public String toString() { 214 StringBuilder buf = new StringBuilder("MethodFilter["); 215 String sep = ""; 216 if (clazz != null) { 217 buf.append(sep).append("clazz=").append(clazz); 218 sep = ", "; 219 } 220 if (methodName != null) { 221 buf.append(sep).append("methodName=").append(methodName); 222 sep = ", "; 223 } 224 if (signature != null) { 225 buf.append(sep).append("signature=").append(Arrays.toString(signature)); 226 sep = ", "; 227 } 228 return buf.append("]").toString(); 229 } 230}