001package daikon.chicory;
002
003import daikon.Chicory;
004import daikon.plumelib.reflection.Signatures;
005import java.lang.reflect.Constructor;
006import java.lang.reflect.Field;
007import java.lang.reflect.Member;
008import java.lang.reflect.Method;
009import java.lang.reflect.Modifier;
010import java.util.StringJoiner;
011import org.checkerframework.checker.signature.qual.BinaryName;
012
013/** DaikonWriter is the parent class of DeclWriter and DTraceWriter. */
014public abstract class DaikonWriter {
015  /** Controls whether modifiers and the return type are included in the decl output. */
016  protected static final boolean no_modifiers_ppt = true;
017
018  /** Platform-dependent line separator. Should be "\n" on Unix. */
019  public static final String lineSep = System.lineSeparator();
020
021  protected DaikonWriter() {}
022
023  /**
024   * Determines if this field warrants an [ = val ] entry in decls file.
025   *
026   * @param field requires field != null
027   * @return true iff field warrants an [ = val ] entry in the decls files
028   */
029  protected static boolean isStaticConstField(Field field) {
030    Class<?> type = field.getType();
031    int mod = field.getModifiers();
032
033    if (DaikonVariableInfo.dkconfig_constant_infer) {
034      return Modifier.isFinal(mod) && Modifier.isStatic(mod);
035    } else {
036      return Modifier.isFinal(mod) && Modifier.isStatic(mod) && type.isPrimitive();
037    }
038  }
039
040  /**
041   * Given a method, returns the method entry program point name for Daikon.
042   *
043   * @param method non-null method
044   * @return the decorated method entry name for Daikon
045   */
046  public static String methodEntryName(Member method) {
047    return methodName(method, daikon.FileIO.enter_suffix);
048  }
049
050  /**
051   * Given a method, returns the method entry program point name for Daikon. Used when reflection
052   * information is not available.
053   *
054   * @param fullClassName packageName.className
055   * @param types string representation of the declared types of the parameters
056   * @param name the method with modifiers and parameters, such as "public static void
057   *     DataStructures.StackArTester.doNew(int size)"
058   * @param short_name just the method's name ("{@code <init>}" for constructors)
059   * @return the decorated method entry name for Daikon
060   */
061  public static String methodEntryName(
062      String fullClassName, String[] types, String name, String short_name) {
063    return methodName(fullClassName, types, name, short_name, daikon.FileIO.enter_suffix);
064  }
065
066  /**
067   * Given a method, returns the method exit program point name for Daikon.
068   *
069   * @param method non-null method
070   * @param lineNum the line number of a return statement in the method
071   * @return the decorated method exit name for Daikon
072   */
073  public static String methodExitName(Member method, int lineNum) {
074    return methodName(method, daikon.FileIO.exit_suffix + lineNum);
075  }
076
077  /**
078   * Given a method, returns the method exit program point name for Daikon. Used when reflection
079   * information is not available.
080   *
081   * @param fullClassName packageName.className
082   * @param types string representation of the declared types of the parameters
083   * @param name the method name with modifiers and parameters
084   * @param short_name just the method's name ("{@code <init>}" for constructors)
085   * @param lineNum the line number of a return statement in the method
086   * @return the decorated method exit name for Daikon
087   */
088  public static String methodExitName(
089      String fullClassName, String[] types, String name, String short_name, int lineNum) {
090    return methodName(fullClassName, types, name, short_name, daikon.FileIO.exit_suffix + lineNum);
091  }
092
093  /**
094   * Constructs the program point name (which includes the point string at the end)
095   *
096   * @param fullClassName packageName.className
097   * @param types string representation of the declared types of the parameters. For example:
098   *     {"int", "java.lang.Object", "float"}.
099   * @param name the method with modifiers and parameters, such as "public static void
100   *     DataStructures.StackArTester.doNew(int size)"
101   * @param short_name just the method's name ("{@code <init>}" for constructors)
102   * @param point program point type/suffix such as "EXIT" or "ENTER"
103   * @return same thing as {@link #methodName(Member, String)}
104   */
105  private static String methodName(
106      String fullClassName, String[] types, String name, String short_name, String point) {
107
108    // System.out.printf("fullclass: %s !!! name: %s !!! short_name: %s %n",
109    //                  fullClassName, name, short_name);
110
111    boolean isConstructor = name.equals("<init>") || name.equals("");
112
113    if (isConstructor) {
114      // replace <init>'s with the actual class name
115      // so "public void <init>" becomes "public void StackAr" for example
116      short_name = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
117      name = name.replace("<init>", short_name);
118    }
119
120    // build up the string to go inside the parens
121    StringJoiner paramTypes = new StringJoiner(",", "(", ")");
122    for (int i = 0; i < types.length; i++) {
123      paramTypes.add(types[i]);
124    }
125    String pptname = fullClassName + "." + short_name + paramTypes + ":::" + point;
126
127    if (Chicory.debug_ppt_names) {
128      System.out.printf("methodName1 final ppt name = '%s'%n", pptname);
129    }
130
131    // Throwable t = new Throwable("debug");
132    // t.fillInStackTrace();
133    // t.printStackTrace();
134    // System.out.printf("ppt name = %s%n", pptname);
135
136    return pptname;
137  }
138
139  /**
140   * Constructs the program point name (which includes the point string at the end)
141   *
142   * @param member reflection object for the method/constructor
143   * @param point usually "ENTER" or "EXIT"
144   */
145  private static String methodName(Member member, String point) {
146    String fullname;
147    Class<?>[] args;
148    Class<?> declaring_class = member.getDeclaringClass();
149    if (member instanceof Method) {
150      Method method = (Method) member;
151      fullname = declaring_class.getName() + "." + method.getName();
152      args = method.getParameterTypes();
153    } else {
154      Constructor<?> constructor = (Constructor<?>) member;
155      fullname = declaring_class.getName() + "." + declaring_class.getSimpleName();
156      args = constructor.getParameterTypes();
157    }
158    String arg_str = "";
159    for (Class<?> arg : args) {
160      if (arg_str.length() > 0) {
161        arg_str += ", ";
162      }
163      if (arg.isArray()) {
164        arg_str += Signatures.fieldDescriptorToBinaryName(arg.getName());
165      } else {
166        arg_str += arg.getName();
167      }
168    }
169    String ppt_name = String.format("%s(%s):::%s", fullname, arg_str, point);
170    return ppt_name;
171  }
172
173  /** Determines if the given method should be instrumented. */
174  protected boolean shouldInstrumentMethod(Member method) {
175    if (method == null) { // <clinit>
176      return Chicory.instrument_clinit;
177    }
178    int modifiers = method.getModifiers();
179    if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
180      return false;
181    }
182    return true;
183  }
184
185  /**
186   * Returns the class name of the specified class as a binary name (i.e., as the class would have
187   * been declared in Java source code, except with '$' instead of '.' separating outer and inner
188   * classes).
189   */
190  public static @BinaryName String stdClassName(Class<?> type) {
191    return Runtime.classGetNameToBinaryName(type.getName());
192  }
193
194  /** Escapes blanks and backslashes in names written to the decl/dtrace files. */
195  public String escape(String str) {
196
197    // If there is nothing to escape, return the original string
198    if ((str.indexOf('\\') == -1) && (str.indexOf(' ') == -1)) {
199      return str;
200    }
201
202    str = str.replace("\\", "\\\\");
203    str = str.replace(" ", "\\_");
204    return str;
205  }
206}