test/tools/javac/MethodParameters/ClassFileVisitor.java

Mon, 16 Oct 2017 16:07:48 +0800

author
aoqi
date
Mon, 16 Oct 2017 16:07:48 +0800
changeset 2893
ca5783d9a597
parent 2733
7974f6da2d76
parent 2525
2eb010b6cb22
permissions
-rw-r--r--

merge

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

mercurial