ClassFileVisitor.java revision 3169:ef2011e4555a
1/* 2 * Copyright (c) 2013, 2015 Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24import java.io.*; 25import com.sun.tools.classfile.*; 26 27/** 28 * The {@code ClassFileVisitor} reads a class file using the 29 * {@code com.sun.tools.classfile} library. It iterates over the methods 30 * in a class, and checks MethodParameters attributes against JLS 31 * requirements, as well as assumptions about the javac implementations. 32 * <p> 33 * It enforces the following rules: 34 * <ul> 35 * <li>All non-synthetic methods with arguments must have the 36 * MethodParameters attribute. </li> 37 * <li>At most one MethodParameters attribute per method.</li> 38 * <li>An empty MethodParameters attribute is not allowed (i.e. no 39 * attribute for methods taking no parameters).</li> 40 * <li>The number of recorded parameter names much equal the number 41 * of parameters, including any implicit or synthetic parameters generated 42 * by the compiler.</li> 43 * <li>Although the spec allow recording parameters with no name, the javac 44 * implementation is assumed to record a name for all parameters. That is, 45 * the Methodparameters attribute must record a non-zero, valid constant 46 * pool index for each parameter.</li> 47 * <li>Check presence, expected names (e.g. this$N, $enum$name, ...) and flags 48 * (e.g. ACC_SYNTHETIC, ACC_MANDATED) for compiler generated parameters.</li> 49 * <li>Names of explicit parameters must reflect the names in the Java source. 50 * This is checked by assuming a design pattern where any name is permitted 51 * for the first explicit parameter. For subsequent parameters the following 52 * rule is checked: <i>param[n] == ++param[n-1].charAt(0) + param[n-1]</i> 53 * </ul> 54 */ 55class ClassFileVisitor extends Tester.Visitor { 56 57 Tester tester; 58 59 public String cname; 60 public boolean isEnum; 61 public boolean isInterface; 62 public boolean isInner; 63 public boolean isPublic; 64 public boolean isStatic; 65 public boolean isAnon; 66 public ClassFile classFile; 67 68 69 public ClassFileVisitor(Tester tester) { 70 super(tester); 71 } 72 73 public void error(String msg) { 74 super.error("classfile: " + msg); 75 } 76 77 public void warn(String msg) { 78 super.warn("classfile: " + msg); 79 } 80 81 /** 82 * Read the class and determine some key characteristics, like if it's 83 * an enum, or inner class, etc. 84 */ 85 void visitClass(final String cname, final File cfile, final StringBuilder sb) throws Exception { 86 this.cname = cname; 87 classFile = ClassFile.read(cfile); 88 isEnum = classFile.access_flags.is(AccessFlags.ACC_ENUM); 89 isInterface = classFile.access_flags.is(AccessFlags.ACC_INTERFACE); 90 isPublic = classFile.access_flags.is(AccessFlags.ACC_PUBLIC); 91 isInner = false; 92 isStatic = false; 93 isAnon = false; 94 95 Attribute attr = classFile.getAttribute("InnerClasses"); 96 if (attr != null) attr.accept(new InnerClassVisitor(), null); 97 isAnon = isInner & isAnon; 98 99 sb.append(isStatic ? "static " : "") 100 .append(isPublic ? "public " : "") 101 .append(isEnum ? "enum " : isInterface ? "interface " : "class ") 102 .append(cname).append(" -- "); 103 if (isInner) { 104 sb.append(isAnon ? "anon" : "inner"); 105 } 106 sb.append("\n"); 107 108 for (Method method : classFile.methods) { 109 new MethodVisitor().visitMethod(method, sb); 110 } 111 } 112 113 /** 114 * Used to visit InnerClasses_attribute of a class, 115 * to determne if this class is an local class, and anonymous 116 * inner class or a none-static member class. These types of 117 * classes all have an containing class instances field that 118 * requires an implicit or synthetic constructor argument. 119 */ 120 class InnerClassVisitor extends AttributeVisitor<Void, Void> { 121 public Void visitInnerClasses(InnerClasses_attribute iattr, Void v) { 122 try{ 123 for (InnerClasses_attribute.Info info : iattr.classes) { 124 if (info.getInnerClassInfo(classFile.constant_pool) == null) continue; 125 String in = info.getInnerClassInfo(classFile.constant_pool).getName(); 126 if (in == null || !cname.equals(in)) continue; 127 isInner = true; 128 isAnon = null == info.getInnerName(classFile.constant_pool); 129 isStatic = info.inner_class_access_flags.is(AccessFlags.ACC_STATIC); 130 break; 131 } 132 } catch(Exception e) { 133 throw new IllegalStateException(e); 134 } 135 return null; 136 } 137 } 138 139 /** 140 * Check the MethodParameters attribute of a method. 141 */ 142 class MethodVisitor extends AttributeVisitor<Void, StringBuilder> { 143 144 public String mName; 145 public Descriptor mDesc; 146 public int mParams; 147 public int mAttrs; 148 public int mNumParams; 149 public boolean mSynthetic; 150 public boolean mIsConstructor; 151 public boolean mIsClinit; 152 public boolean mIsBridge; 153 public boolean isFinal; 154 public String prefix; 155 156 void visitMethod(Method method, StringBuilder sb) throws Exception { 157 158 mName = method.getName(classFile.constant_pool); 159 mDesc = method.descriptor; 160 mParams = mDesc.getParameterCount(classFile.constant_pool); 161 mAttrs = method.attributes.attrs.length; 162 mNumParams = -1; // no MethodParameters attribute found 163 mSynthetic = method.access_flags.is(AccessFlags.ACC_SYNTHETIC); 164 mIsConstructor = mName.equals("<init>"); 165 mIsClinit = mName.equals("<clinit>"); 166 prefix = cname + "." + mName + "() - "; 167 mIsBridge = method.access_flags.is(AccessFlags.ACC_BRIDGE); 168 169 if (mIsClinit) { 170 sb = new StringBuilder(); // Discard output 171 } 172 sb.append(cname).append(".").append(mName).append("("); 173 174 for (Attribute a : method.attributes) { 175 a.accept(this, sb); 176 } 177 if (mNumParams == -1) { 178 if (mSynthetic) { 179 // We don't generate MethodParameters attribute for synthetic 180 // methods, so we are creating a parameter pattern to match 181 // ReflectionVisitor API output. 182 for (int i = 0; i < mParams; i++) { 183 if (i == 0) 184 sb.append("arg").append(i); 185 else 186 sb.append(", arg").append(i); 187 } 188 sb.append(")/*synthetic*/"); 189 } else { 190 sb.append(")"); 191 } 192 } 193 sb.append("\n"); 194 195 // IMPL: methods with arguments must have a MethodParameters 196 // attribute, except possibly some synthetic methods. 197 if (mNumParams == -1 && mParams > 0 && ! mSynthetic) { 198 error(prefix + "missing MethodParameters attribute"); 199 } 200 } 201 202 public Void visitMethodParameters(MethodParameters_attribute mp, 203 StringBuilder sb) { 204 205 // SPEC: At most one MethodParameters attribute allowed 206 if (mNumParams != -1) { 207 error(prefix + "Multiple MethodParameters attributes"); 208 return null; 209 } 210 211 mNumParams = mp.method_parameter_table_length; 212 213 // SPEC: An empty attribute is not allowed! 214 if (mNumParams == 0) { 215 error(prefix + "0 length MethodParameters attribute"); 216 return null; 217 } 218 219 // SPEC: one name per parameter. 220 if (mNumParams != mParams) { 221 error(prefix + "found " + mNumParams + 222 " parameters, expected " + mParams); 223 return null; 224 } 225 226 // IMPL: Whether MethodParameters attributes will be generated 227 // for some synthetics is unresolved. For now, assume no. 228 if (mSynthetic) { 229 warn(prefix + "synthetic has MethodParameter attribute"); 230 } 231 232 String sep = ""; 233 String userParam = null; 234 for (int x = 0; x < mNumParams; x++) { 235 isFinal = (mp.method_parameter_table[x].flags & AccessFlags.ACC_FINAL) != 0; 236 // IMPL: Assume all parameters are named, something. 237 int cpi = mp.method_parameter_table[x].name_index; 238 if (cpi == 0) { 239 error(prefix + "name expected, param[" + x + "]"); 240 return null; 241 } 242 243 // SPEC: a non 0 index, must be valid! 244 String param = null; 245 try { 246 param = classFile.constant_pool.getUTF8Value(cpi); 247 if (isFinal) 248 param = "final " + param; 249 sb.append(sep).append(param); 250 sep = ", "; 251 } catch(ConstantPoolException e) { 252 error(prefix + "invalid index " + cpi + " for param[" 253 + x + "]"); 254 return null; 255 } 256 257 258 // Check availability, flags and special names 259 int check = checkParam(mp, param, x, sb, isFinal); 260 if (check < 0) { 261 return null; 262 } 263 264 // TEST: check test assumptions about parameter name. 265 // Expected names are calculated starting with the 266 // 2nd explicit (user given) parameter. 267 // param[n] == ++param[n-1].charAt(0) + param[n-1] 268 String expect = null; 269 if (userParam != null) { 270 char c = userParam.charAt(0); 271 expect = (++c) + userParam; 272 } 273 if(isFinal && expect != null) 274 expect = "final " + expect; 275 if (check > 0) { 276 if(isFinal) { 277 userParam = param.substring(6); 278 } else { 279 userParam = param; 280 } 281 } 282 if (check > 0 && expect != null && !param.equals(expect)) { 283 error(prefix + "param[" + x + "]='" 284 + param + "' expected '" + expect + "'"); 285 return null; 286 } 287 } 288 if (mSynthetic) { 289 sb.append(")/*synthetic*/"); 290 } else { 291 sb.append(")"); 292 } 293 return null; 294 } 295 296 /* 297 * Check a parameter for conformity to JLS and javac specific 298 * assumptions. 299 * Return -1, if an error is detected. Otherwise, return 0, if 300 * the parameter is compiler generated, or 1 for an (presumably) 301 * explicitly declared parameter. 302 */ 303 int checkParam(MethodParameters_attribute mp, String param, int index, 304 StringBuilder sb, boolean isFinal) { 305 306 boolean synthetic = (mp.method_parameter_table[index].flags 307 & AccessFlags.ACC_SYNTHETIC) != 0; 308 boolean mandated = (mp.method_parameter_table[index].flags 309 & AccessFlags.ACC_MANDATED) != 0; 310 311 // Setup expectations for flags and special names 312 String expect = null; 313 boolean allowMandated = false; 314 boolean allowSynthetic = false; 315 if (mSynthetic || synthetic) { 316 // not an implementation gurantee, but okay for now 317 expect = "arg" + index; // default 318 } 319 if (mIsConstructor) { 320 if (isEnum) { 321 if (index == 0) { 322 expect = "\\$enum\\$name"; 323 allowSynthetic = true; 324 } else if(index == 1) { 325 expect = "\\$enum\\$ordinal"; 326 allowSynthetic = true; 327 } 328 } else if (index == 0) { 329 if (isAnon) { 330 expect = "this\\$[0-9]+"; 331 allowMandated = true; 332 if (isFinal) { 333 expect = "final this\\$[0-9]+"; 334 } 335 } else if (isInner && !isStatic) { 336 expect = "this\\$[0-9]+"; 337 allowMandated = true; 338 if (!isPublic) { 339 // some but not all non-public inner classes 340 // have synthetic argument. For now we give 341 // the test a bit of slack and allow either. 342 allowSynthetic = true; 343 } 344 if (isFinal) { 345 expect = "final this\\$[0-9]+"; 346 } 347 } 348 } 349 350 if (synthetic && !mandated && !allowSynthetic) { 351 //patch treatment for local captures 352 if (isAnon || (isInner & !isStatic)) { 353 expect = "val\\$.*"; 354 allowSynthetic = true; 355 if (isFinal) { 356 expect = "final val\\$.*"; 357 } 358 } 359 } 360 } else if (isEnum && mNumParams == 1 && index == 0 && mName.equals("valueOf")) { 361 expect = "name"; 362 allowMandated = true; 363 } else if (mIsBridge) { 364 allowSynthetic = true; 365 /* you can't expect an special name for bridges' parameters. 366 * The name of the original parameters are now copied. 367 */ 368 expect = null; 369 } 370 if (mandated) sb.append("/*implicit*/"); 371 if (synthetic) sb.append("/*synthetic*/"); 372 373 // IMPL: our rules a somewhat fuzzy, sometimes allowing both mandated 374 // and synthetic. However, a parameters cannot be both. 375 if (mandated && synthetic) { 376 error(prefix + "param[" + index + "] == \"" + param 377 + "\" ACC_SYNTHETIC and ACC_MANDATED"); 378 return -1; 379 } 380 // ... but must be either, if both "allowed". 381 if (!(mandated || synthetic) && allowMandated && allowSynthetic) { 382 error(prefix + "param[" + index + "] == \"" + param 383 + "\" expected ACC_MANDATED or ACC_SYNTHETIC"); 384 return -1; 385 } 386 387 // ... if only one is "allowed", we meant "required". 388 if (!mandated && allowMandated && !allowSynthetic) { 389 error(prefix + "param[" + index + "] == \"" + param 390 + "\" expected ACC_MANDATED"); 391 return -1; 392 } 393 if (!synthetic && !allowMandated && allowSynthetic) { 394 error(prefix + "param[" + index + "] == \"" + param 395 + "\" expected ACC_SYNTHETIC"); 396 return -1; 397 } 398 399 // ... and not "allowed", means prohibited. 400 if (mandated && !allowMandated) { 401 error(prefix + "param[" + index + "] == \"" + param 402 + "\" unexpected, is ACC_MANDATED"); 403 return -1; 404 } 405 if (synthetic && !allowSynthetic) { 406 error(prefix + "param[" + index + "] == \"" + param 407 + "\" unexpected, is ACC_SYNTHETIC"); 408 return -1; 409 } 410 411 // Test special name expectations 412 if (expect != null) { 413 if (param.matches(expect)) { 414 return 0; 415 } 416 error(prefix + "param[" + index + "]='" + param + 417 "' expected '" + expect + "'"); 418 return -1; 419 } 420 421 // No further checking for synthetic methods. 422 if (mSynthetic) { 423 return 0; 424 } 425 // Otherwise, do check test parameter naming convention. 426 return 1; 427 } 428 } 429} 430