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