/** Instrument the specified method to replace mapped calls. */ public void instrument_method(Method m, MethodGen mg) { // Loop through each instruction, making substitutions InstructionList il = mg.getInstructionList(); for (InstructionHandle ih = il.getStart(); ih != null; ) { if (debug_instrument_inst.enabled()) { debug_instrument_inst.log("instrumenting instruction %s%n", ih); // ih.getInstruction().toString(pool.getConstantPool())); } InstructionList new_il = null; // Remember the next instruction to process InstructionHandle next_ih = ih.getNext(); // Get the translation for this instruction (if any) new_il = xform_inst(mg, ih.getInstruction()); if (debug_instrument_inst.enabled()) debug_instrument_inst.log(" new inst: %s%n", new_il); // If this instruction was modified, replace it with the new // instruction list. If this instruction was the target of any // jumps or line numbers , replace them with the first // instruction in the new list replace_instructions(il, ih, new_il); ih = next_ih; } }
/** Dumps out the map list to the debug_map logger * */ public static void dump_map_list() { if (debug_map.enabled()) { for (MethodMapInfo mmi : map_list) { debug_map.log("Class re '%s': %n", mmi.class_regex); for (MethodDef md : mmi.map.keySet()) { MethodInfo mi = mmi.map.get(md); debug_map.log(" %s - %s [%d replacements]%n", md, mi.method_class, mi.cnt); } } } }
/** * Transforms invoke instructions that match the specified list for this class to call the * specified static call instead. */ private InstructionList xform_inst(MethodGen mg, Instruction inst) { switch (inst.getOpcode()) { case Constants.INVOKESTATIC: { InstructionList il = new InstructionList(); INVOKESTATIC is = (INVOKESTATIC) inst; String cname = is.getClassName(pgen); String mname = is.getMethodName(pgen); Type[] args = is.getArgumentTypes(pgen); MethodDef orig = new MethodDef(cname + "." + mname, args); MethodInfo call = method_map.get(orig); if (call != null) { call.cnt++; String classname = call.method_class; String methodname = mname; debug_map.log( "%s.%s: Replacing method %s.%s (%s) with %s.%s%n", mg.getClassName(), mg.getName(), cname, mname, UtilMDE.join(args, ", "), classname, methodname); il.append( ifact.createInvoke( classname, methodname, is.getReturnType(pgen), args, Constants.INVOKESTATIC)); } return (il); } case Constants.INVOKEVIRTUAL: { InstructionList il = new InstructionList(); INVOKEVIRTUAL iv = (INVOKEVIRTUAL) inst; String cname = iv.getClassName(pgen); String mname = iv.getMethodName(pgen); Type[] args = iv.getArgumentTypes(pgen); Type instance_type = iv.getReferenceType(pgen); Type[] new_args = BCELUtil.insert_type(instance_type, args); MethodDef orig = new MethodDef(cname + "." + mname, args); if (debug_class) System.out.printf("looking for %s in map %s%n", orig, method_map); MethodInfo call = method_map.get(orig); if (call != null) { call.cnt++; String classname = call.method_class; String methodname = mname; debug_map.log( "Replacing method %s.%s (%s) with %s.%s%n", cname, mname, ArraysMDE.toString(args), classname, methodname); il.append( ifact.createInvoke( classname, methodname, iv.getReturnType(pgen), new_args, Constants.INVOKESTATIC)); } return (il); } default: return (null); } }
/** * Given another class, return a transformed version of the class which replaces specified calls * with alternative static implementations */ public byte[] transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { // debug = className.equals ("chicory/Test"); String fullClassName = className.replace("/", "."); debug_transform.log("In Transform: class = %s%n", className); // Don't instrument boot classes. We only want to instrument // user classes classpath. // Most boot classes have the null loader, // but some generated classes (such as those in sun.reflect) will // have a non-null loader. Some of these have a null parent loader, // but some do not. The check for the sun.reflect package is a hack // to catch all of these. A more consistent mechanism to determine // boot classes would be preferrable. if (loader == null) { debug_transform.log("ignoring system class %s, class loader == null", fullClassName); return (null); } else if (loader.getParent() == null) { debug_transform.log("ignoring system class %s, parent loader == null\n", fullClassName); return (null); } else if (fullClassName.startsWith("sun.reflect")) { debug_transform.log("ignoring system class %s, in sun.reflect package", fullClassName); return (null); } else if (fullClassName.startsWith("com.sun")) { System.out.printf("Class from com.sun package %s with nonnull loaders\n", fullClassName); } // Don't intrument our code if (className.startsWith("randoop.")) { debug_transform.log("Not considering randoop class %s%n", fullClassName); return (null); } // Look for match with specified regular expressions for class method_map = null; debug_class = false; for (MethodMapInfo mmi : map_list) { if (mmi.class_regex.matcher(className).matches()) { if (false && className.startsWith("RandoopTest")) debug_class = true; if (debug_class) System.out.printf("Classname %s matches re %s%n", className, mmi.class_regex); method_map = mmi.map; break; } } if (method_map == null) return null; debug_transform.log( "transforming class %s, loader %s - %s%n", className, loader, loader.getParent()); // Parse the bytes of the classfile, die on any errors JavaClass c = null; ClassParser parser = new ClassParser(new ByteArrayInputStream(classfileBuffer), className); try { c = parser.parse(); } catch (Exception e) { throw new RuntimeException("Unexpected error", e); } try { // Get the class information ClassGen cg = new ClassGen(c); ifact = new InstructionFactory(cg); map_calls(cg, className, loader); JavaClass njc = cg.getJavaClass(); if (debug) njc.dump("/tmp/ret/" + njc.getClassName() + ".class"); if (true) { return (cg.getJavaClass().getBytes()); } else { debug_transform.log("not including class %s (filtered out)", className); return null; } } catch (Throwable e) { out.format("Unexpected error %s in transform", e); e.printStackTrace(); return (null); } }