001package daikon.dcomp;
002
003import daikon.DynComp;
004import daikon.chicory.ClassInfo;
005import daikon.chicory.DaikonWriter;
006import daikon.chicory.Instrument;
007import daikon.chicory.MethodInfo;
008import daikon.plumelib.bcelutil.BcelUtil;
009import daikon.plumelib.bcelutil.InstructionListUtils;
010import daikon.plumelib.bcelutil.SimpleLog;
011import daikon.plumelib.bcelutil.StackTypes;
012import daikon.plumelib.options.Option;
013import daikon.plumelib.reflection.Signatures;
014import daikon.plumelib.util.EntryReader;
015import java.io.File;
016import java.io.IOException;
017import java.io.InputStream;
018import java.io.PrintStream;
019import java.lang.reflect.Constructor;
020import java.lang.reflect.Modifier;
021import java.net.URL;
022import java.util.ArrayDeque;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.Deque;
026import java.util.HashMap;
027import java.util.HashSet;
028import java.util.Iterator;
029import java.util.LinkedHashMap;
030import java.util.List;
031import java.util.Map;
032import java.util.Set;
033import java.util.concurrent.ConcurrentHashMap;
034import java.util.regex.Pattern;
035import org.apache.bcel.Const;
036import org.apache.bcel.classfile.AnnotationEntry;
037import org.apache.bcel.classfile.Annotations;
038import org.apache.bcel.classfile.Attribute;
039import org.apache.bcel.classfile.ClassParser;
040import org.apache.bcel.classfile.Constant;
041import org.apache.bcel.classfile.ConstantInterfaceMethodref;
042import org.apache.bcel.classfile.Field;
043import org.apache.bcel.classfile.JavaClass;
044import org.apache.bcel.classfile.Method;
045import org.apache.bcel.classfile.RuntimeVisibleAnnotations;
046import org.apache.bcel.classfile.StackMapEntry;
047import org.apache.bcel.classfile.StackMapType;
048import org.apache.bcel.generic.AALOAD;
049import org.apache.bcel.generic.ACONST_NULL;
050import org.apache.bcel.generic.ALOAD;
051import org.apache.bcel.generic.ASTORE;
052import org.apache.bcel.generic.ATHROW;
053import org.apache.bcel.generic.AnnotationEntryGen;
054import org.apache.bcel.generic.ArrayType;
055import org.apache.bcel.generic.BasicType;
056import org.apache.bcel.generic.BranchInstruction;
057import org.apache.bcel.generic.ClassGen;
058import org.apache.bcel.generic.ClassGenException;
059import org.apache.bcel.generic.CodeExceptionGen;
060import org.apache.bcel.generic.DUP;
061import org.apache.bcel.generic.DUP2;
062import org.apache.bcel.generic.DUP_X2;
063import org.apache.bcel.generic.FieldGen;
064import org.apache.bcel.generic.FieldInstruction;
065import org.apache.bcel.generic.GETFIELD;
066import org.apache.bcel.generic.GETSTATIC;
067import org.apache.bcel.generic.IADD;
068import org.apache.bcel.generic.INVOKEDYNAMIC;
069import org.apache.bcel.generic.INVOKEINTERFACE;
070import org.apache.bcel.generic.INVOKESPECIAL;
071import org.apache.bcel.generic.INVOKEVIRTUAL;
072import org.apache.bcel.generic.Instruction;
073import org.apache.bcel.generic.InstructionFactory;
074import org.apache.bcel.generic.InstructionHandle;
075import org.apache.bcel.generic.InstructionList;
076import org.apache.bcel.generic.InstructionTargeter;
077import org.apache.bcel.generic.InvokeInstruction;
078import org.apache.bcel.generic.LDC;
079import org.apache.bcel.generic.LDC2_W;
080import org.apache.bcel.generic.LineNumberGen;
081import org.apache.bcel.generic.LoadInstruction;
082import org.apache.bcel.generic.LocalVariableGen;
083import org.apache.bcel.generic.LocalVariableInstruction;
084import org.apache.bcel.generic.MULTIANEWARRAY;
085import org.apache.bcel.generic.MethodGen;
086import org.apache.bcel.generic.NOP;
087import org.apache.bcel.generic.ObjectType;
088import org.apache.bcel.generic.PUTFIELD;
089import org.apache.bcel.generic.PUTSTATIC;
090import org.apache.bcel.generic.ReferenceType;
091import org.apache.bcel.generic.ReturnInstruction;
092import org.apache.bcel.generic.SWAP;
093import org.apache.bcel.generic.StoreInstruction;
094import org.apache.bcel.generic.Type;
095import org.apache.bcel.generic.TypedInstruction;
096import org.apache.bcel.verifier.structurals.OperandStack;
097import org.checkerframework.checker.lock.qual.GuardSatisfied;
098import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
099import org.checkerframework.checker.nullness.qual.KeyFor;
100import org.checkerframework.checker.nullness.qual.Nullable;
101import org.checkerframework.checker.signature.qual.BinaryName;
102import org.checkerframework.checker.signature.qual.ClassGetName;
103import org.checkerframework.checker.signature.qual.DotSeparatedIdentifiers;
104import org.checkerframework.dataflow.qual.Pure;
105import org.checkerframework.dataflow.qual.SideEffectFree;
106
107/** Instruments a class file to perform Dynamic Comparability. */
108@SuppressWarnings({"nullness"}) //
109public class DCInstrument extends InstructionListUtils {
110
111  /**
112   * Used when testing to continue processing if an error occurs. Currently, This flag is only used
113   * by BuildJDK.
114   */
115  @Option("Halt if an instrumentation error occurs")
116  public static boolean quit_if_error = true;
117
118  /** Unmodified version of input class. */
119  protected JavaClass orig_class;
120
121  /** ClassGen for the current class. */
122  protected ClassGen gen;
123
124  /** MethodGen for the current method. */
125  protected MethodGen mgen;
126
127  /** Is the current class a member of the JDK? */
128  protected boolean in_jdk;
129
130  /** The BCEL InstructionFactory for generating byte code instructions. */
131  protected InstructionFactory ifact;
132
133  /** The loader that loaded the Class to instrument. */
134  protected @Nullable ClassLoader loader;
135
136  /** Has an {@code <init>} method completed initialization? */
137  protected boolean constructor_is_initialized;
138
139  /** Local that stores the tag frame for the current method. */
140  protected LocalVariableGen tag_frame_local;
141
142  // Argument descriptors
143  protected static Type[] two_objects = new Type[] {Type.OBJECT, Type.OBJECT};
144  protected static Type[] object_string = new Type[] {Type.OBJECT, Type.STRING};
145  protected static Type[] two_ints = new Type[] {Type.INT, Type.INT};
146  protected static Type[] object_int = new Type[] {Type.OBJECT, Type.INT};
147  protected static Type[] string_arg = new Type[] {Type.STRING};
148  protected static Type[] integer_arg = new Type[] {Type.INT};
149  protected static Type[] float_arg = new Type[] {Type.FLOAT};
150  protected static Type[] double_arg = new Type[] {Type.DOUBLE};
151  protected static Type[] boolean_arg = new Type[] {Type.BOOLEAN};
152  protected static Type[] long_arg = new Type[] {Type.LONG};
153  protected static Type[] short_arg = new Type[] {Type.SHORT};
154  protected static Type[] object_arg = new Type[] {Type.OBJECT};
155  protected static Type[] CharSequence_arg = new Type[] {new ObjectType("java.lang.CharSequence")};
156  protected static Type javalangClass = new ObjectType("java.lang.Class");
157  protected static Type[] class_str = new Type[] {javalangClass, Type.STRING};
158
159  // Type descriptors
160  protected static Type object_arr = new ArrayType(Type.OBJECT, 1);
161  // private Type int_arr = new ArrayType (Type.INT, 1);
162  protected static ObjectType throwable = new ObjectType("java.lang.Throwable");
163  protected ObjectType dcomp_marker;
164  protected static ObjectType javalangObject = new ObjectType("java.lang.Object");
165
166  // Debug loggers
167  /** Log file if debug_native is enabled. */
168  protected static SimpleLog debug_native = new SimpleLog(false);
169
170  /** Log file if debug_dup is enabled. */
171  protected static SimpleLog debug_dup = new SimpleLog(false);
172
173  // Flags to enable additional console output for debugging
174  /** If true, enable JUnit analysis debugging. */
175  protected static final boolean debugJUnitAnalysis = false;
176
177  /** If true, enable {@link #getDefiningInterface} debugging. */
178  protected static final boolean debugGetDefiningInterface = false;
179
180  /** If true, enable {@link #handleInvoke} debugging. */
181  protected static final boolean debugHandleInvoke = false;
182
183  /** Keeps track of the methods that were not successfully instrumented. */
184  protected List<String> skipped_methods = new ArrayList<>();
185
186  /**
187   * Specifies if we are to use an instrumented version of the JDK. Calls into the JDK must be
188   * modified to remove the arguments from the tag stack if the JDK is not instrumented. This flag
189   * is set/reset in daikon.dcomp.Premain.
190   */
191  protected static boolean jdk_instrumented = true;
192
193  /** Either "java.lang.DCompInstrumented" or "daikon.dcomp.DCompInstrumented". */
194  // Static because used in DCRuntime
195  protected static @BinaryName String instrumentation_interface;
196
197  /** Either "java.lang" or "daikon.dcomp". */
198  protected @DotSeparatedIdentifiers String dcomp_prefix;
199
200  /** Either "daikon.dcomp.DCRuntime" or "java.lang.DCRuntime". */
201  protected @DotSeparatedIdentifiers String dcompRuntimeClassName = "daikon.dcomp.DCRuntime";
202
203  /** Name prefix for tag setter methods. */
204  protected static final String SET_TAG = "set_tag";
205
206  /** Name prefix for tag getter methods. */
207  protected static final String GET_TAG = "get_tag";
208
209  /** Set of JUnit test classes. */
210  protected static Set<String> junitTestClasses = new HashSet<>();
211
212  /** Possible states of JUnit test discovery. */
213  protected enum JUnitState {
214    NOT_SEEN,
215    STARTING,
216    TEST_DISCOVERY,
217    RUNNING
218  };
219
220  /** Current state of JUnit test discovery. */
221  protected static JUnitState junit_state = JUnitState.NOT_SEEN;
222
223  /** Have we seen 'JUnitCommandLineParseResult.parse'? */
224  protected static boolean junit_parse_seen = false;
225
226  /**
227   * Map from each static field name to its unique integer id. Note that while it's intuitive to
228   * think that each static should show up exactly once, that is not the case. A static defined in a
229   * superclass can be accessed through each of its subclasses. Tag accessor methods must be added
230   * in each subclass and each should return the same id. We thus will lookup the same name multiple
231   * times.
232   */
233  static Map<String, Integer> static_field_id = new LinkedHashMap<>();
234
235  /**
236   * Map from class name to its access_flags. Used to cache the results of the lookup done in {@link
237   * #getAccessFlags}. If a class is marked ACC_ANNOTATION then it will not have been instrumented.
238   */
239  static Map<String, Integer> accessFlags = new HashMap<>();
240
241  /** Integer constant of access_flag value of ACC_ANNOTATION. */
242  static Integer Integer_ACC_ANNOTATION = Integer.valueOf(Const.ACC_ANNOTATION);
243
244  /**
245   * Array of classes whose fields are not initialized from java. Since the fields are not
246   * initialized from java, their tag storage is not allocated as part of a store, but rather must
247   * be allocated as part of a load. We call a special runtime method for this so that we can check
248   * for this in other cases.
249   */
250  protected static String[] uninit_classes =
251      new String[] {
252        "java.lang.String",
253        "java.lang.Class",
254        "java.lang.StringBuilder",
255        "java.lang.AbstractStringBuilder",
256      };
257
258  /**
259   * List of Object methods. Since we can't instrument Object, none of these can be instrumented,
260   * and most of them don't provide useful comparability information anyway. The equals method and
261   * the clone method are special-cased in the {@link #handleInvoke} routine.
262   */
263  protected static MethodDef[] obj_methods =
264      new MethodDef[] {
265        new MethodDef("finalize", new Type[0]),
266        new MethodDef("getClass", new Type[0]),
267        new MethodDef("hashCode", new Type[0]),
268        new MethodDef("notify", new Type[0]),
269        new MethodDef("notifyall", new Type[0]),
270        new MethodDef("toString", new Type[0]),
271        new MethodDef("wait", new Type[0]),
272        new MethodDef("wait", new Type[] {Type.LONG}),
273        new MethodDef("wait", new Type[] {Type.LONG, Type.INT}),
274      };
275
276  protected static InstructionList global_catch_il;
277  protected static CodeExceptionGen global_exception_handler;
278  private InstructionHandle insertion_placeholder;
279
280  /** Class that defines a method (by its name and argument types) */
281  static class MethodDef {
282    String name;
283    Type[] arg_types;
284
285    MethodDef(String name, Type[] arg_types) {
286      this.name = name;
287      this.arg_types = arg_types;
288    }
289
290    @EnsuresNonNullIf(result = true, expression = "#1")
291    boolean equals(@GuardSatisfied MethodDef this, String name, Type[] arg_types) {
292      if (!name.equals(this.name)) {
293        return false;
294      }
295      if (this.arg_types.length != arg_types.length) {
296        return false;
297      }
298      for (int ii = 0; ii < arg_types.length; ii++) {
299        if (!arg_types[ii].equals(this.arg_types[ii])) {
300          return false;
301        }
302      }
303      return true;
304    }
305
306    @EnsuresNonNullIf(result = true, expression = "#1")
307    @Pure
308    @Override
309    public boolean equals(@GuardSatisfied MethodDef this, @GuardSatisfied @Nullable Object obj) {
310      if (!(obj instanceof MethodDef)) {
311        return false;
312      }
313      MethodDef md = (MethodDef) obj;
314      return equals(md.name, md.arg_types);
315    }
316
317    @Pure
318    @Override
319    public int hashCode(@GuardSatisfied MethodDef this) {
320      int code = name.hashCode();
321      for (Type arg : arg_types) {
322        code += arg.hashCode();
323      }
324      return code;
325    }
326  }
327
328  /** Class that defines a range of byte code within a method. */
329  static class CodeRange {
330    int start_pc;
331    int len;
332
333    CodeRange(int start_pc, int len) {
334      this.start_pc = start_pc;
335      this.len = len;
336    }
337
338    public boolean contains(int offset) {
339      return (offset >= start_pc) && (offset < (start_pc + len));
340    }
341
342    @SideEffectFree
343    @Override
344    public String toString(@GuardSatisfied CodeRange this) {
345      return String.format("Code range: %d..%d", start_pc, start_pc + len - 1);
346    }
347  }
348
349  /** Initialize with the original class and whether or not the class is part of the JDK. */
350  @SuppressWarnings("StaticAssignmentInConstructor") // instrumentation_interface
351  public DCInstrument(JavaClass orig_class, boolean in_jdk, @Nullable ClassLoader loader) {
352    super();
353    this.orig_class = orig_class;
354    this.in_jdk = in_jdk;
355    this.loader = loader;
356    gen = new ClassGen(orig_class);
357    pool = gen.getConstantPool();
358    ifact = new InstructionFactory(gen);
359    constructor_is_initialized = false;
360    if (jdk_instrumented) {
361      dcomp_prefix = "java.lang";
362    } else {
363      dcomp_prefix = "daikon.dcomp";
364    }
365    dcomp_marker = new ObjectType(Signatures.addPackage(dcomp_prefix, "DCompMarker"));
366    if (BcelUtil.javaVersion == 8) {
367      dcomp_prefix = "daikon.dcomp";
368    }
369    instrumentation_interface = Signatures.addPackage(dcomp_prefix, "DCompInstrumented");
370
371    // System.out.printf("DCInstrument %s%n", orig_class.getClassName());
372    // Turn on some of the logging based on debug option.
373    debugInstrument.enabled = DynComp.debug || Premain.debug_dcinstrument;
374    debug_native.enabled = DynComp.debug;
375  }
376
377  /**
378   * Instruments the original class to perform dynamic comparabilty and returns the new class
379   * definition.
380   *
381   * @return the modified JavaClass
382   */
383  public JavaClass instrument() {
384
385    String classname = gen.getClassName();
386
387    // Don't know where I got this idea.  They are executed.  Don't remember why
388    // adding dcomp marker causes problems.
389    // Don't instrument annotations.  They aren't executed and adding
390    // the marker argument causes subtle errors
391    if ((gen.getModifiers() & Const.ACC_ANNOTATION) != 0) {
392      Instrument.debug_transform.log("Not instrumenting annotation %s%n", classname);
393      // WHY NOT RETURN NULL?
394      return gen.getJavaClass().copy();
395    }
396
397    Instrument.debug_transform.log("Instrumenting class %s%n", classname);
398    Instrument.debug_transform.indent();
399
400    // Create the ClassInfo for this class and its list of methods
401    ClassInfo class_info = new ClassInfo(classname, loader);
402    boolean track_class = false;
403
404    // Handle object methods for this class
405    handle_object(gen);
406
407    // Have all top-level classes implement our interface
408    if (gen.getSuperclassName().equals("java.lang.Object")) {
409      // Add equals method if it doesn't already exist. This ensures
410      // that an instrumented version, equals(Object, DCompMarker),
411      // will be created in this class.
412      Method eq = gen.containsMethod("equals", "(Ljava/lang/Object;)Z");
413      if (eq == null) {
414        debugInstrument.log("Added equals method%n");
415        add_equals_method(gen);
416      }
417
418      // Add DCompInstrumented interface and the required
419      // equals_dcomp_instrumented method.
420      add_dcomp_interface(gen);
421    }
422
423    // A very tricky special case: If JUnit is running and the current
424    // class has been passed to JUnit on the command line, then this
425    // is a JUnit test class and our normal instrumentation will
426    // cause JUnit to complain about multiple constructors and
427    // methods that should have no arguments. To work around these
428    // restrictions, we replace rather than duplicate each method
429    // we instrument and we do not add the dcomp marker argument.
430    // We must also remember the class name so if we see a subsequent
431    // call to one of its methods we do not add the dcomp argument.
432
433    Instrument.debug_transform.log("junit_state: %s%n", junit_state);
434
435    StackTraceElement[] stack_trace;
436
437    switch (junit_state) {
438      case NOT_SEEN:
439        if (classname.startsWith("org.junit")) {
440          junit_state = JUnitState.STARTING;
441        }
442        break;
443
444      case STARTING:
445        // Now check to see if JUnit is looking for test class(es).
446        stack_trace = Thread.currentThread().getStackTrace();
447        // [0] is getStackTrace
448        for (int i = 1; i < stack_trace.length; i++) {
449          if (debugJUnitAnalysis) {
450            System.out.printf(
451                "%s : %s%n", stack_trace[i].getClassName(), stack_trace[i].getMethodName());
452          }
453          if (isJunitTrigger(stack_trace[i].getClassName(), stack_trace[i].getMethodName())) {
454            junit_parse_seen = true;
455            junit_state = JUnitState.TEST_DISCOVERY;
456            break;
457          }
458        }
459        break;
460
461      case TEST_DISCOVERY:
462        // Now check to see if JUnit is done looking for test class(es).
463        boolean local_junit_parse_seen = false;
464        stack_trace = Thread.currentThread().getStackTrace();
465        // [0] is getStackTrace
466        for (int i = 1; i < stack_trace.length; i++) {
467          if (debugJUnitAnalysis) {
468            System.out.printf(
469                "%s : %s%n", stack_trace[i].getClassName(), stack_trace[i].getMethodName());
470          }
471          if (isJunitTrigger(stack_trace[i].getClassName(), stack_trace[i].getMethodName())) {
472            local_junit_parse_seen = true;
473            break;
474          }
475        }
476        if (junit_parse_seen && !local_junit_parse_seen) {
477          junit_parse_seen = false;
478          junit_state = JUnitState.RUNNING;
479        } else if (!junit_parse_seen && local_junit_parse_seen) {
480          junit_parse_seen = true;
481        }
482        break;
483
484      case RUNNING:
485        if (debugJUnitAnalysis) {
486          stack_trace = Thread.currentThread().getStackTrace();
487          // [0] is getStackTrace
488          for (int i = 1; i < stack_trace.length; i++) {
489            System.out.printf(
490                "%s : %s%n", stack_trace[i].getClassName(), stack_trace[i].getMethodName());
491          }
492        }
493        // nothing to do
494        break;
495
496      default:
497        throw new Error("invalid junit_state");
498    }
499
500    Instrument.debug_transform.log("junit_state: %s%n", junit_state);
501
502    boolean junit_test_class = false;
503    if (junit_state == JUnitState.TEST_DISCOVERY) {
504      // We have a possible JUnit test class.  We need to verify by
505      // one of two methods.  Either the class is a subclass of
506      // junit.framework.TestCase or one of its methods has a
507      // RuntimeVisibleAnnotation of org/junit/Test.
508      Deque<String> classnameStack = new ArrayDeque<>();
509      String super_class;
510      String this_class = classname;
511      while (true) {
512        super_class = getSuperclassName(this_class);
513        if (super_class == null) {
514          // something has gone wrong
515          break;
516        }
517        if (debugJUnitAnalysis) {
518          System.out.printf("this_class: %s%n", this_class);
519          System.out.printf("super_class: %s%n", super_class);
520        }
521        if (super_class.equals("junit.framework.TestCase")) {
522          // This is a junit test class and so are the
523          // elements of classnameStack.
524          junit_test_class = true;
525          junitTestClasses.add(this_class);
526          while (!classnameStack.isEmpty()) {
527            junitTestClasses.add(classnameStack.pop());
528          }
529          break;
530        } else if (super_class.equals("java.lang.Object")) {
531          // We're done; not a junit test class.
532          // Ignore items on classnameStack.
533          break;
534        }
535        // Recurse and check the super_class.
536        classnameStack.push(this_class);
537        this_class = super_class;
538      }
539
540      if (!junit_test_class) {
541        // need to check for junit Test annotation on a method
542        searchloop:
543        for (Method m : gen.getMethods()) {
544          for (final Attribute attribute : m.getAttributes()) {
545            if (attribute instanceof RuntimeVisibleAnnotations) {
546              if (debugJUnitAnalysis) {
547                System.out.printf("attribute: %s%n", attribute.toString());
548              }
549              for (final AnnotationEntry item : ((Annotations) attribute).getAnnotationEntries()) {
550                if (debugJUnitAnalysis) {
551                  System.out.printf("item: %s%n", item.toString());
552                }
553                if (item.toString().endsWith("org/junit/Test;") // JUnit 4
554                    || item.toString().endsWith("org/junit/jupiter/api/Test;") // JUnit 5
555                ) {
556                  junit_test_class = true;
557                  junitTestClasses.add(this_class);
558                  break searchloop;
559                }
560              }
561            }
562          }
563        }
564      }
565
566      if (junit_test_class) {
567        Instrument.debug_transform.log("JUnit test class: %s%n", classname);
568      } else {
569        Instrument.debug_transform.log("Not a JUnit test class: %s%n", classname);
570      }
571    }
572
573    // Process each method
574    for (Method m : gen.getMethods()) {
575
576      tag_frame_local = null;
577      try {
578        // Note whether we want to track the daikon variables in this method
579        boolean track = should_track(classname, m.getName(), methodEntryName(classname, m));
580
581        // We do not want to track bridge methods the compiler has synthesized as
582        // they are overloaded on return type which normal Java does not support.
583        if ((m.getAccessFlags() & Const.ACC_BRIDGE) != 0) {
584          track = false;
585        }
586
587        // If any one method is tracked, then the class is tracked.
588        if (track) {
589          track_class = true;
590        }
591
592        // If we are tracking variables, make sure the class is public
593        if (track && !gen.isPublic()) {
594          gen.isPrivate(false);
595          gen.isProtected(false);
596          gen.isPublic(true);
597        }
598
599        Instrument.debug_transform.log(
600            "  Processing method %s, track=%b%n", simplify_method_name(m), track);
601        Instrument.debug_transform.indent();
602
603        MethodGen mg = new MethodGen(m, classname, pool);
604        mgen = mg; // copy to global
605
606        InstructionList il = mg.getInstructionList();
607        boolean has_code = (il != null);
608        if (has_code) {
609          setCurrentStackMapTable(mg, gen.getMajor());
610          buildUninitializedNewMap(il);
611        }
612
613        fixLocalVariableTable(mg);
614
615        // If the method is native
616        if (mg.isNative()) {
617
618          // Create Java code that cleans up the tag stack and calls the real native method.
619          fix_native(gen, mg);
620          has_code = true;
621          setCurrentStackMapTable(mg, gen.getMajor());
622
623          // Add the DCompMarker argument to distinguish our version
624          add_dcomp_arg(mg);
625
626        } else { // normal method
627
628          if (!junit_test_class) {
629            // Add the DCompMarker argument to distinguish our version
630            add_dcomp_arg(mg);
631          }
632
633          // Create a MethodInfo that describes this method's arguments
634          // and exit line numbers (information not available via reflection)
635          // and add it to the list for this class.
636          MethodInfo mi = null;
637          if (track && has_code) {
638            mi = create_method_info(class_info, mg);
639            class_info.method_infos.add(mi);
640            DCRuntime.methods.add(mi);
641          }
642
643          // Instrument the method
644          if (has_code) {
645            // Create the local to store the tag frame for this method
646            tag_frame_local = create_tag_frame_local(mg);
647            build_exception_handler(mg);
648            instrument_method(mg);
649            if (track) {
650              add_enter(mg, mi, DCRuntime.methods.size() - 1);
651              add_exit(mg, mi, DCRuntime.methods.size() - 1);
652            }
653            install_exception_handler(mg);
654          }
655        }
656
657        if (has_code) {
658          updateUninitializedNewOffsets(mg.getInstructionList());
659          createNewStackMapAttribute(mg);
660          mg.setMaxLocals();
661          mg.setMaxStack();
662        } else {
663          mg.removeCodeAttributes();
664          mg.removeLocalVariables();
665        }
666
667        remove_local_variable_type_table(mg);
668
669        // We do not want to copy the @HotSpotIntrinsicCandidate annotations from
670        // the original method to our instrumented method as the signature will
671        // not match anything in the JVM's list.  This won't cause an execution
672        // problem but will produce a massive number of warnings.
673        // JDK 11: @HotSpotIntrinsicCandidate
674        // JDK 17: @IntrinsicCandidate
675        AnnotationEntryGen[] aes = mg.getAnnotationEntries();
676        for (AnnotationEntryGen item : aes) {
677          String type = item.getTypeName();
678          if (type.endsWith("IntrinsicCandidate;")) {
679            mg.removeAnnotationEntry(item);
680          }
681        }
682
683        // Can't duplicate 'main' or 'clinit' or a JUnit test.
684        boolean replacingMethod = BcelUtil.isMain(mg) || BcelUtil.isClinit(mg) || junit_test_class;
685        try {
686          if (has_code) {
687            il = mg.getInstructionList();
688            InstructionHandle end = il.getEnd();
689            int length = end.getPosition() + end.getInstruction().getLength();
690            if (length >= Const.MAX_CODE_SIZE) {
691              throw new ClassGenException(
692                  "Code array too big: must be smaller than " + Const.MAX_CODE_SIZE + " bytes.");
693            }
694          }
695          if (replacingMethod) {
696            gen.replaceMethod(m, mg.getMethod());
697            if (BcelUtil.isMain(mg)) {
698              gen.addMethod(create_dcomp_stub(mg).getMethod());
699            }
700          } else {
701            gen.addMethod(mg.getMethod());
702          }
703        } catch (Exception e) {
704          String s = e.getMessage();
705          if (s == null) {
706            throw e;
707          }
708          if (s.startsWith("Branch target offset too large")
709              || s.startsWith("Code array too big")) {
710            System.err.printf(
711                "DynComp warning: ClassFile: %s - method %s is too large to instrument and is"
712                    + " being skipped.%n",
713                classname, mg.getName());
714            // Build a dummy instrumented method that has DCompMarker
715            // argument and no instrumentation.
716            // first, restore unmodified method
717            mg = new MethodGen(m, classname, pool);
718            // restore StackMapTable
719            setCurrentStackMapTable(mg, gen.getMajor());
720            // Add the DCompMarker argument
721            add_dcomp_arg(mg);
722            remove_local_variable_type_table(mg);
723            // try again
724            if (replacingMethod) {
725              gen.replaceMethod(m, mg.getMethod());
726              if (BcelUtil.isMain(mg)) {
727                gen.addMethod(create_dcomp_stub(mg).getMethod());
728              }
729            } else {
730              gen.addMethod(mg.getMethod());
731            }
732          } else {
733            throw e;
734          }
735        }
736        Instrument.debug_transform.exdent();
737      } catch (Throwable t) {
738        // debug code
739        // t.printStackTrace();
740        if (debugInstrument.enabled) {
741          t.printStackTrace();
742        }
743        throw new Error("Unexpected error processing " + classname + "." + m.getName(), t);
744      }
745    }
746
747    // Add tag accessor methods for each primitive in the class
748    create_tag_accessors(gen);
749
750    // Keep track of when the class is initialized (so we don't look
751    // for fields in uninitialized classes)
752    track_class_init();
753    Instrument.debug_transform.exdent();
754
755    // The code that builds the list of daikon variables for each ppt
756    // needs to know what classes are instrumented.  Its looks in the
757    // Chicory runtime for this information.
758    if (track_class) {
759      Instrument.debug_transform.log("DCInstrument adding %s to all class list%n", class_info);
760      synchronized (daikon.chicory.SharedData.all_classes) {
761        daikon.chicory.SharedData.all_classes.add(class_info);
762      }
763    }
764    Instrument.debug_transform.log("Instrumentation complete: %s%n", classname);
765
766    return gen.getJavaClass().copy();
767  }
768
769  /**
770   * Returns true if the specified classname.method_name is the root of JUnit startup code.
771   *
772   * @param classname class to be checked
773   * @param method_name method to be checked
774   * @return true if the given method is a JUnit trigger
775   */
776  boolean isJunitTrigger(String classname, String method_name) {
777    if ((classname.contains("JUnitCommandLineParseResult")
778            && method_name.equals("parse")) // JUnit 4
779        || (classname.contains("EngineDiscoveryRequestResolution")
780            && method_name.equals("resolve")) // JUnit 5
781    ) {
782      return true;
783    }
784    return false;
785  }
786
787  /**
788   * General Java Runtime instrumentation strategy:
789   *
790   * <p>It is a bit of a misnomer, but the Daikon code and documentation uses the term JDK to refer
791   * to the Java Runtime Environment class libraries. In Java 8 and earlier, they were usually found
792   * in {@code <your java installation>/jre/lib/rt.jar}. For these versions of Java, we
793   * pre-instrumented the entire rt.jar.
794   *
795   * <p>In Java 9 and later, the Java Runtime classes have been divided into modules that are
796   * usually found in: {@code <your java installation>/jmods/*.jmod}.
797   *
798   * <p>With the conversion to modules for Java 9 and beyond, we have elected to pre-instrument only
799   * java.base.jmod and instrument all other Java Runtime (aka JDK) classes dynamically as they are
800   * loaded.
801   *
802   * <p>Post Java 8 there are increased security checks when loading JDK classes. In particular, the
803   * core classes contained in the java.base module may not reference anything outside of java.base.
804   * This means we cannot pre-instrument classes in the same manner as was done for Java 8 as this
805   * would introduce external references to the DynComp runtime (DCRuntime.java).
806   *
807   * <p>However, we can get around this restriction in the following manner: We create a shadow
808   * DynComp runtime called java.lang.DCRuntime that contains all the public methods of
809   * daikon.dcomp.DCRuntime, but with method bodies that contain only a return statement. We
810   * pre-instrument java.base the same as we would for JDK 8, but change all references to
811   * daikon.dcomp.DCRuntime to refer to java.lang.DCRuntime instead, and thus pass the security load
812   * test. During DynComp initialization (in Premain) we use java.lang.instrument.redefineClasses to
813   * replace the dummy java.lang.DCRuntime with a version where each method calls the corresponding
814   * method in daikon.dcomp.DCRuntime. The Java runtime does not enforce the security check in this
815   * case.
816   */
817
818  /**
819   * Instruments a JDK class to perform dynamic comparability and returns the new class definition.
820   * A second version of each method in the class is created which is instrumented for
821   * comparability.
822   *
823   * @return the modified JavaClass
824   */
825  public JavaClass instrument_jdk() {
826
827    String classname = gen.getClassName();
828
829    // Don't instrument annotations.  They aren't executed and adding
830    // the marker argument causes subtle errors
831    if ((gen.getModifiers() & Const.ACC_ANNOTATION) != 0) {
832      Instrument.debug_transform.log("Not instrumenting annotation %s%n", classname);
833      // MUST NOT RETURN NULL
834      return gen.getJavaClass().copy();
835    }
836
837    int i = classname.lastIndexOf('.');
838    if (i > 0) {
839      // Don't instrument problem packages.
840      // See Premain.java for a list and explainations.
841      String packageName = classname.substring(0, i);
842      if (Premain.problem_packages.contains(packageName)) {
843        Instrument.debug_transform.log("Skipping problem package %s%n", packageName);
844        return gen.getJavaClass().copy();
845      }
846    }
847
848    if (BcelUtil.javaVersion > 8) {
849      // Don't instrument problem classes.
850      // See Premain.java for a list and explainations.
851      if (Premain.problem_classes.contains(classname)) {
852        Instrument.debug_transform.log("Skipping problem class %s%n", classname);
853        return gen.getJavaClass().copy();
854      }
855      dcompRuntimeClassName = "java.lang.DCRuntime";
856    }
857
858    Instrument.debug_transform.log("Instrumenting class(JDK) %s%n", classname);
859    Instrument.debug_transform.indent();
860
861    // Handle object methods for this class
862    handle_object(gen);
863
864    // Have all top-level classes implement our interface
865    if (gen.getSuperclassName().equals("java.lang.Object")) {
866      // Add equals method if it doesn't already exist. This ensures
867      // that an instrumented version, equals(Object, DCompMarker),
868      // will be created in this class.
869      Method eq = gen.containsMethod("equals", "(Ljava/lang/Object;)Z");
870      if (eq == null) {
871        debugInstrument.log("Added equals method%n");
872        add_equals_method(gen);
873      }
874      // Add DCompInstrumented interface and the required
875      // equals_dcomp_instrumented method.
876      add_dcomp_interface(gen);
877    }
878
879    // Process each method
880    for (Method m : gen.getMethods()) {
881
882      tag_frame_local = null;
883      try {
884        // Don't modify class initialization methods.  They can't affect
885        // user comparability and there isn't any way to get a second
886        // copy of them.
887        if (BcelUtil.isClinit(m)) {
888          continue;
889        }
890
891        Instrument.debug_transform.log("  Processing method %s%n", simplify_method_name(m));
892        Instrument.debug_transform.indent();
893
894        MethodGen mg = new MethodGen(m, classname, pool);
895        mgen = mg; // copy to global
896
897        InstructionList il = mg.getInstructionList();
898        boolean has_code = (il != null);
899        if (has_code) {
900          setCurrentStackMapTable(mg, gen.getMajor());
901          buildUninitializedNewMap(il);
902        }
903
904        fixLocalVariableTable(mg);
905
906        // If the method is native
907        if (mg.isNative()) {
908
909          // Create Java code that cleans up the tag stack and calls the real native method.
910          fix_native(gen, mg);
911          has_code = true;
912          setCurrentStackMapTable(mg, gen.getMajor());
913
914          // Add the DCompMarker argument to distinguish our version
915          add_dcomp_arg(mg);
916
917        } else { // normal method
918
919          // Add the DCompMarker argument to distinguish our version
920          add_dcomp_arg(mg);
921
922          // Instrument the method
923          if (has_code) {
924            // Create the local to store the tag frame for this method
925            tag_frame_local = create_tag_frame_local(mg);
926            build_exception_handler(mg);
927            instrument_method(mg);
928            install_exception_handler(mg);
929          }
930        }
931
932        if (has_code) {
933          updateUninitializedNewOffsets(mg.getInstructionList());
934          createNewStackMapAttribute(mg);
935          mg.setMaxLocals();
936          mg.setMaxStack();
937        } else {
938          mg.removeCodeAttributes();
939          mg.removeLocalVariables();
940        }
941
942        remove_local_variable_type_table(mg);
943
944        // We do not want to copy the @HotSpotIntrinsicCandidate annotations from
945        // the original method to our instrumented method as the signature will
946        // not match anything in the JVM's list.  This won't cause an execution
947        // problem but will produce a massive number of warnings.
948        // JDK 11: @HotSpotIntrinsicCandidate
949        // JDK 17: @IntrinsicCandidate
950        AnnotationEntryGen[] aes = mg.getAnnotationEntries();
951        for (AnnotationEntryGen item : aes) {
952          String type = item.getTypeName();
953          if (type.endsWith("IntrinsicCandidate;")) {
954            mg.removeAnnotationEntry(item);
955          }
956        }
957
958        try {
959          if (has_code) {
960            il = mg.getInstructionList();
961            InstructionHandle end = il.getEnd();
962            int length = end.getPosition() + end.getInstruction().getLength();
963            if (length >= Const.MAX_CODE_SIZE) {
964              throw new ClassGenException(
965                  "Code array too big: must be smaller than " + Const.MAX_CODE_SIZE + " bytes.");
966            }
967          }
968          gen.addMethod(mg.getMethod());
969        } catch (Exception e) {
970          String s = e.getMessage();
971          if (s == null) {
972            throw e;
973          }
974          if (s.startsWith("Branch target offset too large")
975              || s.startsWith("Code array too big")) {
976            System.err.printf(
977                "DynComp warning: ClassFile: %s - method %s is too large to instrument and is"
978                    + " being skipped.%n",
979                classname, mg.getName());
980            // Build a dummy instrumented method that has DCompMarker
981            // argument and no instrumentation.
982            // first, restore unmodified method
983            mg = new MethodGen(m, classname, pool);
984            // restore StackMapTable
985            setCurrentStackMapTable(mg, gen.getMajor());
986            // Add the DCompMarker argument
987            add_dcomp_arg(mg);
988            remove_local_variable_type_table(mg);
989            // try again
990            gen.addMethod(mg.getMethod());
991          } else {
992            throw e;
993          }
994        }
995
996        Instrument.debug_transform.exdent();
997      } catch (Throwable t) {
998        if (debugInstrument.enabled) {
999          t.printStackTrace();
1000        }
1001        skip_method(mgen);
1002        if (quit_if_error) {
1003          throw new Error("Unexpected error processing " + classname + "." + m.getName(), t);
1004        } else {
1005          System.err.printf("Unexpected error processing %s.%s: %s%n", classname, m.getName(), t);
1006          System.err.printf("Method is NOT instrumented.%n");
1007        }
1008      }
1009    }
1010
1011    // Add tag accessor methods for each primitive in the class
1012    create_tag_accessors(gen);
1013
1014    // We don't need to track class initialization in the JDK because
1015    // that is only used when printing comparability which is only done
1016    // for client classes
1017    // track_class_init();
1018
1019    Instrument.debug_transform.exdent();
1020    Instrument.debug_transform.log("Instrumentation complete: %s%n", classname);
1021
1022    return gen.getJavaClass().copy();
1023  }
1024
1025  /**
1026   * Instrument the specified method for dynamic comparability.
1027   *
1028   * @param mg MethodGen for the method to be instrumented
1029   */
1030  public void instrument_method(MethodGen mg) {
1031
1032    // Because the tag_frame_local is active for the entire method
1033    // and its creation will change the state of the locals layout,
1034    // we need to insert the code to initialize it now so that the
1035    // stack anaylsis we are about to do is correct for potential
1036    // code replacements we might make later.
1037    InstructionHandle orig_start = mg.getInstructionList().getStart();
1038    add_create_tag_frame(mg);
1039    // Calculate the operand stack value(s) for revised code.
1040    mg.setMaxStack();
1041    // Calculate stack types information
1042    StackTypes stack_types = bcelCalcStackTypes(mg);
1043    if (stack_types == null) {
1044      skip_method(mg);
1045      return;
1046    }
1047
1048    InstructionList il = mg.getInstructionList();
1049    OperandStack stack = null;
1050
1051    // Prior to adding support for Stack Maps, the position field
1052    // of each InstructionHandle was not updated until the modified
1053    // method was written out.  Hence, it could be used as the
1054    // index into stack_types.  To support StackMaps we need to
1055    // update the position field as we modify the code bytes.  So
1056    // we need a mapping from InstructionHandle to orignal offset.
1057    // I beleive we always visit the InstructionHandle nodes of
1058    // the method's InstructionList in order - hence, we will use
1059    // a simple array for now.  If this turns out to not be the
1060    // case we will need to use a hash map.
1061
1062    int[] handle_offsets = new int[il.getLength()];
1063    InstructionHandle ih = orig_start;
1064    int index = 0;
1065    // Loop through each instruction, building up offset map.
1066    while (ih != null) {
1067      handle_offsets[index++] = ih.getPosition();
1068
1069      if (debugInstrument.enabled) {
1070        debugInstrument.log("inst: %s %n", ih);
1071        for (InstructionTargeter it : ih.getTargeters()) {
1072          debugInstrument.log("targeter: %s %n", it);
1073        }
1074      }
1075
1076      ih = ih.getNext();
1077    }
1078
1079    index = 0;
1080    // Loop through each instruction, making substitutions
1081    for (ih = orig_start; ih != null; ) {
1082      debugInstrument.log("instrumenting instruction %s%n", ih);
1083      InstructionList new_il = null;
1084
1085      // Remember the next instruction to process
1086      InstructionHandle next_ih = ih.getNext();
1087
1088      // Get the stack information
1089      stack = stack_types.get(handle_offsets[index++]);
1090
1091      // Get the translation for this instruction (if any)
1092      new_il = xform_inst(mg, ih, stack);
1093
1094      // If this instruction was modified, replace it with the new
1095      // instruction list. If this instruction was the target of any
1096      // jumps or line numbers, replace them with the first
1097      // instruction in the new list.
1098      replaceInstructions(mg, il, ih, new_il);
1099
1100      // If the modified method is now too large, we quit instrumenting the method
1101      // and will rediscover the problem in the main instrumentation loop above
1102      // and deal with it there.
1103      if (ih.getPosition() >= Const.MAX_CODE_SIZE) {
1104        break;
1105      }
1106
1107      ih = next_ih;
1108    }
1109  }
1110
1111  /**
1112   * Adds the method name and containing class name to {@code skip_methods}, the list of
1113   * uninstrumented methods.
1114   *
1115   * @param mg method to add to skipped_methods list
1116   */
1117  void skip_method(MethodGen mg) {
1118    skipped_methods.add(mg.getClassName() + "." + mg.getName());
1119  }
1120
1121  /**
1122   * Returns the list of uninstrumented methods. (Note: instrument_jdk() needs to have been called
1123   * first.)
1124   */
1125  public List<String> get_skipped_methods() {
1126    return new ArrayList<String>(skipped_methods);
1127  }
1128
1129  /**
1130   * Adds a try/catch block around the entire method. If an exception occurs, the tag stack is
1131   * cleaned up and the exception is rethrown.
1132   */
1133  public void build_exception_handler(MethodGen mg) {
1134
1135    if (mg.getName().equals("main")) {
1136      global_catch_il = null;
1137      global_exception_handler = null;
1138      return;
1139    }
1140
1141    InstructionList il = new InstructionList();
1142    il.append(new DUP());
1143    il.append(
1144        ifact.createInvoke(
1145            dcompRuntimeClassName, "exception_exit", Type.VOID, object_arg, Const.INVOKESTATIC));
1146    il.append(new ATHROW());
1147
1148    add_exception_handler(mg, il);
1149  }
1150
1151  /** Adds a try/catch block around the entire method. */
1152  public void add_exception_handler(MethodGen mg, InstructionList catch_il) {
1153
1154    // <init> methods (constructors) turn out to be problematic
1155    // for adding a whole method exception handler.  The start of
1156    // the exception handler should be after the primary object is
1157    // initialized - but this is hard to determine without a full
1158    // analysis of the code.  Hence, we just skip these methods.
1159    if (!mg.isStatic()) {
1160      if (BcelUtil.isConstructor(mg)) {
1161        global_catch_il = null;
1162        global_exception_handler = null;
1163        return;
1164      }
1165    }
1166
1167    InstructionList cur_il = mg.getInstructionList();
1168    InstructionHandle start = cur_il.getStart();
1169    InstructionHandle end = cur_il.getEnd();
1170
1171    // This is just a temporary handler to get the start and end
1172    // address tracked as we make code modifications.
1173    global_catch_il = catch_il;
1174    global_exception_handler = new CodeExceptionGen(start, end, null, throwable);
1175  }
1176
1177  /** Adds a try/catch block around the entire method. */
1178  public void install_exception_handler(MethodGen mg) {
1179
1180    if (global_catch_il == null) {
1181      return;
1182    }
1183
1184    InstructionList cur_il = mg.getInstructionList();
1185    InstructionHandle start = global_exception_handler.getStartPC();
1186    InstructionHandle end = global_exception_handler.getEndPC();
1187    InstructionHandle exc = cur_il.append(global_catch_il);
1188    cur_il.setPositions();
1189    mg.addExceptionHandler(start, end, exc, throwable);
1190    // discard temporary handler
1191    global_catch_il = null;
1192    global_exception_handler = null;
1193
1194    if (!needStackMap) {
1195      return;
1196    }
1197
1198    int exc_offset = exc.getPosition();
1199
1200    debugInstrument.log(
1201        "New ExceptionHandler: %x %x %x %n", start.getPosition(), end.getPosition(), exc_offset);
1202
1203    // This is a trick to get runningOffset set to
1204    // value of last stack map entry.
1205    updateStackMapOffset(exc_offset, 0);
1206    int map_offset = exc_offset - runningOffset - 1;
1207
1208    // Get the argument types for this method
1209    Type[] arg_types = mg.getArgumentTypes();
1210
1211    int arg_index = (mg.isStatic() ? 0 : 1);
1212    StackMapType[] arg_map_types = new StackMapType[arg_types.length + arg_index];
1213    if (!mg.isStatic()) {
1214      arg_map_types[0] =
1215          new StackMapType(
1216              Const.ITEM_Object, pool.addClass(mg.getClassName()), pool.getConstantPool());
1217    }
1218    for (int ii = 0; ii < arg_types.length; ii++) {
1219      arg_map_types[arg_index++] = generateStackMapTypeFromType(arg_types[ii]);
1220    }
1221
1222    StackMapEntry map_entry;
1223    StackMapType stack_map_type =
1224        new StackMapType(
1225            Const.ITEM_Object, pool.addClass(throwable.getClassName()), pool.getConstantPool());
1226    StackMapType[] stack_map_types = {stack_map_type};
1227    map_entry =
1228        new StackMapEntry(
1229            Const.FULL_FRAME, map_offset, arg_map_types, stack_map_types, pool.getConstantPool());
1230
1231    int orig_size = stackMapTable.length;
1232    StackMapEntry[] new_stack_map_table = new StackMapEntry[orig_size + 1];
1233    System.arraycopy(stackMapTable, 0, new_stack_map_table, 0, orig_size);
1234    new_stack_map_table[orig_size] = map_entry;
1235    stackMapTable = new_stack_map_table;
1236  }
1237
1238  /**
1239   * Adds the code to create the tag frame to the beginning of the method. This needs to be before
1240   * the call to DCRuntime.enter (since it passed to that method).
1241   */
1242  public void add_create_tag_frame(MethodGen mg) {
1243
1244    InstructionList nl = create_tag_frame(mg, tag_frame_local);
1245
1246    // We add a temporary NOP at the end of the create_tag_frame
1247    // code that we will replace with runtime initization code
1248    // later.  We do this so that any existing stack map at
1249    // instruction offset 0 is not replaced by the one we are
1250    // about to add for initializing the tag_frame variable.
1251    insertion_placeholder = nl.append(new NOP());
1252
1253    byte[] code = nl.getByteCode();
1254    // -1 because of the NOP we inserted.
1255    int len_code = code.length - 1;
1256
1257    insertAtMethodStart(mg, nl);
1258
1259    if (!needStackMap) {
1260      return;
1261    }
1262
1263    // For Java 7 and beyond the StackMapTable is part of the
1264    // verification process.  We need to create and or update it to
1265    // account for instrumentation code we have inserted as well as
1266    // adjustments for the new 'tag_frame' local.
1267
1268    // Get existing StackMapTable (if present)
1269    if (stackMapTable.length > 0) {
1270      // Each stack map frame specifies (explicity or implicitly) an
1271      // offset_delta that is used to calculate the actual bytecode
1272      // offset at which the frame applies.  This is caluclated by
1273      // by adding offset_delta + 1 to the bytecode offset of the
1274      // previous frame, unless the previous frame is the initial
1275      // frame of the method, in which case the bytecode offset is
1276      // offset_delta. (From the Java Virual Machine Specification,
1277      // Java SE 7 Edition, section 4.7.4)
1278
1279      // Since we are inserting a new stack map frame at the
1280      // beginning of the stack map table, we need to adjust the
1281      // offset_delta of the original first stack map frame due to
1282      // the fact that it will no longer be the first entry.  We
1283      // must subtract (len_code + 1).
1284      // (We don't have to worry about the special case of the
1285      // original first entry having an offset of 0 because of the
1286      // NOP we inserted above.
1287
1288      stackMapTable[0].updateByteCodeOffset(-(len_code + 1));
1289    }
1290
1291    int new_table_length = stackMapTable.length + 1;
1292    StackMapEntry[] new_stack_map_table = new StackMapEntry[new_table_length];
1293
1294    // Insert a new StackMapEntry at the beginning of the table
1295    // that adds the tag_frame variable.
1296    StackMapType tag_frame_type = generateStackMapTypeFromType(object_arr);
1297    StackMapType[] stack_map_type_arr = {tag_frame_type};
1298    new_stack_map_table[0] =
1299        new StackMapEntry(
1300            Const.APPEND_FRAME, len_code, stack_map_type_arr, null, pool.getConstantPool());
1301
1302    // We can just copy the rest of the stack frames over as the FULL_FRAME
1303    // ones were already updated when the tag_frame variable was allocated.
1304    for (int i = 0; i < stackMapTable.length; i++) {
1305      new_stack_map_table[i + 1] = stackMapTable[i];
1306    }
1307    stackMapTable = new_stack_map_table;
1308    // print_stackMapTable ("add_create_tag_frame");
1309  }
1310
1311  /**
1312   * Adds the call to DCRuntime.enter to the beginning of the method.
1313   *
1314   * @param mg method to modify
1315   * @param mi MethodInfo for method
1316   * @param method_info_index index for MethodInfo
1317   */
1318  public void add_enter(MethodGen mg, MethodInfo mi, int method_info_index) {
1319    InstructionList il = mg.getInstructionList();
1320    replaceInstructions(
1321        mg, il, insertion_placeholder, call_enter_exit(mg, method_info_index, "enter", -1));
1322  }
1323
1324  /**
1325   * Creates the local used to store the tag frame and returns it.
1326   *
1327   * @param mg method to modify
1328   * @return LocalVariableGen for the tag_frame local
1329   */
1330  LocalVariableGen create_tag_frame_local(MethodGen mg) {
1331    return create_method_scope_local(mg, "dcomp_tag_frame$5a", object_arr);
1332  }
1333
1334  /**
1335   * Creates code to create the tag frame for this method and store it in tag_frame_local.
1336   *
1337   * @param mg method to modify
1338   * @param tag_frame_local LocalVariableGen for the tag_frame local
1339   * @return InstructionList for tag_frame setup code
1340   */
1341  InstructionList create_tag_frame(MethodGen mg, LocalVariableGen tag_frame_local) {
1342
1343    Type arg_types[] = mg.getArgumentTypes();
1344
1345    // Determine the offset of the first argument in the frame
1346    int offset = 1;
1347    if (mg.isStatic()) {
1348      offset = 0;
1349    }
1350
1351    // allocate an extra slot to save the tag frame depth for debugging
1352    int frame_size = mg.getMaxLocals() + 1;
1353
1354    // unsigned byte max = 255.  minus the character '0' (decimal 48)
1355    // Largest frame size noted so far is 123.
1356    assert frame_size < 207 : frame_size + " " + mg.getClassName() + "." + mg.getName();
1357    String params = "" + (char) (frame_size + '0');
1358    // Character.forDigit (frame_size, Character.MAX_RADIX);
1359    List<Integer> plist = new ArrayList<>();
1360    for (Type argType : arg_types) {
1361      if (argType instanceof BasicType) {
1362        plist.add(offset);
1363      }
1364      offset += argType.getSize();
1365    }
1366    for (int ii = plist.size() - 1; ii >= 0; ii--) {
1367      char tmpChar = (char) (plist.get(ii) + '0');
1368      params += tmpChar;
1369      // Character.forDigit (plist.get(ii), Character.MAX_RADIX);
1370    }
1371
1372    // Create code to create/init the tag frame and store in tag_frame_local
1373    InstructionList il = new InstructionList();
1374    il.append(ifact.createConstant(params));
1375    il.append(
1376        ifact.createInvoke(
1377            dcompRuntimeClassName, "create_tag_frame", object_arr, string_arg, Const.INVOKESTATIC));
1378    il.append(InstructionFactory.createStore(object_arr, tag_frame_local.getIndex()));
1379    debugInstrument.log("Store Tag frame local at index %d%n", tag_frame_local.getIndex());
1380
1381    return il;
1382  }
1383
1384  /**
1385   * Pushes the object, method info index, parameters, and return value on the stack and calls the
1386   * specified Method (normally enter or exit) in DCRuntime. The parameters are passed as an array
1387   * of objects.
1388   *
1389   * @param mg method to modify
1390   * @param method_info_index index for MethodInfo
1391   * @param method_name "enter" or "exit"
1392   * @param line source line number if type is exit
1393   * @return InstructionList for the enter or exit code
1394   */
1395  InstructionList call_enter_exit(
1396      MethodGen mg, int method_info_index, String method_name, int line) {
1397
1398    InstructionList il = new InstructionList();
1399    Type[] arg_types = mg.getArgumentTypes();
1400
1401    // Push the tag frame
1402    il.append(InstructionFactory.createLoad(object_arr, tag_frame_local.getIndex()));
1403
1404    // Push the object.  Null if this is a static method or a constructor
1405    if (mg.isStatic() || (method_name.equals("enter") && BcelUtil.isConstructor(mg))) {
1406      il.append(new ACONST_NULL());
1407    } else { // must be an instance method
1408      il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
1409    }
1410
1411    // Determine the offset of the first parameter
1412    int param_offset = 1;
1413    if (mg.isStatic()) {
1414      param_offset = 0;
1415    }
1416
1417    // Push the MethodInfo index
1418    il.append(ifact.createConstant(method_info_index));
1419
1420    // Create an array of objects with elements for each parameter
1421    il.append(ifact.createConstant(arg_types.length));
1422    il.append(ifact.createNewArray(Type.OBJECT, (short) 1));
1423
1424    // Put each argument into the array
1425    int param_index = param_offset;
1426    for (int ii = 0; ii < arg_types.length; ii++) {
1427      il.append(InstructionFactory.createDup(object_arr.getSize()));
1428      il.append(ifact.createConstant(ii));
1429      Type at = arg_types[ii];
1430      if (at instanceof BasicType) {
1431        il.append(new ACONST_NULL());
1432        // il.append (create_wrapper (c, at, param_index));
1433      } else { // must be reference of some sort
1434        il.append(InstructionFactory.createLoad(Type.OBJECT, param_index));
1435      }
1436      il.append(InstructionFactory.createArrayStore(Type.OBJECT));
1437      param_index += at.getSize();
1438    }
1439
1440    // If this is an exit, push the return value and line number.
1441    // The return value
1442    // is stored in the local "return__$trace2_val"  If the return
1443    // value is a primitive, wrap it in the appropriate run-time wrapper
1444    if (method_name.equals("exit")) {
1445      Type returnType = mg.getReturnType();
1446      if (returnType == Type.VOID) {
1447        il.append(new ACONST_NULL());
1448      } else {
1449        LocalVariableGen return_local = get_return_local(mg, returnType);
1450        if (returnType instanceof BasicType) {
1451          il.append(new ACONST_NULL());
1452          // il.append (create_wrapper (c, returnType, return_local.getIndex()));
1453        } else {
1454          il.append(InstructionFactory.createLoad(Type.OBJECT, return_local.getIndex()));
1455        }
1456      }
1457
1458      // push line number
1459      il.append(ifact.createConstant(line));
1460    }
1461
1462    // Call the specified method
1463    Type[] method_args;
1464    if (method_name.equals("exit")) {
1465      method_args =
1466          new Type[] {object_arr, Type.OBJECT, Type.INT, object_arr, Type.OBJECT, Type.INT};
1467    } else {
1468      method_args = new Type[] {object_arr, Type.OBJECT, Type.INT, object_arr};
1469    }
1470    il.append(
1471        ifact.createInvoke(
1472            dcompRuntimeClassName, method_name, Type.VOID, method_args, Const.INVOKESTATIC));
1473
1474    return il;
1475  }
1476
1477  /**
1478   * Transforms instructions to track comparability. Returns a list of instructions that replaces
1479   * the specified instruction. Returns null if the instruction should not be replaced.
1480   *
1481   * @param mg method being instrumented
1482   * @param ih handle of Instruction to translate
1483   * @param stack current contents of the stack
1484   */
1485  @Nullable InstructionList xform_inst(MethodGen mg, InstructionHandle ih, OperandStack stack) {
1486
1487    Instruction inst = ih.getInstruction();
1488
1489    switch (inst.getOpcode()) {
1490
1491        // Replace the object comparison instructions with a call to
1492        // DCRuntime.object_eq or DCRuntime.object_ne.  Those methods
1493        // return a boolean which is used in a ifeq/ifne instruction
1494      case Const.IF_ACMPEQ:
1495        return object_comparison((BranchInstruction) inst, "object_eq", Const.IFNE);
1496      case Const.IF_ACMPNE:
1497        return object_comparison((BranchInstruction) inst, "object_ne", Const.IFNE);
1498
1499        // These instructions compare the integer on the top of the stack
1500        // to zero.  Nothing is made comparable by this, so we need only
1501        // discard the tag on the top of the stack.
1502      case Const.IFEQ:
1503      case Const.IFNE:
1504      case Const.IFLT:
1505      case Const.IFGE:
1506      case Const.IFGT:
1507      case Const.IFLE:
1508        {
1509          return discard_tag_code(inst, 1);
1510        }
1511
1512        // Instanceof pushes either 0 or 1 on the stack depending on whether
1513        // the object on top of stack is of the specified type.  We push a
1514        // tag for a constant, since nothing is made comparable by this.
1515      case Const.INSTANCEOF:
1516        return build_il(dcr_call("push_const", Type.VOID, Type.NO_ARGS), inst);
1517
1518        // Duplicates the item on the top of stack.  If the value on the
1519        // top of the stack is a primitive, we need to do the same on the
1520        // tag stack.  Otherwise, we need do nothing.
1521      case Const.DUP:
1522        {
1523          return dup_tag(inst, stack);
1524        }
1525
1526        // Duplicates the item on the top of the stack and inserts it 2
1527        // values down in the stack.  If the value at the top of the stack
1528        // is not a primitive, there is nothing to do here.  If the second
1529        // value is not a primitive, then we need only to insert the duped
1530        // value down 1 on the tag stack (which contains only primitives)
1531      case Const.DUP_X1:
1532        {
1533          return dup_x1_tag(inst, stack);
1534        }
1535
1536        // Duplicates either the top 2 category 1 values or a single
1537        // category 2 value and inserts it 2 or 3 values down on the
1538        // stack.
1539      case Const.DUP2_X1:
1540        {
1541          return dup2_x1_tag(inst, stack);
1542        }
1543
1544        // Duplicate either one category 2 value or two category 1 values.
1545      case Const.DUP2:
1546        {
1547          return dup2_tag(inst, stack);
1548        }
1549
1550        // Dup the category 1 value on the top of the stack and insert it either
1551        // two or three values down on the stack.
1552      case Const.DUP_X2:
1553        {
1554          return dup_x2(inst, stack);
1555        }
1556
1557      case Const.DUP2_X2:
1558        {
1559          return dup2_x2(inst, stack);
1560        }
1561
1562        // Pop instructions discard the top of the stack.  We want to discard
1563        // the top of the tag stack iff the item on the top of the stack is a
1564        // primitive.
1565      case Const.POP:
1566        {
1567          return pop_tag(inst, stack);
1568        }
1569
1570        // Pops either the top 2 category 1 values or a single category 2 value
1571        // from the top of the stack.  We must do the same to the tag stack
1572        // if the values are primitives.
1573      case Const.POP2:
1574        {
1575          return pop2_tag(inst, stack);
1576        }
1577
1578        // Swaps the two category 1 types on the top of the stack.  We need
1579        // to swap the top of the tag stack if the two top elements on the
1580        // real stack are primitives.
1581      case Const.SWAP:
1582        {
1583          return swap_tag(inst, stack);
1584        }
1585
1586      case Const.IF_ICMPEQ:
1587      case Const.IF_ICMPGE:
1588      case Const.IF_ICMPGT:
1589      case Const.IF_ICMPLE:
1590      case Const.IF_ICMPLT:
1591      case Const.IF_ICMPNE:
1592        {
1593          return build_il(dcr_call("cmp_op", Type.VOID, Type.NO_ARGS), inst);
1594        }
1595
1596      case Const.GETFIELD:
1597        {
1598          return load_store_field(mg, (GETFIELD) inst);
1599        }
1600
1601      case Const.PUTFIELD:
1602        {
1603          return load_store_field(mg, (PUTFIELD) inst);
1604        }
1605
1606      case Const.GETSTATIC:
1607        {
1608          return load_store_field(mg, ((GETSTATIC) inst));
1609        }
1610
1611      case Const.PUTSTATIC:
1612        {
1613          return load_store_field(mg, ((PUTSTATIC) inst));
1614        }
1615
1616      case Const.DLOAD:
1617      case Const.DLOAD_0:
1618      case Const.DLOAD_1:
1619      case Const.DLOAD_2:
1620      case Const.DLOAD_3:
1621      case Const.FLOAD:
1622      case Const.FLOAD_0:
1623      case Const.FLOAD_1:
1624      case Const.FLOAD_2:
1625      case Const.FLOAD_3:
1626      case Const.ILOAD:
1627      case Const.ILOAD_0:
1628      case Const.ILOAD_1:
1629      case Const.ILOAD_2:
1630      case Const.ILOAD_3:
1631      case Const.LLOAD:
1632      case Const.LLOAD_0:
1633      case Const.LLOAD_1:
1634      case Const.LLOAD_2:
1635      case Const.LLOAD_3:
1636        {
1637          return load_store_local((LoadInstruction) inst, tag_frame_local, "push_local_tag");
1638        }
1639
1640      case Const.DSTORE:
1641      case Const.DSTORE_0:
1642      case Const.DSTORE_1:
1643      case Const.DSTORE_2:
1644      case Const.DSTORE_3:
1645      case Const.FSTORE:
1646      case Const.FSTORE_0:
1647      case Const.FSTORE_1:
1648      case Const.FSTORE_2:
1649      case Const.FSTORE_3:
1650      case Const.ISTORE:
1651      case Const.ISTORE_0:
1652      case Const.ISTORE_1:
1653      case Const.ISTORE_2:
1654      case Const.ISTORE_3:
1655      case Const.LSTORE:
1656      case Const.LSTORE_0:
1657      case Const.LSTORE_1:
1658      case Const.LSTORE_2:
1659      case Const.LSTORE_3:
1660        {
1661          return load_store_local((StoreInstruction) inst, tag_frame_local, "pop_local_tag");
1662        }
1663
1664      case Const.LDC:
1665      case Const.LDC_W:
1666      case Const.LDC2_W:
1667        {
1668          return ldc_tag(inst, stack);
1669        }
1670
1671        // Push the tag for the array onto the tag stack.  This causes
1672        // anything comparable to the length to be comparable to the array
1673        // as an index.
1674      case Const.ARRAYLENGTH:
1675        {
1676          return array_length(inst);
1677        }
1678
1679      case Const.BIPUSH:
1680      case Const.SIPUSH:
1681      case Const.DCONST_0:
1682      case Const.DCONST_1:
1683      case Const.FCONST_0:
1684      case Const.FCONST_1:
1685      case Const.FCONST_2:
1686      case Const.ICONST_0:
1687      case Const.ICONST_1:
1688      case Const.ICONST_2:
1689      case Const.ICONST_3:
1690      case Const.ICONST_4:
1691      case Const.ICONST_5:
1692      case Const.ICONST_M1:
1693      case Const.LCONST_0:
1694      case Const.LCONST_1:
1695        {
1696          return build_il(dcr_call("push_const", Type.VOID, Type.NO_ARGS), inst);
1697        }
1698
1699        // Primitive Binary operators.  Each is augmented with a call to
1700        // DCRuntime.binary_tag_op that merges the tags and updates the tag
1701        // Stack.
1702      case Const.DADD:
1703      case Const.DCMPG:
1704      case Const.DCMPL:
1705      case Const.DDIV:
1706      case Const.DMUL:
1707      case Const.DREM:
1708      case Const.DSUB:
1709      case Const.FADD:
1710      case Const.FCMPG:
1711      case Const.FCMPL:
1712      case Const.FDIV:
1713      case Const.FMUL:
1714      case Const.FREM:
1715      case Const.FSUB:
1716      case Const.IADD:
1717      case Const.IAND:
1718      case Const.IDIV:
1719      case Const.IMUL:
1720      case Const.IOR:
1721      case Const.IREM:
1722      case Const.ISHL:
1723      case Const.ISHR:
1724      case Const.ISUB:
1725      case Const.IUSHR:
1726      case Const.IXOR:
1727      case Const.LADD:
1728      case Const.LAND:
1729      case Const.LCMP:
1730      case Const.LDIV:
1731      case Const.LMUL:
1732      case Const.LOR:
1733      case Const.LREM:
1734      case Const.LSHL:
1735      case Const.LSHR:
1736      case Const.LSUB:
1737      case Const.LUSHR:
1738      case Const.LXOR:
1739        return build_il(dcr_call("binary_tag_op", Type.VOID, Type.NO_ARGS), inst);
1740
1741        // Computed jump based on the int on the top of stack.  Since that int
1742        // is not made comparable to anything, we just discard its tag.  One
1743        // might argue that the key should be made comparable to each value in
1744        // the jump table.  But the tags for those values are not available.
1745        // And since they are all constants, its not clear how interesting it
1746        // would be anyway.
1747      case Const.LOOKUPSWITCH:
1748      case Const.TABLESWITCH:
1749        return discard_tag_code(inst, 1);
1750
1751        // Make the integer argument to ANEWARRAY comparable to the new
1752        // array's index.
1753      case Const.ANEWARRAY:
1754      case Const.NEWARRAY:
1755        {
1756          return new_array(inst);
1757        }
1758
1759        // If the new array has 2 dimensions, make the integer arguments
1760        // comparable to the corresponding indices of the new array.
1761        // For any other number of dimensions, discard the tags for the
1762        // arguments.
1763      case Const.MULTIANEWARRAY:
1764        {
1765          return multi_newarray_dc(inst);
1766        }
1767
1768        // Mark the array and its index as comparable.  Also for primitives,
1769        // push the tag of the array element on the tag stack
1770      case Const.AALOAD:
1771      case Const.BALOAD:
1772      case Const.CALOAD:
1773      case Const.DALOAD:
1774      case Const.FALOAD:
1775      case Const.IALOAD:
1776      case Const.LALOAD:
1777      case Const.SALOAD:
1778        {
1779          return array_load(inst);
1780        }
1781
1782        // Mark the array and its index as comparable.  For primitives, store
1783        // the tag for the value on the top of the stack in the tag storage
1784        // for the array.
1785      case Const.AASTORE:
1786        return array_store(inst, "aastore", Type.OBJECT);
1787      case Const.BASTORE:
1788        // The JVM uses bastore for both byte and boolean.
1789        // We need to differentiate.
1790        Type arr_type = stack.peek(2);
1791        if (arr_type.getSignature().equals("[Z")) {
1792          return array_store(inst, "zastore", Type.BOOLEAN);
1793        } else {
1794          return array_store(inst, "bastore", Type.BYTE);
1795        }
1796      case Const.CASTORE:
1797        return array_store(inst, "castore", Type.CHAR);
1798      case Const.DASTORE:
1799        return array_store(inst, "dastore", Type.DOUBLE);
1800      case Const.FASTORE:
1801        return array_store(inst, "fastore", Type.FLOAT);
1802      case Const.IASTORE:
1803        return array_store(inst, "iastore", Type.INT);
1804      case Const.LASTORE:
1805        return array_store(inst, "lastore", Type.LONG);
1806      case Const.SASTORE:
1807        return array_store(inst, "sastore", Type.SHORT);
1808
1809        // Prefix the return with a call to the correct normal_exit method
1810        // to handle the tag stack
1811      case Const.ARETURN:
1812      case Const.DRETURN:
1813      case Const.FRETURN:
1814      case Const.IRETURN:
1815      case Const.LRETURN:
1816      case Const.RETURN:
1817        {
1818          return return_tag(mg, inst);
1819        }
1820
1821        // Handle subroutine calls.  Calls to instrumented code are modified
1822        // to call the instrumented version (with the DCompMarker argument).
1823        // Calls to uninstrumented code (rare) discard primitive arguments
1824        // from the tag stack and produce an arbitrary return tag.
1825      case Const.INVOKESTATIC:
1826      case Const.INVOKEVIRTUAL:
1827      case Const.INVOKESPECIAL:
1828      case Const.INVOKEINTERFACE:
1829      case Const.INVOKEDYNAMIC:
1830        return handleInvoke((InvokeInstruction) inst);
1831
1832        // Throws an exception.  This clears the operand stack of the current
1833        // frame.  We need to clear the tag stack as well.
1834      case Const.ATHROW:
1835        return build_il(dcr_call("throw_op", Type.VOID, Type.NO_ARGS), inst);
1836
1837        // Opcodes that don't need any modifications.  Here for reference
1838      case Const.ACONST_NULL:
1839      case Const.ALOAD:
1840      case Const.ALOAD_0:
1841      case Const.ALOAD_1:
1842      case Const.ALOAD_2:
1843      case Const.ALOAD_3:
1844      case Const.ASTORE:
1845      case Const.ASTORE_0:
1846      case Const.ASTORE_1:
1847      case Const.ASTORE_2:
1848      case Const.ASTORE_3:
1849      case Const.CHECKCAST:
1850      case Const.D2F: // double to float
1851      case Const.D2I: // double to integer
1852      case Const.D2L: // double to long
1853      case Const.DNEG: // Negate double on top of stack
1854      case Const.F2D: // float to double
1855      case Const.F2I: // float to integer
1856      case Const.F2L: // float to long
1857      case Const.FNEG: // Negate float on top of stack
1858      case Const.GOTO:
1859      case Const.GOTO_W:
1860      case Const.I2B: // integer to byte
1861      case Const.I2C: // integer to char
1862      case Const.I2D: // integer to double
1863      case Const.I2F: // integer to float
1864      case Const.I2L: // integer to long
1865      case Const.I2S: // integer to short
1866      case Const.IFNONNULL:
1867      case Const.IFNULL:
1868      case Const.IINC: // increment local variable by a constant
1869      case Const.INEG: // negate integer on top of stack
1870      case Const.JSR: // pushes return address on the stack, but that
1871        // is thought of as an object, so we don't need
1872        // a tag for it.
1873      case Const.JSR_W:
1874      case Const.L2D: // long to double
1875      case Const.L2F: // long to float
1876      case Const.L2I: // long to int
1877      case Const.LNEG: // negate long on top of stack
1878      case Const.MONITORENTER:
1879      case Const.MONITOREXIT:
1880      case Const.NEW:
1881      case Const.NOP:
1882      case Const.RET: // this is the internal JSR return
1883        return null;
1884
1885        // Make sure we didn't miss anything
1886      default:
1887        throw new Error("instruction " + inst + " unsupported");
1888    }
1889  }
1890
1891  /**
1892   * Adds a call to DCruntime.exit() at each return from the method. This call calculates
1893   * comparability on the daikon variables. It is only necessary if we are tracking comparability
1894   * for the variables of this method.
1895   *
1896   * @param mg method to modify
1897   * @param mi MethodInfo for method
1898   * @param method_info_index index for MethodInfo
1899   */
1900  void add_exit(MethodGen mg, MethodInfo mi, int method_info_index) {
1901
1902    // Iterator over all of the exit line numbers for this method, in order.
1903    // We will read one element from it each time that we encounter a
1904    // return instruction.
1905    Iterator<Integer> exit_iter = mi.exit_locations.iterator();
1906
1907    // Loop through each instruction, looking for return instructions.
1908    InstructionList il = mg.getInstructionList();
1909    for (InstructionHandle ih = il.getStart(); ih != null; ) {
1910
1911      // Remember the next instruction to process
1912      InstructionHandle next_ih = ih.getNext();
1913
1914      // If this is a return instruction, Call DCRuntime.exit to calculate
1915      // comparability on Daikon variables
1916      Instruction inst = ih.getInstruction();
1917      if (inst instanceof ReturnInstruction) {
1918        Type type = mg.getReturnType();
1919        InstructionList new_il = new InstructionList();
1920        if (type != Type.VOID) {
1921          LocalVariableGen return_loc = get_return_local(mg, type);
1922          new_il.append(InstructionFactory.createDup(type.getSize()));
1923          new_il.append(InstructionFactory.createStore(type, return_loc.getIndex()));
1924        }
1925        new_il.append(call_enter_exit(mg, method_info_index, "exit", exit_iter.next()));
1926        new_il.append(inst);
1927        replaceInstructions(mg, il, ih, new_il);
1928      }
1929
1930      ih = next_ih;
1931    }
1932  }
1933
1934  /**
1935   * Return the interface class containing the implementation of the given method. The interfaces of
1936   * {@code startClass} are recursively searched.
1937   *
1938   * @param startClass the JavaClass whose interfaces are to be searched
1939   * @param methodName the target method to search for
1940   * @param argTypes the target method's argument types
1941   * @return the name of the interface class containing target method, or null if not found
1942   */
1943  private @Nullable @ClassGetName String getDefiningInterface(
1944      JavaClass startClass, String methodName, Type[] argTypes) {
1945
1946    if (debugGetDefiningInterface) {
1947      System.out.println("searching interfaces of: " + startClass.getClassName());
1948    }
1949    for (@ClassGetName String interfaceName : startClass.getInterfaceNames()) {
1950      if (debugGetDefiningInterface) {
1951        System.out.println("interface: " + interfaceName);
1952      }
1953      JavaClass ji;
1954      try {
1955        ji = getJavaClass(interfaceName);
1956      } catch (Throwable e) {
1957        throw new Error(String.format("Unable to load class: %s", interfaceName), e);
1958      }
1959      for (Method jm : ji.getMethods()) {
1960        if (debugGetDefiningInterface) {
1961          System.out.println("  " + jm.getName() + Arrays.toString(jm.getArgumentTypes()));
1962        }
1963        if (jm.getName().equals(methodName) && Arrays.equals(jm.getArgumentTypes(), argTypes)) {
1964          // We have a match.
1965          return interfaceName;
1966        }
1967      }
1968      // no match found; does this interface extend other interfaces?
1969      @ClassGetName String foundAbove = getDefiningInterface(ji, methodName, argTypes);
1970      if (foundAbove != null) {
1971        // We have a match.
1972        return foundAbove;
1973      }
1974    }
1975    // nothing found
1976    return null;
1977  }
1978
1979  /**
1980   * Process an Invoke instruction. There are three cases:
1981   *
1982   * <ul>
1983   *   <li>convert calls to Object.equals to calls to dcomp_equals or dcomp_super_equals
1984   *   <li>convert calls to Object.clone to calls to dcomp_clone or dcomp_super_clone
1985   *   <li>otherwise, determine whether the target of the invoke is instrumented or not (this is the
1986   *       {@code callee_instrumented} variable)
1987   *       <ul>
1988   *         <li>If the target method is instrumented, add a DCompMarker argument to the end of the
1989   *             argument list.
1990   *         <li>If the target method is not instrumented, we must account for the fact that the
1991   *             instrumentation code generated up to this point has assumed that the target method
1992   *             is instrumented. Hence, generate code to discard a primitive tag from the
1993   *             DCRuntime's per-thread comparability data stack for each primitive argument. If the
1994   *             return type of the target method is a primitive, add code to push a tag onto the
1995   *             runtime comparability data stack to represent the primitive return value.
1996   *       </ul>
1997   * </ul>
1998   *
1999   * @param invoke a method invocation bytecode instruction
2000   * @return instructions to replace the given instruction
2001   */
2002  private InstructionList handleInvoke(InvokeInstruction invoke) {
2003
2004    // Get information about the call
2005    String methodName = invoke.getMethodName(pool);
2006    // getClassName does not work properly if invoke is INVOKEDYNAMIC.
2007    // We will deal with this later.
2008    @ClassGetName String classname = invoke.getClassName(pool);
2009    Type returnType = invoke.getReturnType(pool);
2010    Type[] argTypes = invoke.getArgumentTypes(pool);
2011
2012    if (is_object_equals(methodName, returnType, argTypes)) {
2013
2014      // Replace calls to Object's equals method with calls to our
2015      // replacement, a static method in DCRuntime.
2016
2017      Type[] new_arg_types = new Type[] {javalangObject, javalangObject};
2018
2019      InstructionList il = new InstructionList();
2020      il.append(
2021          ifact.createInvoke(
2022              dcompRuntimeClassName,
2023              (invoke.getOpcode() == Const.INVOKESPECIAL) ? "dcomp_super_equals" : "dcomp_equals",
2024              returnType,
2025              new_arg_types,
2026              Const.INVOKESTATIC));
2027      return il;
2028    }
2029
2030    if (is_object_clone(methodName, returnType, argTypes)) {
2031
2032      // Replace calls to Object's clone method with calls to our
2033      // replacement, a static method in DCRuntime.
2034
2035      InstructionList il = instrument_clone_call(invoke);
2036      return il;
2037    }
2038
2039    boolean callee_instrumented = isTargetInstrumented(invoke, classname, methodName, argTypes);
2040
2041    if (debugHandleInvoke) {
2042      System.out.printf("handleInvoke(%s)%n", invoke);
2043      System.out.printf("  invoke host: %s%n", gen.getClassName() + "." + mgen.getName());
2044      System.out.printf("  invoke targ: %s%n", classname + "." + methodName);
2045      System.out.printf("  callee_instrumented: %s%n", callee_instrumented);
2046    }
2047
2048    if (callee_instrumented) {
2049
2050      InstructionList il = new InstructionList();
2051      // Add the DCompMarker argument so that it calls the instrumented version.
2052      il.append(new ACONST_NULL());
2053      Type[] new_arg_types = BcelUtil.postpendToArray(argTypes, dcomp_marker);
2054      Constant methodref = pool.getConstant(invoke.getIndex());
2055      il.append(
2056          ifact.createInvoke(
2057              classname,
2058              methodName,
2059              returnType,
2060              new_arg_types,
2061              invoke.getOpcode(),
2062              methodref instanceof ConstantInterfaceMethodref));
2063      return il;
2064
2065    } else { // not instrumented, discard the tags before making the call
2066
2067      InstructionList il = new InstructionList();
2068      // JUnit test classes are a bit strange.  They are marked as not being callee_instrumented
2069      // because they do not have the dcomp_marker added to the argument list, but
2070      // they actually contain instrumentation code.  So we do not want to discard
2071      // the primitive tags prior to the call.
2072      if (!junitTestClasses.contains(classname)) {
2073        il.append(discard_primitive_tags(argTypes));
2074      }
2075
2076      // Add a tag for the return type if it is primitive.
2077      if ((returnType instanceof BasicType) && (returnType != Type.VOID)) {
2078        if (debugHandleInvoke) {
2079          System.out.printf("push tag for return  type of %s%n", invoke.getReturnType(pool));
2080        }
2081        il.append(dcr_call("push_const", Type.VOID, Type.NO_ARGS));
2082      }
2083      il.append(invoke);
2084      return il;
2085    }
2086  }
2087
2088  /**
2089   * Returns instructions that will discard any primitive tags corresponding to the specified
2090   * arguments. Returns an empty instruction list if there are no primitive arguments to discard.
2091   *
2092   * @param argTypes argument types of target method
2093   * @return an instruction list that discards primitive tags from DCRuntime's per-thread
2094   *     comparability data stack
2095   */
2096  private InstructionList discard_primitive_tags(Type[] argTypes) {
2097
2098    InstructionList il = new InstructionList();
2099    int primitive_cnt = 0;
2100    for (Type argType : argTypes) {
2101      if (argType instanceof BasicType) {
2102        primitive_cnt++;
2103      }
2104    }
2105    if (primitive_cnt > 0) {
2106      il.append(discard_tag_code(new NOP(), primitive_cnt));
2107    }
2108    return il;
2109  }
2110
2111  /**
2112   * Returns true if the invoke target is instrumented.
2113   *
2114   * @param invoke instruction whose target is to be checked
2115   * @param classname target class of the invoke
2116   * @param methodName target method of the invoke
2117   * @param argTypes argument types of target method
2118   * @return true if the target is instrumented
2119   */
2120  private boolean isTargetInstrumented(
2121      InvokeInstruction invoke,
2122      @ClassGetName String classname,
2123      String methodName,
2124      Type[] argTypes) {
2125    boolean targetInstrumented;
2126
2127    if (invoke instanceof INVOKEDYNAMIC) {
2128      // We don't instrument lambda methods.
2129      // BUG: BCEL doesn't know how to get classname from an INVOKEDYNAMIC instruction.
2130      if (debugHandleInvoke) {
2131        System.out.printf("invokedynamic NOT the classname: %s%n", classname);
2132      }
2133      targetInstrumented = false;
2134    } else if (is_object_method(methodName, invoke.getArgumentTypes(pool))) {
2135      targetInstrumented = false;
2136    } else {
2137      targetInstrumented = isClassnameInstrumented(classname, methodName);
2138
2139      if (debugHandleInvoke) {
2140        System.out.printf("invoke host: %s%n", gen.getClassName() + "." + mgen.getName());
2141        System.out.printf("invoke targ: %s%n", classname + "." + methodName);
2142      }
2143
2144      if (Premain.problem_methods.contains(classname + "." + methodName)) {
2145        debugInstrument.log(
2146            "Don't call instrumented version of problem method %s.%n",
2147            classname + "." + methodName);
2148        targetInstrumented = false;
2149      }
2150
2151      // targetInstrumented (the return value of this method) has been set.
2152      // Now, adjust it for some special cases.
2153      // Every adjustment is from `true` to `false`.
2154
2155      // There are two special cases we need to detect:
2156      //   calls to annotations
2157      //   calls to functional interfaces
2158      //
2159      // Annotation classes are never instrumented so we must set
2160      // the targetInstrumented flag to false.
2161      //
2162      // Functional interfaces are a bit more complicated. These are primarily (only?)
2163      // used by Lambda functions.  Lambda methods are generated dynamically at
2164      // run time via the InvokeDynamic instruction.  They are not seen by our
2165      // ClassFileTransformer so are never instrumented.  Thus we must set the
2166      // targetInstrumented flag to false when we see a call to a Lambda method.
2167      // The heuristic we use is to assume that any InvokeInterface or InvokeVirtual
2168      // call to a functional interface is a call to a Lambda method.
2169      //
2170      // The Java compiler detects functional interfaces automatically, but the
2171      // user can declare their intent with the @FunctionInterface annotation.
2172      // The Java runtime is annotated in this manner.  Hence, we look for this
2173      // annotation to detect a call to a functional interface.  In practice, we
2174      // could detect functional interfaces in a manner similar to the Java
2175      // compiler, but for now we will go with this simpler method.
2176      //
2177      // Note that to simplify our code we set the access flags for a functional
2178      // interface to ANNOTATION in our accessFlags map.
2179      //
2180      if (targetInstrumented == true
2181          && (invoke instanceof INVOKEINTERFACE || invoke instanceof INVOKEVIRTUAL)) {
2182        Integer access = getAccessFlags(classname);
2183
2184        if ((access.intValue() & Const.ACC_ANNOTATION) != 0) {
2185          targetInstrumented = false;
2186        }
2187
2188        // UNDONE: New code added above should handle the case below.  Need to find a test
2189        // case and verify this code is no longer needed.
2190        // This is a bit of a hack.  An invokeinterface instruction with a
2191        // a target of "java.util.stream.<something>" might be calling a
2192        // Lambda method in which case we don't want to add the dcomp_marker.
2193        // Might lose something in 'normal' cases, but no easy way to detect.
2194        if (classname.startsWith("java.util.stream")) {
2195          targetInstrumented = false;
2196        }
2197
2198        // In a similar fashion, when the Java runtime is processing annotations, there might
2199        // be an invoke (via reflection) of a member of the java.lang.annotation package; this
2200        // too should not have the dcomp_marker added.
2201        if (classname.startsWith("java.lang.annotation")) {
2202          targetInstrumented = false;
2203        }
2204      }
2205
2206      // If we are not using the instrumented JDK, then we need to track down the
2207      // actual target of an INVOKEVIRTUAL to see if it has been instrumented or not.
2208      if (targetInstrumented == true && invoke instanceof INVOKEVIRTUAL) {
2209        if (!jdk_instrumented && !mgen.getName().equals("equals_dcomp_instrumented")) {
2210
2211          if (debugHandleInvoke) {
2212            System.out.println("method: " + methodName);
2213            System.out.println("argTypes: " + Arrays.toString(argTypes));
2214            System.out.printf("invoke host: %s%n", gen.getClassName() + "." + mgen.getName());
2215          }
2216
2217          @ClassGetName String targetClassname = classname;
2218          // Search this class for the target method. If not found, set targetClassname to
2219          // its superclass and try again.
2220          mainloop:
2221          while (true) {
2222            // Check that the class exists
2223            JavaClass targetClass;
2224            try {
2225              targetClass = getJavaClass(targetClassname);
2226            } catch (Throwable e) {
2227              targetClass = null;
2228            }
2229            if (targetClass == null) {
2230              // We cannot locate or read the .class file, better assume not instrumented.
2231              if (debugHandleInvoke) {
2232                System.out.printf("Unable to locate class: %s%n%n", targetClassname);
2233              }
2234              targetInstrumented = false;
2235              break;
2236            }
2237            if (debugHandleInvoke) {
2238              System.out.println("target class: " + targetClassname);
2239            }
2240
2241            for (Method m : targetClass.getMethods()) {
2242              if (debugHandleInvoke) {
2243                System.out.println("  " + m.getName() + Arrays.toString(m.getArgumentTypes()));
2244              }
2245              if (m.getName().equals(methodName) && Arrays.equals(m.getArgumentTypes(), argTypes)) {
2246                // We have a match.
2247                if (debugHandleInvoke) {
2248                  System.out.printf("we have a match%n%n");
2249                }
2250                if (BcelUtil.inJdk(targetClassname)) {
2251                  targetInstrumented = false;
2252                }
2253                break mainloop;
2254              }
2255            }
2256
2257            {
2258              // no methods match - search this class's interfaces
2259              @ClassGetName String found;
2260              try {
2261                found = getDefiningInterface(targetClass, methodName, argTypes);
2262              } catch (Throwable e) {
2263                // We cannot locate or read the .class file, better assume it is not instrumented.
2264                targetInstrumented = false;
2265                break;
2266              }
2267              if (found != null) {
2268                // We have a match.
2269                if (debugHandleInvoke) {
2270                  System.out.printf("we have a match%n%n");
2271                }
2272                if (BcelUtil.inJdk(found)) {
2273                  targetInstrumented = false;
2274                }
2275                break;
2276              }
2277            }
2278
2279            // Method not found; perhaps inherited from superclass.
2280            // Cannot use "targetClass = targetClass.getSuperClass()" because the superclass might
2281            // not have been loaded into BCEL yet.
2282            if (targetClass.getSuperclassNameIndex() == 0) {
2283              // The target class is Object; the search completed without finding a matching method.
2284              if (debugHandleInvoke) {
2285                System.out.printf("Unable to locate method: %s%n%n", methodName);
2286              }
2287              targetInstrumented = false;
2288              break;
2289            }
2290            // Recurse looking in the superclass.
2291            targetClassname = targetClass.getSuperclassName();
2292          }
2293        }
2294      }
2295    }
2296
2297    if (invoke instanceof INVOKESPECIAL) {
2298      if (classname.equals(gen.getSuperclassName()) && methodName.equals("<init>")) {
2299        this.constructor_is_initialized = true;
2300      }
2301    }
2302
2303    return targetInstrumented;
2304  }
2305
2306  /**
2307   * Returns the access flags for the given class.
2308   *
2309   * @param classname the class whose access flags to return
2310   * @return the access flags for the given class
2311   */
2312  private Integer getAccessFlags(String classname) {
2313    Integer access = accessFlags.get(classname);
2314    if (access == null) {
2315      // We have not seen this class before. Check to see if the target class is
2316      // an Annotation or a FunctionalInterface.
2317      JavaClass c = getJavaClass(classname);
2318      if (c != null) {
2319        access = c.getAccessFlags();
2320
2321        // Now check for FunctionalInterface
2322        for (final AnnotationEntry item : c.getAnnotationEntries()) {
2323          if (item.getAnnotationType().endsWith("FunctionalInterface;")) {
2324            access = Integer_ACC_ANNOTATION;
2325            if (debugHandleInvoke) {
2326              System.out.println(item.getAnnotationType());
2327            }
2328            break;
2329          }
2330        }
2331      } else {
2332        // We cannot locate or read the .class file, better pretend it is an Annotation.
2333        if (debugHandleInvoke) {
2334          System.out.printf("Unable to locate class: %s%n", classname);
2335        }
2336        access = Integer_ACC_ANNOTATION;
2337      }
2338      accessFlags.put(classname, access);
2339    }
2340    return access;
2341  }
2342
2343  /**
2344   * Returns true if the specified classname is instrumented.
2345   *
2346   * @param classname class to be checked
2347   * @param methodName method to be checked (currently unused)
2348   * @return true if classname is instrumented
2349   */
2350  private boolean isClassnameInstrumented(@ClassGetName String classname, String methodName) {
2351
2352    if (debugHandleInvoke) {
2353      System.out.printf("Checking callee instrumented on %s%n", classname);
2354    }
2355
2356    // Our copy of daikon.plumelib is not instrumented.  It would be odd, though,
2357    // to see calls to this.
2358    if (classname.startsWith("daikon.plumelib")) {
2359      return false;
2360    }
2361
2362    // Special-case JUnit test classes.
2363    if (junitTestClasses.contains(classname)) {
2364      return false;
2365    }
2366
2367    if (daikon.dcomp.Instrument.is_transformer(classname.replace('.', '/'))) {
2368      return false;
2369    }
2370
2371    // Special case the execution trace tool.
2372    if (classname.startsWith("minst.Minst")) {
2373      return false;
2374    }
2375
2376    // We should probably change the interface to include method name
2377    // and use "classname.methodname" as arg to pattern matcher.
2378    // If any of the omit patterns match, use the uninstrumented version of the method
2379    for (Pattern p : DynComp.ppt_omit_pattern) {
2380      if (p.matcher(classname).find()) {
2381        if (debugHandleInvoke) {
2382          System.out.printf("callee instrumented = false: %s.%s%n", classname, methodName);
2383        }
2384        return false;
2385      }
2386    }
2387
2388    // If its not a JDK class, presume its instrumented.
2389    if (!BcelUtil.inJdk(classname)) {
2390      return true;
2391    }
2392
2393    int i = classname.lastIndexOf('.');
2394    if (i > 0) {
2395      if (Premain.problem_packages.contains(classname.substring(0, i))) {
2396        debugInstrument.log(
2397            "Don't call instrumented member of problem package %s%n", classname.substring(0, i));
2398        return false;
2399      }
2400    }
2401
2402    if (Premain.problem_classes.contains(classname)) {
2403      debugInstrument.log("Don't call instrumented member of problem class %s%n", classname);
2404      return false;
2405    }
2406
2407    // We have decided not to use the instrumented version of Random as
2408    // the method generates values based on an initial seed value.
2409    // (Typical of random() algorithms.) This has the undesirable side
2410    // effect of putting all the generated values in the same comparison
2411    // set when they should be distinct.
2412    // NOTE: If we find other classes that should not use the instrumented
2413    // versions, we should consider making this a searchable list.
2414    if (classname.equals("java.util.Random")) {
2415      return false;
2416    }
2417
2418    // If using the instrumented JDK, then everthing but object is instrumented
2419    if (jdk_instrumented && !classname.equals("java.lang.Object")) {
2420      return true;
2421    }
2422
2423    return false;
2424  }
2425
2426  /**
2427   * Given a classname return it's superclass name. Note that BCEL reports that the superclass of
2428   * 'java.lang.Object' is 'java.lang.Object' rather than saying there is no superclass.
2429   *
2430   * @param classname the fully qualified name of the class in binary form. E.g., "java.util.List"
2431   * @return superclass name of classname or null if there is an error
2432   */
2433  private @ClassGetName String getSuperclassName(String classname) {
2434    JavaClass jc = getJavaClass(classname);
2435    if (jc != null) {
2436      return jc.getSuperclassName();
2437    } else {
2438      return null;
2439    }
2440  }
2441
2442  /** Cache for {@link #getJavaClass} method. */
2443  private static Map<String, JavaClass> javaClasses = new ConcurrentHashMap<String, JavaClass>();
2444
2445  /**
2446   * There are times when it is useful to inspect a class file other than the one we are currently
2447   * instrumenting. Note we cannot use classForName to do this as it might trigger a recursive call
2448   * to Instrument which would not work at this point.
2449   *
2450   * <p>Given a class name, we treat it as a system resource and try to open it as an input stream
2451   * that we can pass to BCEL to read and convert to a JavaClass object.
2452   *
2453   * @param classname the fully qualified name of the class in binary form, e.g., "java.util.List"
2454   * @return the JavaClass of the corresponding classname or null
2455   */
2456  private @Nullable JavaClass getJavaClass(String classname) {
2457    JavaClass cached = javaClasses.get(classname);
2458    if (cached != null) {
2459      return cached;
2460    }
2461
2462    URL class_url = ClassLoader.getSystemResource(classname.replace('.', '/') + ".class");
2463    if (class_url != null) {
2464      try (InputStream inputStream = class_url.openStream()) {
2465        if (inputStream != null) {
2466          // Parse the bytes of the classfile, die on any errors
2467          ClassParser parser = new ClassParser(inputStream, classname + "<internal>");
2468          JavaClass result = parser.parse();
2469          javaClasses.put(classname, result);
2470          return result;
2471        }
2472      } catch (Throwable t) {
2473        throw new Error("Unexpected error reading " + class_url, t);
2474      }
2475    }
2476    // Do not cache a null result, because a subsequent invocation might return non-null.
2477    return null;
2478  }
2479
2480  /**
2481   * Returns whether or not the method is Object.equals().
2482   *
2483   * @param methodName method to check
2484   * @param returnType return type of method
2485   * @param args array of argument types to method
2486   * @return true if method is Object.equals()
2487   */
2488  @Pure
2489  boolean is_object_equals(String methodName, Type returnType, Type[] args) {
2490    return (methodName.equals("equals")
2491        && returnType == Type.BOOLEAN
2492        && args.length == 1
2493        && args[0].equals(javalangObject));
2494  }
2495
2496  /**
2497   * Returns true if the specified method is Object.clone().
2498   *
2499   * @param methodName method to check
2500   * @param returnType return type of method
2501   * @param args array of argument types to method
2502   * @return true if method is Object.clone()
2503   */
2504  @Pure
2505  boolean is_object_clone(String methodName, Type returnType, Type[] args) {
2506    return methodName.equals("clone") && returnType.equals(javalangObject) && (args.length == 0);
2507  }
2508
2509  /**
2510   * Instrument calls to the Object method clone. An instrumented version is called if it exists,
2511   * the non-instrumented version if it does not.
2512   *
2513   * @param invoke invoke instruction to inspect and replace
2514   * @return InstructionList to call the correct version of clone or toString
2515   */
2516  InstructionList instrument_clone_call(InvokeInstruction invoke) {
2517
2518    InstructionList il = new InstructionList();
2519    Type returnType = invoke.getReturnType(pool);
2520    String classname = invoke.getClassName(pool);
2521    ReferenceType ref_type = invoke.getReferenceType(pool);
2522    if (ref_type instanceof ArrayType) {
2523      // <array>.clone() is never instrumented, return original invoke.
2524      il.append(invoke);
2525      return il;
2526    }
2527
2528    // push the target class
2529    il.append(new LDC(pool.addClass(classname)));
2530
2531    // if this is a super call
2532    if (invoke.getOpcode() == Const.INVOKESPECIAL) {
2533
2534      // Runtime will discover if the object's superclass has an instrumented clone method.
2535      // If so, call it; otherwise call the uninstrumented version.
2536      il.append(dcr_call("dcomp_super_clone", returnType, new Type[] {Type.OBJECT, javalangClass}));
2537
2538    } else { // a regular (non-super) clone() call
2539
2540      // Runtime will discover if the object has an instrumented clone method.
2541      // If so, call it; otherwise call the uninstrumented version.
2542      il.append(dcr_call("dcomp_clone", returnType, new Type[] {Type.OBJECT, javalangClass}));
2543    }
2544
2545    return il;
2546  }
2547
2548  /**
2549   * Create the instructions that replace the object eq or ne branch instruction. They are replaced
2550   * by a call to the specified compare_method (which returns a boolean) followed by the specified
2551   * boolean ifeq or ifne instruction.
2552   */
2553  InstructionList object_comparison(
2554      BranchInstruction branch, String compare_method, short boolean_if) {
2555
2556    InstructionList il = new InstructionList();
2557    il.append(
2558        ifact.createInvoke(
2559            dcompRuntimeClassName, compare_method, Type.BOOLEAN, two_objects, Const.INVOKESTATIC));
2560    assert branch.getTarget() != null;
2561    il.append(InstructionFactory.createBranchInstruction(boolean_if, branch.getTarget()));
2562    return il;
2563  }
2564
2565  /**
2566   * Handles load and store field instructions. The instructions must be augmented to either push
2567   * (load) or pop (store) the tag on the tag stack. This is accomplished by calling the tag get/set
2568   * method for this field.
2569   */
2570  InstructionList load_store_field(MethodGen mg, FieldInstruction f) {
2571
2572    Type field_type = f.getFieldType(pool);
2573    if (field_type instanceof ReferenceType) {
2574      return null;
2575    }
2576    ObjectType obj_type = (ObjectType) f.getReferenceType(pool);
2577    InstructionList il = new InstructionList();
2578    String classname = obj_type.getClassName();
2579
2580    // If this class doesn't support tag fields, don't load/store them
2581    if (!tag_fields_ok(mg, classname)) {
2582      if ((f instanceof GETFIELD) || (f instanceof GETSTATIC)) {
2583        il.append(dcr_call("push_const", Type.VOID, Type.NO_ARGS));
2584      } else {
2585        il.append(ifact.createConstant(1));
2586        il.append(dcr_call("discard_tag", Type.VOID, integer_arg));
2587      }
2588
2589      // Perform the normal field command
2590      il.append(f);
2591      return il;
2592    }
2593
2594    if (f instanceof GETSTATIC) {
2595      il.append(
2596          ifact.createInvoke(
2597              classname,
2598              tag_method_name(GET_TAG, classname, f.getFieldName(pool)),
2599              Type.VOID,
2600              Type.NO_ARGS,
2601              Const.INVOKESTATIC));
2602    } else if (f instanceof PUTSTATIC) {
2603      il.append(
2604          ifact.createInvoke(
2605              classname,
2606              tag_method_name(SET_TAG, classname, f.getFieldName(pool)),
2607              Type.VOID,
2608              Type.NO_ARGS,
2609              Const.INVOKESTATIC));
2610    } else if (f instanceof GETFIELD) {
2611      il.append(InstructionFactory.createDup(obj_type.getSize()));
2612      il.append(
2613          ifact.createInvoke(
2614              classname,
2615              tag_method_name(GET_TAG, classname, f.getFieldName(pool)),
2616              Type.VOID,
2617              Type.NO_ARGS,
2618              Const.INVOKEVIRTUAL));
2619    } else { // must be put field
2620      if (field_type.getSize() == 2) {
2621        LocalVariableGen lv = get_tmp2_local(mg, field_type);
2622        il.append(InstructionFactory.createStore(field_type, lv.getIndex()));
2623        il.append(InstructionFactory.createDup(obj_type.getSize()));
2624        il.append(
2625            ifact.createInvoke(
2626                classname,
2627                tag_method_name(SET_TAG, classname, f.getFieldName(pool)),
2628                Type.VOID,
2629                Type.NO_ARGS,
2630                Const.INVOKEVIRTUAL));
2631        il.append(InstructionFactory.createLoad(field_type, lv.getIndex()));
2632      } else {
2633        il.append(new SWAP());
2634        il.append(InstructionFactory.createDup(obj_type.getSize()));
2635        il.append(
2636            ifact.createInvoke(
2637                classname,
2638                tag_method_name(SET_TAG, classname, f.getFieldName(pool)),
2639                Type.VOID,
2640                Type.NO_ARGS,
2641                Const.INVOKEVIRTUAL));
2642        il.append(new SWAP());
2643      }
2644    }
2645
2646    // Perform the normal field command
2647    il.append(f);
2648
2649    return il;
2650  }
2651
2652  /**
2653   * Handles load and store local instructions. The instructions must be augmented to either push
2654   * (load) or pop (store) the tag on the tag stack. This is accomplished by calling the specified
2655   * method in DCRuntime and passing that method the tag frame and the offset of local/parameter.
2656   */
2657  InstructionList load_store_local(
2658      LocalVariableInstruction lvi, LocalVariableGen tag_frame_local, String method) {
2659
2660    // Don't need tags for objects
2661    assert !(lvi instanceof ALOAD) && !(lvi instanceof ASTORE) : "lvi " + lvi;
2662
2663    InstructionList il = new InstructionList();
2664
2665    // Push the tag frame and the index of this local
2666    il.append(InstructionFactory.createLoad(object_arr, tag_frame_local.getIndex()));
2667    debugInstrument.log("CreateLoad %s %d%n", object_arr, tag_frame_local.getIndex());
2668    il.append(ifact.createConstant(lvi.getIndex()));
2669
2670    // Call the runtime method to handle loading/storing the local/parameter
2671    il.append(
2672        ifact.createInvoke(
2673            dcompRuntimeClassName,
2674            method,
2675            Type.VOID,
2676            new Type[] {object_arr, Type.INT},
2677            Const.INVOKESTATIC));
2678    il.append(lvi);
2679    return il;
2680  }
2681
2682  /** Returns the number of the specified field in the primitive fields of obj_type. */
2683  int get_field_num(String name, ObjectType obj_type) {
2684
2685    // If this is the current class, get the information directly
2686    if (obj_type.getClassName().equals(orig_class.getClassName())) {
2687      int fcnt = 0;
2688      for (Field f : orig_class.getFields()) {
2689        if (f.getName().equals(name)) {
2690          return fcnt;
2691        }
2692        if (f.getType() instanceof BasicType) {
2693          fcnt++;
2694        }
2695      }
2696      throw new Error("Can't find " + name + " in " + obj_type);
2697    }
2698
2699    // Look up the class using this classes class loader.  This may
2700    // not be the best way to accomplish this.
2701    Class<?> obj_class;
2702    try {
2703      obj_class = Class.forName(obj_type.getClassName(), false, loader);
2704    } catch (Exception e) {
2705      throw new Error("can't find class " + obj_type.getClassName(), e);
2706    }
2707
2708    // Loop through all of the fields, counting the number of primitive fields
2709    int fcnt = 0;
2710    for (java.lang.reflect.Field f : obj_class.getDeclaredFields()) {
2711      if (f.getName().equals(name)) {
2712        return fcnt;
2713      }
2714      if (f.getType().isPrimitive()) {
2715        fcnt++;
2716      }
2717    }
2718    throw new Error("Can't find " + name + " in " + obj_class);
2719  }
2720
2721  /**
2722   * Gets the local variable used to store a category2 temporary. This is used in the PUTFIELD code
2723   * to temporarily store the value being placed in the field.
2724   */
2725  LocalVariableGen get_tmp2_local(MethodGen mg, Type typ) {
2726
2727    String name = "dcomp_$tmp_" + typ;
2728    // System.out.printf("local var name = %s%n", name);
2729
2730    // See if the local has already been created
2731    for (LocalVariableGen lv : mg.getLocalVariables()) {
2732      if (lv.getName().equals(name)) {
2733        assert lv.getType().equals(typ) : lv + " " + typ;
2734        return lv;
2735      }
2736    }
2737
2738    // Create the variable
2739    return mg.addLocalVariable(name, typ, null, null);
2740  }
2741
2742  /**
2743   * Returns the local variable used to store the return result. If it is not present, creates it
2744   * with the specified type. If the variable is known to already exist, the type can be null.
2745   */
2746  LocalVariableGen get_return_local(MethodGen mg, @Nullable Type return_type) {
2747
2748    // Find the local used for the return value
2749    LocalVariableGen return_local = null;
2750    for (LocalVariableGen lv : mg.getLocalVariables()) {
2751      if (lv.getName().equals("return__$trace2_val")) {
2752        return_local = lv;
2753        break;
2754      }
2755    }
2756
2757    // If a type was specified and the variable was found, they must match
2758    if (return_local == null) {
2759      assert (return_type != null) : " return__$trace2_val doesn't exist";
2760    } else {
2761      assert return_type.equals(return_local.getType())
2762          : " return_type = " + return_type + "current type = " + return_local.getType();
2763    }
2764
2765    if (return_local == null) {
2766      // log ("Adding return local of type %s%n", return_type);
2767      return_local = mg.addLocalVariable("return__$trace2_val", return_type, null, null);
2768    }
2769
2770    return return_local;
2771  }
2772
2773  /**
2774   * Creates a MethodInfo corresponding to the specified method. The exit locations are filled in,
2775   * but the reflection information is not generated. Returns null if there are no instructions.
2776   *
2777   * @param class_info class containing the method
2778   * @param mg method to inspect
2779   * @return MethodInfo for the method
2780   */
2781  @Nullable MethodInfo create_method_info(ClassInfo class_info, MethodGen mg) {
2782
2783    // if (mg.getName().equals("<clinit>")) {
2784    //   // This case DOES occur at run time.  -MDE 1/22/2010
2785    // }
2786
2787    // Get the argument names for this method
2788    String[] argNames = mg.getArgumentNames();
2789    LocalVariableGen[] lvs = mg.getLocalVariables();
2790    int param_offset = 1;
2791    if (mg.isStatic()) {
2792      param_offset = 0;
2793    }
2794    if (lvs != null) {
2795      for (int ii = 0; ii < argNames.length; ii++) {
2796        if ((ii + param_offset) < lvs.length) {
2797          argNames[ii] = lvs[ii + param_offset].getName();
2798        }
2799      }
2800    }
2801
2802    // Get the argument types for this method
2803    Type[] argTypes = mg.getArgumentTypes();
2804    @ClassGetName String[] arg_type_strings = new @ClassGetName String[argTypes.length];
2805    for (int ii = 0; ii < argTypes.length; ii++) {
2806      arg_type_strings[ii] = typeToClassGetName(argTypes[ii]);
2807      // System.out.printf("DCI arg types: %s %s%n", argTypes[ii], arg_type_strings[ii]);
2808    }
2809
2810    // Loop through each instruction and find the line number for each
2811    // return opcode
2812    List<Integer> exit_locs = new ArrayList<>();
2813
2814    // Tells whether each exit loc in the method is included or not
2815    // (based on filters)
2816    List<Boolean> isIncluded = new ArrayList<>();
2817
2818    // log ("Looking for exit points in %s%n", mg.getName());
2819    InstructionList il = mg.getInstructionList();
2820    int line_number = 0;
2821    int last_line_number = 0;
2822    boolean foundLine;
2823
2824    if (il == null) {
2825      return null;
2826    }
2827
2828    for (InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
2829      foundLine = false;
2830
2831      if (ih.hasTargeters()) {
2832        for (InstructionTargeter it : ih.getTargeters()) {
2833          if (it instanceof LineNumberGen) {
2834            LineNumberGen lng = (LineNumberGen) it;
2835            // log ("  line number at %s: %d%n", ih, lng.getSourceLine());
2836            // System.out.printf("  line number at %s: %d%n", ih,
2837            // lng.getSourceLine());
2838            line_number = lng.getSourceLine();
2839            foundLine = true;
2840          }
2841        }
2842      }
2843
2844      switch (ih.getInstruction().getOpcode()) {
2845        case Const.ARETURN:
2846        case Const.DRETURN:
2847        case Const.FRETURN:
2848        case Const.IRETURN:
2849        case Const.LRETURN:
2850        case Const.RETURN:
2851          // log ("Exit at line %d%n", line_number);
2852          // only do incremental lines if we don't have the line generator
2853          if (line_number == last_line_number && foundLine == false) {
2854            line_number++;
2855          }
2856          last_line_number = line_number;
2857
2858          exit_locs.add(line_number);
2859          isIncluded.add(true);
2860          break;
2861
2862        default:
2863          break;
2864      }
2865    }
2866
2867    return new MethodInfo(
2868        class_info, mg.getName(), argNames, arg_type_strings, exit_locs, isIncluded);
2869  }
2870
2871  /**
2872   * Adds a call to DCRuntime.set_class_initialized (String classname) to the class initializer for
2873   * this class. Creates a class initializer if one is not currently present.
2874   */
2875  void track_class_init() {
2876
2877    // Look for the class init method.  If not found, create an empty one.
2878    Method cinit = null;
2879    for (Method m : gen.getMethods()) {
2880      if (m.getName().equals("<clinit>")) {
2881        cinit = m;
2882        break;
2883      }
2884    }
2885    if (cinit == null) {
2886      InstructionList il = new InstructionList();
2887      il.append(InstructionFactory.createReturn(Type.VOID));
2888      MethodGen cinit_gen =
2889          new MethodGen(
2890              Const.ACC_STATIC,
2891              Type.VOID,
2892              Type.NO_ARGS,
2893              new String[0],
2894              "<clinit>",
2895              gen.getClassName(),
2896              il,
2897              pool);
2898      cinit_gen.setMaxLocals();
2899      cinit_gen.setMaxStack();
2900      cinit_gen.update();
2901      cinit = cinit_gen.getMethod();
2902      gen.addMethod(cinit);
2903    }
2904
2905    try {
2906      MethodGen cinit_gen = new MethodGen(cinit, gen.getClassName(), pool);
2907      setCurrentStackMapTable(cinit_gen, gen.getMajor());
2908
2909      // Add a call to DCRuntime.set_class_initialized to the beginning of the method
2910      InstructionList il = new InstructionList();
2911      il.append(ifact.createConstant(gen.getClassName()));
2912      il.append(
2913          ifact.createInvoke(
2914              dcompRuntimeClassName,
2915              "set_class_initialized",
2916              Type.VOID,
2917              string_arg,
2918              Const.INVOKESTATIC));
2919
2920      insertAtMethodStart(cinit_gen, il);
2921      createNewStackMapAttribute(cinit_gen);
2922      cinit_gen.setMaxLocals();
2923      cinit_gen.setMaxStack();
2924      gen.replaceMethod(cinit, cinit_gen.getMethod());
2925    } catch (Throwable t) {
2926      if (debugInstrument.enabled) {
2927        t.printStackTrace();
2928      }
2929      throw new Error(
2930          "Unexpected error processing " + gen.getClassName() + "." + cinit.getName(), t);
2931    }
2932  }
2933
2934  /**
2935   * Creates code that makes the index comparable (for indexing purposes) with the array in array
2936   * load instructions. First the arrayref and its index are duplicated on the stack. Then the
2937   * appropriate array load method is called to mark them as comparable and update the tag stack.
2938   * Finally the original load instruction is performed.
2939   *
2940   * @param inst an array load instruction
2941   * @return instruction list that calls the runtime to handle the array load instruction
2942   */
2943  InstructionList array_load(Instruction inst) {
2944
2945    InstructionList il = new InstructionList();
2946
2947    // Duplicate the array ref and index and pass them to DCRuntime
2948    // which will make the index comparable with the array.  In the case
2949    // of primtives it will also get the tag for the primitive and push
2950    // it on the tag stack.
2951    il.append(new DUP2());
2952    String method = "primitive_array_load";
2953    if (inst instanceof AALOAD) {
2954      method = "ref_array_load";
2955    } else if (is_uninit_class(gen.getClassName())) {
2956      method = "primitive_array_load_null_ok";
2957    }
2958
2959    il.append(dcr_call(method, Type.VOID, new Type[] {Type.OBJECT, Type.INT}));
2960
2961    // Perform the original instruction
2962    il.append(inst);
2963
2964    return il;
2965  }
2966
2967  /**
2968   * Creates code to make the index comparable (for indexing purposes) with the array in the array
2969   * store instruction. This is accomplished by calling the specified method and passing it the
2970   * array reference, index, and value (of base_type). The method will mark the array and index as
2971   * comparable and perform the array store.
2972   *
2973   * @param inst an array store instruction
2974   * @param method runtime method to call
2975   * @param base_type type of array store
2976   * @return instruction list that calls the runtime to handle the array store instruction
2977   */
2978  InstructionList array_store(Instruction inst, String method, Type base_type) {
2979
2980    InstructionList il = new InstructionList();
2981    Type arr_type = new ArrayType(base_type, 1);
2982    il.append(dcr_call(method, Type.VOID, new Type[] {arr_type, Type.INT, base_type}));
2983    return il;
2984  }
2985
2986  /**
2987   * Creates code that pushes the array's tag onto the tag stack, so that the index is comparable to
2988   * the array length. First, the arrayref is duplicated on the stack. Then a method is called to
2989   * push the array's tag onto the tag stack. Finally the original arraylength instruction is
2990   * performed.
2991   *
2992   * @param inst an arraylength instruction
2993   * @return instruction list that calls the runtime to handle the arraylength instruction
2994   */
2995  InstructionList array_length(Instruction inst) {
2996
2997    InstructionList il = new InstructionList();
2998
2999    // Duplicate the array ref and pass it to DCRuntime which will push
3000    // it onto the tag stack.
3001    il.append(new DUP());
3002    il.append(dcr_call("push_array_tag", Type.VOID, new Type[] {Type.OBJECT}));
3003
3004    // Perform the original instruction
3005    il.append(inst);
3006
3007    return il;
3008  }
3009
3010  /**
3011   * Creates code to make the declared length of a new array comparable to its index.
3012   *
3013   * @param inst a anewarray or newarray instruction
3014   * @return instruction list that calls the runtime to handle the newarray instruction
3015   */
3016  InstructionList new_array(Instruction inst) {
3017    InstructionList il = new InstructionList();
3018
3019    // Perform the original instruction
3020    il.append(inst);
3021
3022    // Duplicate the array ref from the top of the stack and pass it
3023    // to DCRuntime which will push it onto the tag stack.
3024    il.append(new DUP());
3025    il.append(dcr_call("push_array_tag", Type.VOID, new Type[] {Type.OBJECT}));
3026
3027    // Make the array and the count comparable. Also, pop the tags for
3028    // the array and the count off the tag stack.
3029    il.append(dcr_call("cmp_op", Type.VOID, Type.NO_ARGS));
3030
3031    return il;
3032  }
3033
3034  /**
3035   * Creates code to make the declared lengths of a new two-dimensional array comparable to the
3036   * corresponding indices.
3037   *
3038   * @param inst a multianewarray instruction
3039   * @return instruction list that calls the runtime to handle the multianewarray instruction
3040   */
3041  InstructionList multiarray2(Instruction inst) {
3042    InstructionList il = new InstructionList();
3043
3044    // Duplicate both count arguments
3045    il.append(new DUP2());
3046
3047    // Perform the original instruction
3048    il.append(inst);
3049
3050    // Duplicate the new arrayref and put it below the count arguments
3051    // Stack is now: ..., arrayref, count1, count2, arrayref
3052    il.append(new DUP_X2());
3053
3054    Type objArray = new ArrayType(Type.OBJECT, 1);
3055    il.append(dcr_call("multianewarray2", Type.VOID, new Type[] {Type.INT, Type.INT, objArray}));
3056
3057    return il;
3058  }
3059
3060  /**
3061   * Returns true if this method is the method identified by method_id. The method is encoded as
3062   * 'classname:method'. The classname is the fully qualified class name. The method is this simple
3063   * method name (no signature).
3064   *
3065   * @param method_id classname:method to check
3066   * @param classname class to check for
3067   * @param m method to check for
3068   * @return true if they match
3069   */
3070  boolean has_specified_method(String method_id, String classname, Method m) {
3071
3072    // Get the classname and method name
3073    String[] sa = method_id.split(":");
3074    String m_classname = sa[0];
3075    String m_name = sa[1];
3076    // System.out.printf("has_specified_method: %s:%s - %s.%s%n", m_classname,
3077    //                    m_name, classname, m.getName());
3078
3079    if (!m_classname.equals(classname)) {
3080      return false;
3081    }
3082
3083    if (!m_name.equals(m.getName())) {
3084      return false;
3085    }
3086
3087    return true;
3088  }
3089
3090  /**
3091   * Returns whether or not this ppt should be included. A ppt is included if it matches ones of the
3092   * select patterns and doesn't match any of the omit patterns.
3093   *
3094   * @param className class to test
3095   * @param methodName method to test
3096   * @param pptName ppt to look for
3097   * @return true if this ppt should be included
3098   */
3099  boolean should_track(@ClassGetName String className, String methodName, String pptName) {
3100
3101    Instrument.debug_transform.log(
3102        "Considering tracking ppt: %s, %s, %s%n", className, methodName, pptName);
3103
3104    // Don't track any JDK classes
3105    if (BcelUtil.inJdk(className)) {
3106      Instrument.debug_transform.log("ignoring %s, is a JDK class%n", className);
3107      return false;
3108    }
3109
3110    // Don't track toString methods because we call them in
3111    // our debug statements.
3112    if (pptName.contains("toString")) {
3113      Instrument.debug_transform.log("ignoring %s, is a toString method%n", pptName);
3114      return false;
3115    }
3116
3117    // call shouldIgnore to check ppt-omit-pattern(s) and ppt-select-pattern(s)
3118    return !daikon.chicory.Instrument.shouldIgnore(className, methodName, pptName);
3119  }
3120
3121  /**
3122   * Constructs a ppt entry name from a Method.
3123   *
3124   * @param fullClassName class name
3125   * @param m method
3126   * @return corresponding ppt name
3127   */
3128  static String methodEntryName(String fullClassName, Method m) {
3129
3130    // System.out.printf("classname = %s, method = %s, short_name = %s%n",
3131    //                   fullClassName, m, m.getName());
3132
3133    // Get an array of the type names
3134    Type[] argTypes = m.getArgumentTypes();
3135    String[] type_names = new String[argTypes.length];
3136    for (int ii = 0; ii < argTypes.length; ii++) {
3137      type_names[ii] = argTypes[ii].toString();
3138    }
3139
3140    // Remove exceptions from the name
3141    String full_name = m.toString();
3142    full_name = full_name.replaceFirst("\\s*throws.*", "");
3143
3144    return fullClassName
3145        + "."
3146        + DaikonWriter.methodEntryName(fullClassName, type_names, full_name, m.getName());
3147  }
3148
3149  /**
3150   * Convenience function to construct a call to a static method in DCRuntime.
3151   *
3152   * @param methodName method to call
3153   * @param returnType type of method return
3154   * @param argTypes array of method argument types
3155   * @return InvokeInstruction for the call
3156   */
3157  InvokeInstruction dcr_call(String methodName, Type returnType, Type[] argTypes) {
3158
3159    return ifact.createInvoke(
3160        dcompRuntimeClassName, methodName, returnType, argTypes, Const.INVOKESTATIC);
3161  }
3162
3163  /**
3164   * Create the code to call discard_tag(tag_count) and append inst to the end of that code.
3165   *
3166   * @param inst instruction to be replaced
3167   * @param tag_count number of tags to discard
3168   * @return InstructionList
3169   */
3170  InstructionList discard_tag_code(Instruction inst, int tag_count) {
3171    InstructionList il = new InstructionList();
3172    il.append(ifact.createConstant(tag_count));
3173    il.append(dcr_call("discard_tag", Type.VOID, integer_arg));
3174    append_inst(il, inst);
3175    return il;
3176  }
3177
3178  /**
3179   * Duplicates the item on the top of stack. If the value on the top of the stack is a primitive,
3180   * we need to do the same on the tag stack. Otherwise, we need do nothing.
3181   */
3182  InstructionList dup_tag(Instruction inst, OperandStack stack) {
3183    Type top = stack.peek();
3184    if (debug_dup.enabled) {
3185      debug_dup.log("DUP -> %s [... %s]%n", "dup", stack_contents(stack, 2));
3186    }
3187    if (is_primitive(top)) {
3188      return build_il(dcr_call("dup", Type.VOID, Type.NO_ARGS), inst);
3189    }
3190    return null;
3191  }
3192
3193  /**
3194   * Duplicates the item on the top of the stack and inserts it 2 values down in the stack. If the
3195   * value at the top of the stack is not a primitive, there is nothing to do here. If the second
3196   * value is not a primitive, then we need only to insert the duped value down 1 on the tag stack
3197   * (which contains only primitives).
3198   */
3199  InstructionList dup_x1_tag(Instruction inst, OperandStack stack) {
3200    Type top = stack.peek();
3201    if (debug_dup.enabled) {
3202      debug_dup.log("DUP -> %s [... %s]%n", "dup_x1", stack_contents(stack, 2));
3203    }
3204    if (!is_primitive(top)) {
3205      return null;
3206    }
3207    String method = "dup_x1";
3208    if (!is_primitive(stack.peek(1))) {
3209      method = "dup";
3210    }
3211    return build_il(dcr_call(method, Type.VOID, Type.NO_ARGS), inst);
3212  }
3213
3214  /**
3215   * Duplicates either the top 2 category 1 values or a single category 2 value and inserts it 2 or
3216   * 3 values down on the stack.
3217   */
3218  InstructionList dup2_x1_tag(Instruction inst, OperandStack stack) {
3219    String op;
3220    Type top = stack.peek();
3221    if (is_category2(top)) {
3222      if (is_primitive(stack.peek(1))) {
3223        op = "dup_x1";
3224      } else { // not a primitive, so just dup
3225        op = "dup";
3226      }
3227    } else if (is_primitive(top)) {
3228      if (is_primitive(stack.peek(1)) && is_primitive(stack.peek(2))) op = "dup2_x1";
3229      else if (is_primitive(stack.peek(1))) op = "dup2";
3230      else if (is_primitive(stack.peek(2))) op = "dup_x1";
3231      else {
3232        // neither value 1 nor value 2 is primitive
3233        op = "dup";
3234      }
3235    } else { // top is not primitive
3236      if (is_primitive(stack.peek(1)) && is_primitive(stack.peek(2))) {
3237        op = "dup_x1";
3238      } else if (is_primitive(stack.peek(1))) {
3239        op = "dup";
3240      } else { // neither of the top two values is primitive
3241        op = null;
3242      }
3243    }
3244    if (debug_dup.enabled) {
3245      debug_dup.log("DUP2_X1 -> %s [... %s]%n", op, stack_contents(stack, 3));
3246    }
3247
3248    if (op != null) {
3249      return build_il(dcr_call(op, Type.VOID, Type.NO_ARGS), inst);
3250    }
3251    return null;
3252  }
3253
3254  /**
3255   * Duplicate either one category 2 value or two category 1 values. The instruction is implemented
3256   * as necessary on the tag stack.
3257   */
3258  InstructionList dup2_tag(Instruction inst, OperandStack stack) {
3259    Type top = stack.peek();
3260    String op;
3261    if (is_category2(top)) {
3262      op = "dup";
3263    } else if (is_primitive(top) && is_primitive(stack.peek(1))) op = "dup2";
3264    else if (is_primitive(top) || is_primitive(stack.peek(1))) op = "dup";
3265    else {
3266      // both of the top two items are not primitive, nothing to dup
3267      op = null;
3268    }
3269    if (debug_dup.enabled) {
3270      debug_dup.log("DUP2 -> %s [... %s]%n", op, stack_contents(stack, 2));
3271    }
3272    if (op != null) {
3273      return build_il(dcr_call(op, Type.VOID, Type.NO_ARGS), inst);
3274    }
3275    return null;
3276  }
3277
3278  /**
3279   * Dup the category 1 value on the top of the stack and insert it either two or three values down
3280   * on the stack.
3281   */
3282  InstructionList dup_x2(Instruction inst, OperandStack stack) {
3283    Type top = stack.peek();
3284    String op = null;
3285    if (is_primitive(top)) {
3286      if (is_category2(stack.peek(1))) op = "dup_x1";
3287      else if (is_primitive(stack.peek(1)) && is_primitive(stack.peek(2))) op = "dup_x2";
3288      else if (is_primitive(stack.peek(1)) || is_primitive(stack.peek(2))) op = "dup_x1";
3289      else {
3290        op = "dup";
3291      }
3292    }
3293    if (debug_dup.enabled) {
3294      debug_dup.log("DUP_X2 -> %s [... %s]%n", op, stack_contents(stack, 3));
3295    }
3296    if (op != null) {
3297      return build_il(dcr_call(op, Type.VOID, Type.NO_ARGS), inst);
3298    }
3299    return null;
3300  }
3301
3302  /**
3303   * Duplicate the top one or two operand stack values and insert two, three, or four values down.
3304   */
3305  InstructionList dup2_x2(Instruction inst, OperandStack stack) {
3306    Type top = stack.peek();
3307    String op;
3308    if (is_category2(top)) {
3309      if (is_category2(stack.peek(1))) op = "dup_x1";
3310      else if (is_primitive(stack.peek(1)) && is_primitive(stack.peek(2))) op = "dup_x2";
3311      else if (is_primitive(stack.peek(1)) || is_primitive(stack.peek(2))) op = "dup_x1";
3312      else {
3313        // both values are references
3314        op = "dup";
3315      }
3316    } else if (is_primitive(top)) {
3317      if (is_category2(stack.peek(1))) {
3318        throw new Error("not supposed to happen " + stack_contents(stack, 3));
3319      } else if (is_category2(stack.peek(2))) {
3320        if (is_primitive(stack.peek(1))) {
3321          op = "dup2_x1";
3322        } else {
3323          op = "dup_x1";
3324        }
3325      } else if (is_primitive(stack.peek(1))) {
3326        if (is_primitive(stack.peek(2)) && is_primitive(stack.peek(3))) op = "dup2_x2";
3327        else if (is_primitive(stack.peek(2)) || is_primitive(stack.peek(3))) op = "dup2_x1";
3328        else {
3329          // both 2 and 3 are references
3330          op = "dup2";
3331        }
3332      } else { // 1 is a reference
3333        if (is_primitive(stack.peek(2)) && is_primitive(stack.peek(3))) op = "dup_x2";
3334        else if (is_primitive(stack.peek(2)) || is_primitive(stack.peek(3))) op = "dup_x1";
3335        else {
3336          // both 2 and 3 are references
3337          op = "dup";
3338        }
3339      }
3340    } else { // top is a reference
3341      if (is_category2(stack.peek(1))) {
3342        throw new Error("not supposed to happen " + stack_contents(stack, 3));
3343      } else if (is_category2(stack.peek(2))) {
3344        if (is_primitive(stack.peek(1))) {
3345          op = "dup_x1";
3346        } else {
3347          op = null; // nothing to dup
3348        }
3349      } else if (is_primitive(stack.peek(1))) {
3350        if (is_primitive(stack.peek(2)) && is_primitive(stack.peek(3))) op = "dup_x2";
3351        else if (is_primitive(stack.peek(2)) || is_primitive(stack.peek(3))) op = "dup_x1";
3352        else {
3353          // both 2 and 3 are references
3354          op = "dup";
3355        }
3356      } else { // 1 is a reference
3357        op = null; // nothing to dup
3358      }
3359    }
3360    if (debug_dup.enabled) {
3361      debug_dup.log("DUP_X2 -> %s [... %s]%n", op, stack_contents(stack, 3));
3362    }
3363    if (op != null) {
3364      return build_il(dcr_call(op, Type.VOID, Type.NO_ARGS), inst);
3365    }
3366    return null;
3367  }
3368
3369  /**
3370   * Pop instructions discard the top of the stack. We want to discard the top of the tag stack iff
3371   * the item on the top of the stack is a primitive.
3372   */
3373  InstructionList pop_tag(Instruction inst, OperandStack stack) {
3374    Type top = stack.peek();
3375    if (is_primitive(top)) {
3376      return discard_tag_code(inst, 1);
3377    }
3378    return null;
3379  }
3380
3381  /**
3382   * Pops either the top 2 category 1 values or a single category 2 value from the top of the stack.
3383   * We must do the same to the tag stack if the values are primitives.
3384   */
3385  InstructionList pop2_tag(Instruction inst, OperandStack stack) {
3386    Type top = stack.peek();
3387    if (is_category2(top)) {
3388      return discard_tag_code(inst, 1);
3389    } else {
3390      int cnt = 0;
3391      if (is_primitive(top)) {
3392        cnt++;
3393      }
3394      if (is_primitive(stack.peek(1))) {
3395        cnt++;
3396      }
3397      if (cnt > 0) {
3398        return discard_tag_code(inst, cnt);
3399      }
3400    }
3401    return null;
3402  }
3403
3404  /**
3405   * Swaps the two category 1 types on the top of the stack. We need to swap the top of the tag
3406   * stack if the two top elements on the real stack are primitives.
3407   */
3408  InstructionList swap_tag(Instruction inst, OperandStack stack) {
3409    Type type1 = stack.peek();
3410    Type type2 = stack.peek(1);
3411    if (is_primitive(type1) && is_primitive(type2)) {
3412      return build_il(dcr_call("swap", Type.VOID, Type.NO_ARGS), inst);
3413    }
3414    return null;
3415  }
3416
3417  /**
3418   * Adjusts the tag stack for load constant opcodes. If the constant is a primitive, pushes its tag
3419   * on the tag stack. If the constant is a reference (string, class), does nothing.
3420   */
3421  @Nullable InstructionList ldc_tag(Instruction inst, OperandStack stack) {
3422    Type type;
3423    if (inst instanceof LDC) { // LDC_W extends LDC
3424      type = ((LDC) inst).getType(pool);
3425    } else {
3426      type = ((LDC2_W) inst).getType(pool);
3427    }
3428    if (!(type instanceof BasicType)) {
3429      return null;
3430    }
3431    return build_il(dcr_call("push_const", Type.VOID, Type.NO_ARGS), inst);
3432  }
3433
3434  /**
3435   * Handle the instruction that allocates multi-dimensional arrays. If the new array has 2
3436   * dimensions, make the integer arguments comparable to the corresponding indices of the new
3437   * array. For any other number of dimensions, discard the tags for the arguments. Higher
3438   * dimensions should really be handled as well, but there are very few cases of this and the
3439   * resulting code would be quite complex (see multiarray2 for details).
3440   */
3441  InstructionList multi_newarray_dc(Instruction inst) {
3442    int dims = ((MULTIANEWARRAY) inst).getDimensions();
3443    if (dims == 2) {
3444      return multiarray2(inst);
3445    } else {
3446      return discard_tag_code(inst, dims);
3447    }
3448  }
3449
3450  /**
3451   * Create an instruction list that calls the runtime to handle returns for the tag stack follow by
3452   * the original return instruction.
3453   *
3454   * @param mg method to modify
3455   * @param inst return instruction to be replaced
3456   * @return the instruction list
3457   */
3458  InstructionList return_tag(MethodGen mg, Instruction inst) {
3459    Type type = mg.getReturnType();
3460    InstructionList il = new InstructionList();
3461
3462    // Push the tag frame
3463    il.append(InstructionFactory.createLoad(object_arr, tag_frame_local.getIndex()));
3464
3465    if ((type instanceof BasicType) && (type != Type.VOID)) {
3466      il.append(dcr_call("normal_exit_primitive", Type.VOID, new Type[] {object_arr}));
3467    } else {
3468      il.append(dcr_call("normal_exit", Type.VOID, new Type[] {object_arr}));
3469    }
3470    il.append(inst);
3471    return il;
3472  }
3473
3474  /**
3475   * Returns whether or not the specified type is a primitive (int, float, double, etc).
3476   *
3477   * @param type type to check
3478   * @return true if type is primitive
3479   */
3480  @Pure
3481  boolean is_primitive(Type type) {
3482    return (type instanceof BasicType) && (type != Type.VOID);
3483  }
3484
3485  /**
3486   * Returns whether or not the specified type is a category 2 (8 byte) type.
3487   *
3488   * @param type type to check
3489   * @return true if type requires 8 bytes
3490   */
3491  @Pure
3492  boolean is_category2(Type type) {
3493    return (type == Type.DOUBLE) || (type == Type.LONG);
3494  }
3495
3496  /**
3497   * Returns the type of the last instruction that modified the top of stack. A gross attempt to
3498   * figure out what is on the top of stack.
3499   *
3500   * @param ih search backward from this instruction
3501   * @return type of last instruction that modified the top of the stack
3502   */
3503  @Nullable Type find_last_push(InstructionHandle ih) {
3504
3505    for (ih = ih.getPrev(); ih != null; ih = ih.getPrev()) {
3506      Instruction inst = ih.getInstruction();
3507      if (inst instanceof InvokeInstruction) {
3508        return ((InvokeInstruction) inst).getReturnType(pool);
3509      }
3510      if (inst instanceof TypedInstruction) {
3511        return ((TypedInstruction) inst).getType(pool);
3512      }
3513    }
3514    throw new Error("couldn't find any typed instructions");
3515  }
3516
3517  /**
3518   * Returns whether or not the invoke specified invokes a native method. This requires that the
3519   * class that contains the method to be loaded.
3520   *
3521   * @param invoke instruction to check
3522   * @return true if the invoke calls a native method
3523   */
3524  @Pure
3525  boolean is_native(InvokeInstruction invoke) {
3526
3527    // Get the class of the method
3528    ClassLoader loader = getClass().getClassLoader();
3529    Class<?> clazz;
3530    try {
3531      clazz = Class.forName(invoke.getClassName(pool), false, loader);
3532    } catch (Exception e) {
3533      throw new Error("can't get class " + invoke.getClassName(pool), e);
3534    }
3535
3536    // Get the arguments to the method
3537    Type[] argTypes = invoke.getArgumentTypes(pool);
3538    Class<?>[] arg_classes = new Class<?>[argTypes.length];
3539    for (int ii = 0; ii < argTypes.length; ii++) {
3540      arg_classes[ii] = type_to_class(argTypes[ii], loader);
3541    }
3542
3543    // Find the method and determine if its native
3544    int modifiers = 0;
3545    String methodName = invoke.getMethodName(pool);
3546    String classes = clazz.getName();
3547    try {
3548      if (methodName.equals("<init>")) {
3549        Constructor<?> c = clazz.getDeclaredConstructor(arg_classes);
3550        modifiers = c.getModifiers();
3551      } else if (clazz.isInterface()) {
3552        return false; // presume interfaces aren't native...
3553      } else {
3554
3555        java.lang.reflect.Method m = null;
3556        while (m == null) {
3557          try {
3558            m = clazz.getDeclaredMethod(methodName, arg_classes);
3559            modifiers = m.getModifiers();
3560          } catch (NoSuchMethodException e) {
3561            clazz = clazz.getSuperclass();
3562            classes += ", " + clazz.getName();
3563          }
3564        }
3565      }
3566    } catch (Exception e) {
3567      throw new Error(
3568          "can't find method "
3569              + methodName
3570              + " "
3571              + Arrays.toString(arg_classes)
3572              + " "
3573              + classes
3574              + " "
3575              + invoke.toString(pool.getConstantPool()),
3576          e);
3577    }
3578
3579    return Modifier.isNative(modifiers);
3580  }
3581
3582  /**
3583   * Converts a BCEL type to a Class. The class referenced will be loaded but not initialized. The
3584   * specified loader must be able to find it. If load is null, the default loader will be used.
3585   *
3586   * @param t type to get class for
3587   * @param loader to use to locate class
3588   * @return instance of class
3589   */
3590  static Class<?> type_to_class(Type t, ClassLoader loader) {
3591
3592    if (loader == null) {
3593      loader = DCInstrument.class.getClassLoader();
3594    }
3595
3596    if (t == Type.BOOLEAN) {
3597      return Boolean.TYPE;
3598    } else if (t == Type.BYTE) {
3599      return Byte.TYPE;
3600    } else if (t == Type.CHAR) {
3601      return Character.TYPE;
3602    } else if (t == Type.DOUBLE) {
3603      return Double.TYPE;
3604    } else if (t == Type.FLOAT) {
3605      return Float.TYPE;
3606    } else if (t == Type.INT) {
3607      return Integer.TYPE;
3608    } else if (t == Type.LONG) {
3609      return Long.TYPE;
3610    } else if (t == Type.SHORT) {
3611      return Short.TYPE;
3612    } else if (t instanceof ObjectType || t instanceof ArrayType) {
3613      @ClassGetName String sig = typeToClassGetName(t);
3614      try {
3615        return Class.forName(sig, false, loader);
3616      } catch (Exception e) {
3617        throw new Error("can't get class " + sig, e);
3618      }
3619    } else {
3620      throw new Error("unexpected type " + t);
3621    }
3622  }
3623
3624  /**
3625   * Modify a doubled native method to call its original method. It pops all of the parameter tags
3626   * off of the tag stack. If there is a primitive return value it puts a new tag value on the stack
3627   * for it.
3628   *
3629   * <p>TODO: add a way to provide a synopsis for native methods that affect comparability.
3630   *
3631   * @param gen current class
3632   * @param mg the interface method. Must be native.
3633   */
3634  void fix_native(ClassGen gen, MethodGen mg) {
3635
3636    InstructionList il = new InstructionList();
3637    Type[] argTypes = mg.getArgumentTypes();
3638    String[] argNames = mg.getArgumentNames();
3639
3640    debug_native.log("Native call %s%n", mg);
3641
3642    // Build local variables for each argument to the method
3643    if (!mg.isStatic()) {
3644      mg.addLocalVariable("this", new ObjectType(mg.getClassName()), null, null);
3645    }
3646    for (int ii = 0; ii < argTypes.length; ii++) {
3647      mg.addLocalVariable(argNames[ii], argTypes[ii], null, null);
3648    }
3649
3650    // Discard the tags for any primitive arguments passed to system
3651    // methods
3652    int primitive_cnt = 0;
3653    for (Type argType : argTypes) {
3654      if (argType instanceof BasicType) {
3655        primitive_cnt++;
3656      }
3657    }
3658    if (primitive_cnt > 0) {
3659      il.append(discard_tag_code(new NOP(), primitive_cnt));
3660    }
3661
3662    // push a tag if there is a primitive return value
3663    Type returnType = mg.getReturnType();
3664    if ((returnType instanceof BasicType) && (returnType != Type.VOID)) {
3665      il.append(dcr_call("push_const", Type.VOID, Type.NO_ARGS));
3666    }
3667
3668    // If the method is not static, push the instance on the stack
3669    if (!mg.isStatic()) {
3670      il.append(InstructionFactory.createLoad(new ObjectType(gen.getClassName()), 0));
3671    }
3672
3673    // System.out.printf("%s: atc = %d, anc = %d%n", mg.getName(), argTypes.length,
3674    // argNames.length);
3675
3676    // if call is sun.reflect.Reflection.getCallerClass (realFramesToSkip)
3677    if (mg.getName().equals("getCallerClass")
3678        && (argTypes.length == 1)
3679        && gen.getClassName().equals("sun.reflect.Reflection")) {
3680
3681      // The call returns the class realFramesToSkip up on the stack. Since we
3682      // have added this call in between, we need to increment that number by 1.
3683      il.append(InstructionFactory.createLoad(Type.INT, 0));
3684      il.append(ifact.createConstant(1));
3685      il.append(new IADD());
3686      // System.out.printf("adding 1 in %s.%s%n", gen.getClassName(),
3687      //                   mg.getName());
3688
3689    } else { // normal call
3690
3691      // push each argument on the stack
3692      int param_index = 1;
3693      if (mg.isStatic()) {
3694        param_index = 0;
3695      }
3696      for (Type argType : argTypes) {
3697        il.append(InstructionFactory.createLoad(argType, param_index));
3698        param_index += argType.getSize();
3699      }
3700    }
3701
3702    // Call the method
3703    il.append(
3704        ifact.createInvoke(
3705            gen.getClassName(),
3706            mg.getName(),
3707            mg.getReturnType(),
3708            argTypes,
3709            (mg.isStatic() ? Const.INVOKESTATIC : Const.INVOKEVIRTUAL)));
3710
3711    // If there is a return value, return it
3712    il.append(InstructionFactory.createReturn(mg.getReturnType()));
3713
3714    // We've created new il; we need to set the instruction handle positions.
3715    il.setPositions();
3716
3717    // Add the instructions to the method
3718    mg.setInstructionList(il);
3719    mg.setMaxStack();
3720    mg.setMaxLocals();
3721
3722    // turn off the native flag
3723    mg.setAccessFlags(mg.getAccessFlags() & ~Const.ACC_NATIVE);
3724  }
3725
3726  /**
3727   * Returns whether or not tag fields are used within the specified method of the specified class.
3728   * We can safely use class fields except in Object, String, and Class.
3729   *
3730   * @param mg method to check
3731   * @param classname class to check
3732   * @return true if tag fields may be used in class for method
3733   */
3734  boolean tag_fields_ok(MethodGen mg, @ClassGetName String classname) {
3735
3736    if (BcelUtil.isConstructor(mg)) {
3737      if (!this.constructor_is_initialized) {
3738        return false;
3739      }
3740    }
3741
3742    if (!jdk_instrumented) {
3743      if (BcelUtil.inJdk(classname)) {
3744        return false;
3745      }
3746    }
3747
3748    if (!classname.startsWith("java.lang")) {
3749      return true;
3750    }
3751
3752    if (classname.equals("java.lang.String")
3753        || classname.equals("java.lang.Class")
3754        || classname.equals("java.lang.Object")
3755        || classname.equals("java.lang.ClassLoader")) {
3756      return false;
3757    }
3758
3759    return true;
3760  }
3761
3762  /**
3763   * Adds a tag field that parallels each primitive field in the class. The tag field is of type
3764   * object and holds the tag associated with that primitive.
3765   */
3766  void add_tag_fields() {
3767
3768    // Add fields for tag storage for each primitive field
3769    for (Field field : gen.getFields()) {
3770      if (is_primitive(field.getType()) && !field.isStatic()) {
3771        FieldGen tag_field =
3772            new FieldGen(
3773                field.getAccessFlags() | Const.ACC_SYNTHETIC,
3774                Type.OBJECT,
3775                DCRuntime.tag_field_name(field.getName()),
3776                pool);
3777        gen.addField(tag_field.getField());
3778      }
3779    }
3780  }
3781
3782  /**
3783   * Returns a string describing the top max_items items on the stack.
3784   *
3785   * @param stack OperandStack
3786   * @param max_items number of items to describe
3787   * @return string describing the top max_items on the operand stack
3788   */
3789  static String stack_contents(OperandStack stack, int max_items) {
3790    String contents = "";
3791    if (max_items >= stack.size()) {
3792      max_items = stack.size() - 1;
3793    }
3794    for (int ii = max_items; ii >= 0; ii--) {
3795      if (contents.length() != 0) {
3796        contents += ", ";
3797      }
3798      contents += stack.peek(ii);
3799    }
3800    return contents;
3801  }
3802
3803  /**
3804   * Creates tag get and set accessor methods for each field in gen. An accessor is created for each
3805   * field (including final, static, and private fields). The accessors share the modifiers of their
3806   * field (except that all are final). Accessors are named {@code <field>_<class>__$get_tag} and
3807   * {@code <field>_<class>__$set_tag}. The class name must be included because field names can
3808   * shadow one another.
3809   *
3810   * <p>If tag_fields_ok is true for the class, then tag fields are created and the accessor uses
3811   * the tag fields. If not, tag storage is created separately and accessed via the field number.
3812   * ISSUE? This flag is not currently tested. (markro)
3813   *
3814   * <p>Accessors are also created for each visible superclass field that is not hidden by a field
3815   * in this class. These accessors just call the superclasses accessor.
3816   *
3817   * <p>Any accessors created are added to the class.
3818   *
3819   * @param gen class to check for fields
3820   */
3821  void create_tag_accessors(ClassGen gen) {
3822
3823    String classname = gen.getClassName();
3824
3825    Set<String> field_set = new HashSet<>();
3826    Map<Field, Integer> field_map = build_field_map(gen.getJavaClass());
3827
3828    // Build accessors for all fields declared in this class
3829    for (Field f : gen.getFields()) {
3830
3831      assert !field_set.contains(f.getName()) : f.getName() + "-" + classname;
3832      field_set.add(f.getName());
3833
3834      // skip primitive fields
3835      if (!is_primitive(f.getType())) {
3836        continue;
3837      }
3838
3839      MethodGen get_method;
3840      MethodGen set_method;
3841      if (f.isStatic()) {
3842        String full_name = full_name(orig_class, f);
3843        get_method = create_get_tag(gen, f, static_field_id.get(full_name));
3844        set_method = create_set_tag(gen, f, static_field_id.get(full_name));
3845      } else {
3846        get_method = create_get_tag(gen, f, field_map.get(f));
3847        set_method = create_set_tag(gen, f, field_map.get(f));
3848      }
3849      gen.addMethod(get_method.getMethod());
3850      gen.addMethod(set_method.getMethod());
3851    }
3852
3853    // Build accessors for each field declared in a superclass that is
3854    // is not shadowed in a subclass
3855    JavaClass[] super_classes;
3856    try {
3857      super_classes = gen.getJavaClass().getSuperClasses();
3858    } catch (Exception e) {
3859      throw new Error(e);
3860    }
3861    for (JavaClass super_class : super_classes) {
3862      for (Field f : super_class.getFields()) {
3863        if (f.isPrivate()) {
3864          continue;
3865        }
3866        if (field_set.contains(f.getName())) {
3867          continue;
3868        }
3869        if (!is_primitive(f.getType())) {
3870          continue;
3871        }
3872
3873        field_set.add(f.getName());
3874        MethodGen get_method;
3875        MethodGen set_method;
3876        if (f.isStatic()) {
3877          String full_name = full_name(super_class, f);
3878          get_method = create_get_tag(gen, f, static_field_id.get(full_name));
3879          set_method = create_set_tag(gen, f, static_field_id.get(full_name));
3880        } else {
3881          get_method = create_get_tag(gen, f, field_map.get(f));
3882          set_method = create_set_tag(gen, f, field_map.get(f));
3883        }
3884        gen.addMethod(get_method.getMethod());
3885        gen.addMethod(set_method.getMethod());
3886      }
3887    }
3888  }
3889
3890  /**
3891   * Builds a Map that relates each field in jc and each of its superclasses to a unique offset. The
3892   * offset can be used to index into a tag array for this class. Instance fields are placed in the
3893   * returned map and static fields are placed in static map (shared between all classes).
3894   *
3895   * @param jc class to check for fields
3896   * @return field offset map
3897   */
3898  Map<Field, Integer> build_field_map(JavaClass jc) {
3899
3900    // Object doesn't have any primitive fields
3901    if (jc.getClassName().equals("java.lang.Object")) {
3902      return new LinkedHashMap<>();
3903    }
3904
3905    // Get the offsets for each field in the superclasses.
3906    JavaClass super_jc;
3907    try {
3908      super_jc = jc.getSuperClass();
3909    } catch (Exception e) {
3910      throw new Error("can't get superclass for " + jc, e);
3911    }
3912    Map<Field, Integer> field_map = build_field_map(super_jc);
3913    int offset = field_map.size();
3914
3915    // Determine the offset for each primitive field in the class
3916    // Also make sure the static_tags list is large enough for
3917    // of the tags.
3918    for (Field f : jc.getFields()) {
3919      if (!is_primitive(f.getType())) {
3920        continue;
3921      }
3922      if (f.isStatic()) {
3923        if (!in_jdk) {
3924          int min_size = static_field_id.size() + DCRuntime.max_jdk_static;
3925          while (DCRuntime.static_tags.size() <= min_size) DCRuntime.static_tags.add(null);
3926          static_field_id.put(full_name(jc, f), min_size);
3927        } else { // building jdk
3928          String full_name = full_name(jc, f);
3929          if (static_field_id.containsKey(full_name)) {
3930            // System.out.printf("Reusing static field %s value %d%n",
3931            //                    full_name, static_field_id.get(full_name));
3932          } else {
3933            // System.out.printf("Allocating new static field %s%n",
3934            //                    full_name);
3935            static_field_id.put(full_name, static_field_id.size() + 1);
3936          }
3937        }
3938      } else {
3939        field_map.put(f, offset);
3940        offset++;
3941      }
3942    }
3943
3944    return field_map;
3945  }
3946
3947  /**
3948   * Creates a get tag method for field f. The tag corresponding to field f will be pushed on the
3949   * tag stack.
3950   *
3951   * <pre>{@code
3952   * void <field>_<class>__$get_tag() {
3953   *   #if f.isStatic()
3954   *     DCRuntime.push_static_tag (tag_offset)
3955   *   #else
3956   *     DCRuntime.push_field_tag (this, tag_offset);
3957   * }
3958   * }</pre>
3959   *
3960   * @param gen class whose accessors are being built. Not necessarily the class declaring f (if f
3961   *     is inherited).
3962   * @param f field to build an accessor for
3963   * @param tag_offset offset of f in the tag storage for this field
3964   * @return the get tag method
3965   */
3966  MethodGen create_get_tag(ClassGen gen, Field f, int tag_offset) {
3967
3968    // Determine the method to call in DCRuntime.  Instance fields and static
3969    // fields are handled separately.  Also instance fields in special
3970    // classes that are created by the JVM are handled separately since only
3971    // in those classes can fields be read without being written (in java)
3972    String methodname = "push_field_tag";
3973    Type[] args = object_int;
3974    if (f.isStatic()) {
3975      methodname = "push_static_tag";
3976      args = integer_arg;
3977    } else if (is_uninit_class(gen.getClassName())) {
3978      methodname = "push_field_tag_null_ok";
3979    }
3980
3981    String classname = gen.getClassName();
3982    String accessor_name = tag_method_name(GET_TAG, classname, f.getName());
3983
3984    InstructionList il = new InstructionList();
3985
3986    if (!f.isStatic()) {
3987      il.append(InstructionFactory.createThis());
3988    }
3989    il.append(ifact.createConstant(tag_offset));
3990    il.append(dcr_call(methodname, Type.VOID, args));
3991    il.append(InstructionFactory.createReturn(Type.VOID));
3992
3993    int access_flags = f.getAccessFlags();
3994    if (gen.isInterface()) {
3995      // method in interface cannot be final
3996      access_flags &= ~Const.ACC_FINAL;
3997    } else {
3998      access_flags |= Const.ACC_FINAL;
3999    }
4000
4001    // Create the get accessor method
4002    MethodGen get_method =
4003        new MethodGen(
4004            access_flags,
4005            Type.VOID,
4006            Type.NO_ARGS,
4007            new String[] {},
4008            accessor_name,
4009            classname,
4010            il,
4011            pool);
4012    get_method.isPrivate(false);
4013    get_method.isProtected(false);
4014    get_method.isPublic(true);
4015    get_method.setMaxLocals();
4016    get_method.setMaxStack();
4017    // add_line_numbers(get_method, il);
4018
4019    return get_method;
4020  }
4021
4022  /**
4023   * Creates a set tag method for field f. The tag on the top of the tag stack will be popped off
4024   * and placed in the tag storeage corresponding to field
4025   *
4026   * <pre>{@code
4027   * void <field>_<class>__$set_tag() {
4028   *   #if f.isStatic()
4029   *     DCRuntime.pop_static_tag (tag_offset)
4030   *   #else
4031   *     DCRuntime.pop_field_tag (this, tag_offset);
4032   * }
4033   * }</pre>
4034   *
4035   * @param gen class whose accessors are being built. Not necessarily the class declaring f (if f
4036   *     is inherited).
4037   * @param f field to build an accessor for
4038   * @param tag_offset offset of f in the tag storage for this field
4039   * @return the set tag method
4040   */
4041  MethodGen create_set_tag(ClassGen gen, Field f, int tag_offset) {
4042
4043    String methodname = "pop_field_tag";
4044    Type[] args = object_int;
4045    if (f.isStatic()) {
4046      methodname = "pop_static_tag";
4047      args = integer_arg;
4048    }
4049
4050    String classname = gen.getClassName();
4051    String setter_name = tag_method_name(SET_TAG, classname, f.getName());
4052
4053    InstructionList il = new InstructionList();
4054
4055    if (!f.isStatic()) {
4056      il.append(InstructionFactory.createThis());
4057    }
4058    il.append(ifact.createConstant(tag_offset));
4059    il.append(dcr_call(methodname, Type.VOID, args));
4060    il.append(InstructionFactory.createReturn(Type.VOID));
4061
4062    int access_flags = f.getAccessFlags();
4063    if (gen.isInterface()) {
4064      // method in interface cannot be final
4065      access_flags &= ~Const.ACC_FINAL;
4066    } else {
4067      access_flags |= Const.ACC_FINAL;
4068    }
4069
4070    // Create the setter method
4071    MethodGen set_method =
4072        new MethodGen(
4073            access_flags,
4074            Type.VOID,
4075            Type.NO_ARGS,
4076            new String[] {},
4077            setter_name,
4078            classname,
4079            il,
4080            pool);
4081    set_method.setMaxLocals();
4082    set_method.setMaxStack();
4083    // add_line_numbers(set_method, il);
4084
4085    return set_method;
4086  }
4087
4088  /**
4089   * Adds the DCompInstrumented interface to the given class. Adds the following method to the
4090   * class, so that it implements the DCompInstrumented interface:
4091   *
4092   * <pre>{@code
4093   * public boolean equals_dcomp_instrumented(Object o) {
4094   *   return this.equals(o, null);
4095   * }
4096   * }</pre>
4097   *
4098   * The method does nothing except call the instrumented equals method (boolean equals(Object,
4099   * DCompMarker)).
4100   *
4101   * @param gen class to add interface to
4102   */
4103  void add_dcomp_interface(ClassGen gen) {
4104    gen.addInterface(instrumentation_interface);
4105    debugInstrument.log("Added interface DCompInstrumented%n");
4106
4107    InstructionList il = new InstructionList();
4108    int access_flags = Const.ACC_PUBLIC;
4109    if (gen.isInterface()) {
4110      access_flags |= Const.ACC_ABSTRACT;
4111    }
4112    MethodGen method =
4113        new MethodGen(
4114            access_flags,
4115            Type.BOOLEAN,
4116            new Type[] {Type.OBJECT},
4117            new String[] {"obj"},
4118            "equals_dcomp_instrumented",
4119            gen.getClassName(),
4120            il,
4121            pool);
4122
4123    il.append(InstructionFactory.createLoad(Type.OBJECT, 0)); // load this
4124    il.append(InstructionFactory.createLoad(Type.OBJECT, 1)); // load obj
4125    il.append(new ACONST_NULL()); // use null for marker
4126    il.append(
4127        ifact.createInvoke(
4128            gen.getClassName(),
4129            "equals",
4130            Type.BOOLEAN,
4131            new Type[] {Type.OBJECT, dcomp_marker},
4132            Const.INVOKEVIRTUAL));
4133    il.append(InstructionFactory.createReturn(Type.BOOLEAN));
4134    method.setMaxStack();
4135    method.setMaxLocals();
4136    gen.addMethod(method.getMethod());
4137    il.dispose();
4138  }
4139
4140  /**
4141   * Adds the following method to a class:
4142   *
4143   * <pre>{@code
4144   * public boolean equals (Object obj) {
4145   *   return super.equals(obj);
4146   * }
4147   * }</pre>
4148   *
4149   * Must only be called if the Object equals method has not been overridden; if the equals method
4150   * is already defined in the class, a ClassFormatError will result because of the duplicate
4151   * method.
4152   *
4153   * @param gen class to add method to
4154   */
4155  void add_equals_method(ClassGen gen) {
4156    InstructionList il = new InstructionList();
4157    int access_flags = Const.ACC_PUBLIC;
4158    if (gen.isInterface()) {
4159      access_flags |= Const.ACC_ABSTRACT;
4160    }
4161    MethodGen method =
4162        new MethodGen(
4163            access_flags,
4164            Type.BOOLEAN,
4165            new Type[] {Type.OBJECT},
4166            new String[] {"obj"},
4167            "equals",
4168            gen.getClassName(),
4169            il,
4170            pool);
4171
4172    il.append(InstructionFactory.createLoad(Type.OBJECT, 0)); // load this
4173    il.append(InstructionFactory.createLoad(Type.OBJECT, 1)); // load obj
4174    il.append(
4175        ifact.createInvoke(
4176            gen.getSuperclassName(),
4177            "equals",
4178            Type.BOOLEAN,
4179            new Type[] {Type.OBJECT},
4180            Const.INVOKESPECIAL));
4181    il.append(InstructionFactory.createReturn(Type.BOOLEAN));
4182    method.setMaxStack();
4183    method.setMaxLocals();
4184    gen.addMethod(method.getMethod());
4185    il.dispose();
4186  }
4187
4188  /**
4189   * Marks the class as implementing various object methods (currently clone and toString). Callers
4190   * will call the instrumented version of the method if it exists, otherwise they will call the
4191   * uninstrumented version.
4192   *
4193   * @param gen class to check
4194   */
4195  void handle_object(ClassGen gen) {
4196    Method cl = gen.containsMethod("clone", "()Ljava/lang/Object;");
4197    if (cl != null) {
4198      gen.addInterface(Signatures.addPackage(dcomp_prefix, "DCompClone"));
4199    }
4200
4201    Method ts = gen.containsMethod("toString", "()Ljava/lang/String;");
4202    if (ts != null) {
4203      gen.addInterface(Signatures.addPackage(dcomp_prefix, "DCompToString"));
4204    }
4205  }
4206
4207  /**
4208   * Returns a field tag accessor method name.
4209   *
4210   * @param type "get_tag" or "set_tag"
4211   * @param classname name of class
4212   * @param fname name of field
4213   * @return name of tag accessor method
4214   */
4215  static String tag_method_name(String type, String classname, String fname) {
4216    return fname + "_" + classname.replace('.', '_') + "__$" + type;
4217  }
4218
4219  /**
4220   * Add a dcomp marker argument to indicate this is the instrumented version of the method.
4221   *
4222   * @param mg method to ard dcomp marker to
4223   */
4224  void add_dcomp_arg(MethodGen mg) {
4225
4226    // Don't modify main or the JVM won't be able to find it.
4227    if (BcelUtil.isMain(mg)) {
4228      return;
4229    }
4230
4231    // Don't modify class init methods, they don't take arguments
4232    if (BcelUtil.isClinit(mg)) {
4233      return;
4234    }
4235
4236    // Add the dcomp marker argument to indicate this is the
4237    // instrumented version of the method.
4238    addNewParameter(mg, "marker", dcomp_marker);
4239  }
4240
4241  /**
4242   * Returns whether or not the method is defined in Object.
4243   *
4244   * @param methodName method to check
4245   * @param argTypes array of argument types to method
4246   * @return true if method is member of Object
4247   */
4248  @Pure
4249  boolean is_object_method(String methodName, Type[] argTypes) {
4250    for (MethodDef md : obj_methods) {
4251      if (md.equals(methodName, argTypes)) {
4252        return true;
4253      }
4254    }
4255    return false;
4256  }
4257
4258  /**
4259   * Returns whether or not the class is one of those that has values initialized by the JVM or
4260   * native methods.
4261   *
4262   * @param classname class to check
4263   * @return true if classname has members that are uninitialized
4264   */
4265  @Pure
4266  boolean is_uninit_class(String classname) {
4267
4268    for (String u_name : uninit_classes) {
4269      if (u_name.equals(classname)) {
4270        return true;
4271      }
4272    }
4273
4274    return false;
4275  }
4276
4277  /**
4278   * Creates a method with a DcompMarker argument that does nothing but call the corresponding
4279   * method without the DCompMarker argument. (Currently, only used for ? va main.)
4280   *
4281   * @param mg MethodGen of method to create stub for
4282   * @return the stub
4283   */
4284  MethodGen create_dcomp_stub(MethodGen mg) {
4285
4286    InstructionList il = new InstructionList();
4287    Type returnType = mg.getReturnType();
4288
4289    // if mg is dynamic, Push 'this' on the stack
4290    int offset = 0;
4291    if (!mg.isStatic()) {
4292      il.append(InstructionFactory.createThis());
4293      offset = 1;
4294    }
4295
4296    // push each argument on the stack
4297    for (Type argType : mg.getArgumentTypes()) {
4298      il.append(InstructionFactory.createLoad(argType, offset));
4299      offset += argType.getSize();
4300    }
4301
4302    // Call the method
4303    short kind = Const.INVOKEVIRTUAL;
4304    if (mg.isStatic()) {
4305      kind = Const.INVOKESTATIC;
4306    }
4307    il.append(
4308        ifact.createInvoke(
4309            mg.getClassName(), mg.getName(), returnType, mg.getArgumentTypes(), kind));
4310
4311    il.append(InstructionFactory.createReturn(returnType));
4312
4313    // Create the method
4314    Type[] argTypes = BcelUtil.postpendToArray(mg.getArgumentTypes(), dcomp_marker);
4315    String[] argNames = addString(mg.getArgumentNames(), "marker");
4316    MethodGen dcomp_mg =
4317        new MethodGen(
4318            mg.getAccessFlags(),
4319            returnType,
4320            argTypes,
4321            argNames,
4322            mg.getName(),
4323            mg.getClassName(),
4324            il,
4325            pool);
4326    dcomp_mg.setMaxLocals();
4327    dcomp_mg.setMaxStack();
4328
4329    return dcomp_mg;
4330  }
4331
4332  /**
4333   * Writes the static map from field names to their integer ids to the specified file. Can be read
4334   * with restore_static_field_id. Each line contains a key/value combination with a blank
4335   * separating them.
4336   *
4337   * @param file where to write the static field ids
4338   * @throws IOException if unable to find or open the file
4339   */
4340  static void save_static_field_id(File file) throws IOException {
4341
4342    PrintStream ps = new PrintStream(file);
4343    for (Map.Entry<@KeyFor("static_field_id") String, Integer> entry : static_field_id.entrySet()) {
4344      ps.printf("%s  %d%n", entry.getKey(), entry.getValue());
4345    }
4346    ps.close();
4347  }
4348
4349  /**
4350   * Restores the static map from the specified file.
4351   *
4352   * @param file where to read the static field ids
4353   * @throws IOException if unable to create an EntryReader
4354   * @see #save_static_field_id(File)
4355   */
4356  static void restore_static_field_id(File file) throws IOException {
4357    try (EntryReader er = new EntryReader(file, "UTF-8")) {
4358      for (String line : er) {
4359        String[] key_val = line.split("  *");
4360        assert !static_field_id.containsKey(key_val[0]) : key_val[0] + " " + key_val[1];
4361        static_field_id.put(key_val[0], Integer.valueOf(key_val[1]));
4362        // System.out.printf("Adding %s %s to static map%n", key_val[0],
4363        //                   key_val[1]);
4364      }
4365    }
4366  }
4367
4368  /**
4369   * Return the fully qualified fieldname of the specified field.
4370   *
4371   * @param jc class containing the field
4372   * @param f the field
4373   * @return string containing the fully qualified name
4374   */
4375  protected String full_name(JavaClass jc, Field f) {
4376    return jc.getClassName() + "." + f.getName();
4377  }
4378
4379  /**
4380   * Return simplified name of a method. Both exceptions and annotations are removed.
4381   *
4382   * @param m the method
4383   * @return string containing the simplified method name
4384   */
4385  protected String simplify_method_name(Method m) {
4386    // Remove exceptions from the full method name
4387    String full_name = m.toString().replaceFirst("\\s*throws.*", "");
4388    // Remove annotations from full method name
4389    return full_name.replaceAll(" \\[.*\\]", "");
4390  }
4391}