/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.tools.asm; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodAdapter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.Locale; import java.math.BigDecimal; import java.math.BigInteger; import java.math.MathContext; import java.math.RoundingMode; import org.jikesrvm.classloader.RVMClassLoader; import org.jikesrvm.classloader.BootstrapClassLoader; import org.vmmagic.pragma.Inline; import org.vmmagic.pragma.NoEscapes; import org.vmmagic.pragma.Pure; import org.vmmagic.pragma.RuntimePure; import org.vmmagic.pragma.Uninterruptible; /** * Add annotations to classes using the ASM framework. */ public final class AnnotationAdder { /** Verbose debug information */ private static final boolean VERBOSE = false; /** A triple of string constants used to identify an element to annotate */ private static final class ElementTriple { private final String className; private final String elementName; private final String signature; ElementTriple(String className, String elementName, String signature) { this.className = className; this.elementName = elementName; this.signature = signature; } String getClassName() { return className; } public int hashCode() { return className.hashCode() + elementName.hashCode() + signature.hashCode(); } public boolean equals(Object other) { if (other instanceof ElementTriple) { ElementTriple triple = (ElementTriple)other; return className.equals(triple.className) && elementName.equals(triple.elementName) && signature.equals(triple.signature); } else { return false; } } public String toString() { return className + " " + elementName + " " + signature; } } /** * Elements we're looking to adapt and the annotations we want adding to them */ private static final Map<AnnotatedElement, Set<Class<? extends Annotation>>> thingsToAnnotate = new HashMap<AnnotatedElement, Set<Class<? extends Annotation>>>(); /** * More elements we're looking to adapt and the annotations we want adding to them */ private static final Map<ElementTriple, Set<Class<? extends Annotation>>> thingsToAnnotate2 = new HashMap<ElementTriple, Set<Class<? extends Annotation>>>(); /** * Name of class library */ private static String classLibrary; /** * Destination directory for annotated classes */ private static String destinationDir; /** * Elements that have been annotated */ private static final Set<AnnotatedElement> annotatedElements = new HashSet<AnnotatedElement>(); /** * More elements that have been annotated */ private static final Set<ElementTriple> annotatedElements2 = new HashSet<ElementTriple>(); /** * Add annotation to element * @param ann annotation to add * @param elem element to add it to */ private static void addToAdapt(Class<? extends Annotation> ann, AnnotatedElement elem) { if (elem == null) throw new Error("Can't adapt a null element"); if (ann == null) throw new Error("Can't annotate with null"); Set<Class<? extends Annotation>> set = thingsToAnnotate.get(elem); if (set == null) { set = new HashSet<Class<? extends Annotation>>(); } set.add(ann); thingsToAnnotate.put(elem, set); } /** * Add annotation to element * @param ann annotation to add * @param elem element to add it to */ private static void addToAdapt(Class<? extends Annotation> ann, String className, String methodName, String signature) { if (ann == null) throw new Error("Can't annotate with null"); ElementTriple triple = new ElementTriple(className, methodName, signature); Set<Class<? extends Annotation>> set = thingsToAnnotate2.get(triple); if (set == null) { set = new HashSet<Class<? extends Annotation>>(); } set.add(ann); thingsToAnnotate2.put(triple, set); } /** Set up things to adapt */ private static void setup() { try { if (classLibrary.toLowerCase().equals("gnu classpath")) { // java.lang.Throwable for (Constructor c : Throwable.class.getConstructors()) { addToAdapt(NoEscapes.class, c); } // java.nio.Buffer addToAdapt(Inline.class, "java/nio/Buffer", "<init>", "(IIIILgnu/classpath/Pointer;)V"); // gnu.java.nio.charset.ByteEncodeLoopHelper addToAdapt(Inline.class, "gnu/java/nio/charset/ByteEncodeLoopHelper", "normalEncodeLoop", "(Ljava/nio/CharBuffer;Ljava/nio/ByteBuffer;)Ljava/nio/charset/CoderResult;"); addToAdapt(Inline.class, "gnu/java/nio/charset/ByteEncodeLoopHelper", "arrayEncodeLoop", "(Ljava/nio/CharBuffer;Ljava/nio/ByteBuffer;)Ljava/nio/charset/CoderResult;"); // gnu.java.nio.charset.ByteDecodeLoopHelper addToAdapt(Inline.class, "gnu/java/nio/charset/ByteDecodeLoopHelper", "normalDecodeLoop", "(Ljava/nio/ByteBuffer;Ljava/nio/CharBuffer;)Ljava/nio/charset/CoderResult;"); addToAdapt(Inline.class, "gnu/java/nio/charset/ByteDecodeLoopHelper", "arrayDecodeLoop", "(Ljava/nio/ByteBuffer;Ljava/nio/CharBuffer;)Ljava/nio/charset/CoderResult;"); } // Proxy addToAdapt(RuntimePure.class, Proxy.class.getMethod("getProxyClass", new Class[]{ClassLoader.class, Class[].class})); // BigDecimal addToAdapt(Pure.class, BigDecimal.class.getMethod("abs", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("abs", new Class[]{MathContext.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("add", new Class[]{BigDecimal.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("add", new Class[]{BigDecimal.class, MathContext.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("byteValueExact", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("compareTo", new Class[]{BigDecimal.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("divide", new Class[]{BigDecimal.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("divide", new Class[]{BigDecimal.class, int.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("divide", new Class[]{BigDecimal.class, int.class, int.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("divide", new Class[]{BigDecimal.class, int.class, RoundingMode.class})); //addToAdapt(Pure.class, BigDecimal.class.getMethod("divide", new Class[]{BigDecimal.class, MathContext.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("doubleValue", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("equals", new Class[]{Object.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("floatValue", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("hashCode", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("intValue", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("intValueExact", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("longValue", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("longValueExact", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("max", new Class[]{BigDecimal.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("min", new Class[]{BigDecimal.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("movePointLeft", new Class[]{int.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("movePointRight", new Class[]{int.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("multiply", new Class[]{BigDecimal.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("multiply", new Class[]{BigDecimal.class, MathContext.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("negate", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("negate", new Class[]{MathContext.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("plus", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("plus", new Class[]{MathContext.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("pow", new Class[]{int.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("pow", new Class[]{int.class, MathContext.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("precision", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("remainder", new Class[]{BigDecimal.class})); //addToAdapt(Pure.class, BigDecimal.class.getMethod("remainder", new Class[]{BigDecimal.class, MathContext.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("round", new Class[]{MathContext.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("scale", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("scaleByPowerOfTen", new Class[]{int.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("setScale", new Class[]{int.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("setScale", new Class[]{int.class, int.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("shortValue", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("shortValueExact", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("signum", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("stripTrailingZeros", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("subtract", new Class[]{BigDecimal.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("subtract", new Class[]{BigDecimal.class, MathContext.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("toBigInteger", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("toBigIntegerExact", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("toEngineeringString", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("toPlainString", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("toString", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("ulp", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("unscaledValue", new Class[0])); addToAdapt(Pure.class, BigDecimal.class.getMethod("valueOf", new Class[]{double.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("valueOf", new Class[]{long.class})); addToAdapt(Pure.class, BigDecimal.class.getMethod("valueOf", new Class[]{long.class, int.class})); // BigInteger addToAdapt(Pure.class, BigInteger.class.getMethod("abs", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("add", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("and", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("andNot", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("bitCount", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("bitLength", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("clearBit", new Class[]{int.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("compareTo", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("divide", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("divideAndRemainder", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("doubleValue", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("equals", new Class[]{Object.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("flipBit", new Class[]{int.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("floatValue", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("gcd", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("getLowestSetBit", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("hashCode", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("intValue", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("isProbablePrime", new Class[]{int.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("longValue", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("max", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("min", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("mod", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("modInverse", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("modPow", new Class[]{BigInteger.class, BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("multiply", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("negate", new Class[0])); //addToAdapt(Pure.class, BigInteger.class.getMethod("nextProbablePrime", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("not", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("or", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("pow", new Class[]{int.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("remainder", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("setBit", new Class[]{int.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("shiftLeft", new Class[]{int.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("shiftRight", new Class[]{int.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("signum", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("subtract", new Class[]{BigInteger.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("testBit", new Class[]{int.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("toByteArray", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("toString", new Class[0])); addToAdapt(Pure.class, BigInteger.class.getMethod("toString", new Class[]{int.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("valueOf", new Class[]{long.class})); addToAdapt(Pure.class, BigInteger.class.getMethod("xor", new Class[]{BigInteger.class})); // Double addToAdapt(Pure.class, Double.class.getMethod("byteValue", new Class[0])); addToAdapt(Pure.class, Double.class.getMethod("compare", new Class[]{double.class, double.class})); addToAdapt(Pure.class, Double.class.getMethod("compareTo", new Class[]{Double.class})); addToAdapt(Pure.class, Double.class.getMethod("doubleValue", new Class[0])); addToAdapt(Pure.class, Double.class.getMethod("equals", new Class[]{Object.class})); addToAdapt(Pure.class, Double.class.getMethod("floatValue", new Class[0])); addToAdapt(Pure.class, Double.class.getMethod("hashCode", new Class[0])); addToAdapt(Pure.class, Double.class.getMethod("intValue", new Class[0])); addToAdapt(Pure.class, Double.class.getMethod("longValue", new Class[0])); addToAdapt(Pure.class, Double.class.getMethod("parseDouble", new Class[]{String.class})); addToAdapt(Pure.class, Double.class.getMethod("shortValue", new Class[0])); addToAdapt(Pure.class, Double.class.getMethod("toHexString", new Class[]{double.class})); addToAdapt(Pure.class, Double.class.getMethod("toString", new Class[0])); addToAdapt(Pure.class, Double.class.getMethod("toString", new Class[]{double.class})); addToAdapt(Pure.class, Double.class.getMethod("valueOf", new Class[]{double.class})); addToAdapt(Pure.class, Double.class.getMethod("valueOf", new Class[]{String.class})); // Float addToAdapt(Pure.class, Float.class.getMethod("byteValue", new Class[0])); addToAdapt(Pure.class, Float.class.getMethod("compare", new Class[]{float.class, float.class})); addToAdapt(Pure.class, Float.class.getMethod("compareTo", new Class[]{Float.class})); addToAdapt(Pure.class, Float.class.getMethod("doubleValue", new Class[0])); addToAdapt(Pure.class, Float.class.getMethod("equals", new Class[]{Object.class})); addToAdapt(Pure.class, Float.class.getMethod("floatValue", new Class[0])); addToAdapt(Pure.class, Float.class.getMethod("hashCode", new Class[0])); addToAdapt(Pure.class, Float.class.getMethod("intValue", new Class[0])); addToAdapt(Pure.class, Float.class.getMethod("longValue", new Class[0])); addToAdapt(Pure.class, Float.class.getMethod("parseFloat", new Class[]{String.class})); addToAdapt(Pure.class, Float.class.getMethod("shortValue", new Class[0])); addToAdapt(Pure.class, Float.class.getMethod("toHexString", new Class[]{float.class})); addToAdapt(Pure.class, Float.class.getMethod("toString", new Class[0])); addToAdapt(Pure.class, Float.class.getMethod("toString", new Class[]{float.class})); addToAdapt(Pure.class, Float.class.getMethod("valueOf", new Class[]{float.class})); addToAdapt(Pure.class, Float.class.getMethod("valueOf", new Class[]{String.class})); // Integer addToAdapt(Pure.class, Integer.class.getMethod("bitCount", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("byteValue", new Class[0])); addToAdapt(Pure.class, Integer.class.getMethod("compareTo", new Class[]{Integer.class})); addToAdapt(Pure.class, Integer.class.getMethod("decode", new Class[]{String.class})); addToAdapt(Pure.class, Integer.class.getMethod("doubleValue", new Class[0])); addToAdapt(Pure.class, Integer.class.getMethod("equals", new Class[]{Object.class})); addToAdapt(Pure.class, Integer.class.getMethod("floatValue", new Class[0])); addToAdapt(Pure.class, Integer.class.getMethod("hashCode", new Class[0])); addToAdapt(Pure.class, Integer.class.getMethod("highestOneBit", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("intValue", new Class[0])); addToAdapt(Pure.class, Integer.class.getMethod("longValue", new Class[0])); addToAdapt(Pure.class, Integer.class.getMethod("lowestOneBit", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("numberOfLeadingZeros", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("numberOfTrailingZeros", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("parseInt", new Class[]{String.class})); addToAdapt(Pure.class, Integer.class.getMethod("parseInt", new Class[]{String.class, int.class})); addToAdapt(Pure.class, Integer.class.getMethod("reverse", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("reverseBytes", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("rotateLeft", new Class[]{int.class, int.class})); addToAdapt(Pure.class, Integer.class.getMethod("rotateRight", new Class[]{int.class, int.class})); addToAdapt(Pure.class, Integer.class.getMethod("shortValue", new Class[0])); addToAdapt(Pure.class, Integer.class.getMethod("signum", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("toBinaryString", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("toHexString", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("toOctalString", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("toString", new Class[0])); addToAdapt(Pure.class, Integer.class.getMethod("toString", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("toString", new Class[]{int.class, int.class})); addToAdapt(Pure.class, Integer.class.getMethod("valueOf", new Class[]{int.class})); addToAdapt(Pure.class, Integer.class.getMethod("valueOf", new Class[]{String.class})); addToAdapt(Pure.class, Integer.class.getMethod("valueOf", new Class[]{String.class, int.class})); // Long addToAdapt(Pure.class, Long.class.getMethod("bitCount", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("byteValue", new Class[0])); addToAdapt(Pure.class, Long.class.getMethod("compareTo", new Class[]{Long.class})); addToAdapt(Pure.class, Long.class.getMethod("decode", new Class[]{String.class})); addToAdapt(Pure.class, Long.class.getMethod("doubleValue", new Class[0])); addToAdapt(Pure.class, Long.class.getMethod("equals", new Class[]{Object.class})); addToAdapt(Pure.class, Long.class.getMethod("floatValue", new Class[0])); addToAdapt(Pure.class, Long.class.getMethod("hashCode", new Class[0])); addToAdapt(Pure.class, Long.class.getMethod("highestOneBit", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("intValue", new Class[0])); addToAdapt(Pure.class, Long.class.getMethod("longValue", new Class[0])); addToAdapt(Pure.class, Long.class.getMethod("lowestOneBit", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("numberOfLeadingZeros", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("numberOfTrailingZeros", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("parseLong", new Class[]{String.class})); addToAdapt(Pure.class, Long.class.getMethod("parseLong", new Class[]{String.class, int.class})); addToAdapt(Pure.class, Long.class.getMethod("reverse", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("reverseBytes", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("rotateLeft", new Class[]{long.class, int.class})); addToAdapt(Pure.class, Long.class.getMethod("rotateRight", new Class[]{long.class, int.class})); addToAdapt(Pure.class, Long.class.getMethod("shortValue", new Class[0])); addToAdapt(Pure.class, Long.class.getMethod("signum", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("toBinaryString", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("toHexString", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("toOctalString", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("toString", new Class[0])); addToAdapt(Pure.class, Long.class.getMethod("toString", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("toString", new Class[]{long.class, int.class})); addToAdapt(Pure.class, Long.class.getMethod("valueOf", new Class[]{long.class})); addToAdapt(Pure.class, Long.class.getMethod("valueOf", new Class[]{String.class})); addToAdapt(Pure.class, Long.class.getMethod("valueOf", new Class[]{String.class, int.class})); // Enum if (classLibrary.toLowerCase().equals("harmony")) { addToAdapt(Uninterruptible.class, Enum.class.getMethod("ordinal", new Class[0])); addToAdapt(Uninterruptible.class, Enum.class.getMethod("name", new Class[0])); } // String if (!classLibrary.toLowerCase().equals("harmony")) { addToAdapt(Pure.class, String.class.getMethod("charAt", new Class[]{int.class})); addToAdapt(Pure.class, String.class.getMethod("getBytes", new Class[]{String.class})); addToAdapt(Pure.class, String.class.getMethod("getBytes", new Class[0])); addToAdapt(Pure.class, String.class.getMethod("equals", new Class[]{Object.class})); addToAdapt(Pure.class, String.class.getMethod("equalsIgnoreCase", new Class[]{String.class})); addToAdapt(Pure.class, String.class.getMethod("compareTo", new Class[]{String.class})); addToAdapt(Pure.class, String.class.getMethod("compareToIgnoreCase", new Class[]{String.class})); addToAdapt(Pure.class, String.class.getMethod("regionMatches", new Class[]{int.class, String.class, int.class, int.class})); addToAdapt(Pure.class, String.class.getMethod("regionMatches", new Class[]{boolean.class, int.class, String.class, int.class, int.class})); addToAdapt(Pure.class, String.class.getMethod("startsWith", new Class[]{String.class, int.class})); addToAdapt(Pure.class, String.class.getMethod("startsWith", new Class[]{String.class})); addToAdapt(Pure.class, String.class.getMethod("endsWith", new Class[]{String.class})); addToAdapt(Pure.class, String.class.getMethod("hashCode", new Class[0])); addToAdapt(Pure.class, String.class.getMethod("indexOf", new Class[]{int.class})); addToAdapt(Pure.class, String.class.getMethod("indexOf", new Class[]{int.class, int.class})); addToAdapt(Pure.class, String.class.getMethod("lastIndexOf", new Class[]{int.class})); addToAdapt(Pure.class, String.class.getMethod("lastIndexOf", new Class[]{int.class, int.class})); addToAdapt(Pure.class, String.class.getMethod("indexOf", new Class[]{String.class})); addToAdapt(Pure.class, String.class.getMethod("indexOf", new Class[]{String.class, int.class})); addToAdapt(Pure.class, String.class.getMethod("lastIndexOf", new Class[]{String.class})); addToAdapt(Pure.class, String.class.getMethod("lastIndexOf", new Class[]{String.class, int.class})); addToAdapt(Pure.class, String.class.getMethod("substring", new Class[]{int.class})); addToAdapt(Pure.class, String.class.getMethod("substring", new Class[]{int.class, int.class})); addToAdapt(Pure.class, String.class.getMethod("subSequence", new Class[]{int.class, int.class})); addToAdapt(Pure.class, String.class.getMethod("concat", new Class[]{String.class})); addToAdapt(Pure.class, String.class.getMethod("replace", new Class[]{char.class, char.class})); addToAdapt(Pure.class, String.class.getMethod("substring", new Class[]{int.class})); addToAdapt(Pure.class, String.class.getMethod("matches", new Class[]{String.class})); addToAdapt(Pure.class, String.class.getMethod("replaceFirst", new Class[]{String.class, String.class})); addToAdapt(Pure.class, String.class.getMethod("replaceAll", new Class[]{String.class, String.class})); addToAdapt(Pure.class, String.class.getMethod("toLowerCase", new Class[]{Locale.class})); addToAdapt(Pure.class, String.class.getMethod("toLowerCase", new Class[0])); addToAdapt(Pure.class, String.class.getMethod("toUpperCase", new Class[]{Locale.class})); addToAdapt(Pure.class, String.class.getMethod("toUpperCase", new Class[0])); addToAdapt(Pure.class, String.class.getMethod("trim", new Class[0])); addToAdapt(Pure.class, String.class.getMethod("valueOf", new Class[]{boolean.class})); addToAdapt(Pure.class, String.class.getMethod("valueOf", new Class[]{char.class})); addToAdapt(Pure.class, String.class.getMethod("valueOf", new Class[]{int.class})); addToAdapt(Pure.class, String.class.getMethod("valueOf", new Class[]{long.class})); addToAdapt(Pure.class, String.class.getMethod("valueOf", new Class[]{float.class})); addToAdapt(Pure.class, String.class.getMethod("valueOf", new Class[]{double.class})); addToAdapt(Pure.class, String.class.getMethod("intern", new Class[0])); addToAdapt(Pure.class, String.class.getMethod("codePointCount", new Class[]{int.class, int.class})); addToAdapt(Pure.class, String.class.getMethod("offsetByCodePoints", new Class[]{int.class, int.class})); addToAdapt(Pure.class, String.class.getMethod("length", new Class[0])); } } catch (Exception e) { System.out.println("Exception " + e); throw new Error(e); } } /** * Find the annotations to add to the given method and remove from the set * of things to annotate * @param className name of class we're adding annotation to * @param methodName name of method we're adding annotation to * @param methodDesc descriptor of method * @return set on annotations to add to method or null if none */ static Set<Class<? extends Annotation>> findAnnotationsForMethod(String className, String methodName, String methodDesc) { for (AnnotatedElement elem: thingsToAnnotate.keySet()) { if (elem instanceof Method) { Method m = (Method)elem; if (m.getDeclaringClass().getName().equals(className) && m.getName().equals(methodName) && Type.getMethodDescriptor(m).equals(methodDesc)) { annotatedElements.add(m); return thingsToAnnotate.get(m); } } else if (elem instanceof Constructor) { Constructor m = (Constructor)elem; if (m.getDeclaringClass().getName().equals(className) && Type.getConstructorDescriptor(m).equals(methodDesc)) { annotatedElements.add(m); return thingsToAnnotate.get(m); } } } ElementTriple triple = new ElementTriple(className, methodName, methodDesc); Set<Class<? extends Annotation>> set = thingsToAnnotate2.get(triple); if (set != null) { annotatedElements2.add(triple); } return set; } /** * Main entry point * @param args args[0] is the class library (harmony or gnu classpath) * args[1] is the classpath to use to read classes, * args[2] is the destination directory */ public static void main(final String[] args) { Set<Class<?>> processedClasses = new HashSet<Class<?>>(); classLibrary = args[0]; RVMClassLoader.init(args[1]); destinationDir = args[2] + "/"; setup(); for(AnnotatedElement elem: thingsToAnnotate.keySet()) { try { Class<?> c = getClassForElement(elem); if (!processedClasses.contains(c)) { adaptClass(c.getName()); processedClasses.add(c); } } catch (Exception e) { throw new Error("Error processing "+elem, e); } } Set<String> processedClasses2 = new HashSet<String>(); for(ElementTriple triple: thingsToAnnotate2.keySet()) { String c = triple.getClassName(); if (!processedClasses2.contains(c)) { adaptClass(c); processedClasses2.add(c); } } for (AnnotatedElement elem: annotatedElements) { thingsToAnnotate.remove(elem); } for (ElementTriple triple: annotatedElements2) { thingsToAnnotate2.remove(triple); } if (thingsToAnnotate.size() > 0 || thingsToAnnotate2.size() > 0) { for (AnnotatedElement elem: thingsToAnnotate.keySet()) { System.out.println("Error finding element to annotate: " + elem); } for (ElementTriple triple: thingsToAnnotate2.keySet()) { System.out.println("Error finding element to annotate: " + triple); } throw new Error("Error finding elements to annotate"); } } /** * Given an annotated element return the class that declares it * @param elem the annotated element * @return the declaring class or null if the declaring class is unknown */ private static Class<?> getClassForElement(AnnotatedElement elem) { if (elem instanceof Method) { return ((Method)elem).getDeclaringClass(); } else if (elem instanceof Constructor) { return ((Constructor)elem).getDeclaringClass(); } return null; } /** * Chain together a ClassReader than will read the given class and a * ClassWriter to write out a new class with an adapter in the middle to add * annotations * @param fromName the name of the class we're coming from */ private static void adaptClass(String fromName) { if (VERBOSE) System.out.println("Adding annotations to class: " + fromName); // gets an input stream to read the bytecode of the class String resource = fromName.replace('.', '/') + ".class"; InputStream is = BootstrapClassLoader.getBootstrapClassLoader().getResourceAsStream(resource); byte[] b; // adapts the class on the fly try { ClassReader cr = new ClassReader(is); ClassWriter cw = new ClassWriter(0); ClassVisitor cv = new AddAnnotationClassAdapter(cw, fromName); cr.accept(cv, 0); b = cw.toByteArray(); } catch (Exception e) { throw new Error("Couldn't find class " + fromName + " (" + resource + ")", e); } // store the adapted class on disk try { File file = new File(destinationDir + resource); new File(file.getParent()).mkdirs(); // ensure we have a directory to write to FileOutputStream fos = new FileOutputStream(file); fos.write(b); fos.close(); } catch (Exception e) { throw new Error("Error writing to " + destinationDir + resource + " to disk", e); } } /** * Class responsible for processing classes and adding annotations */ private static final class AddAnnotationClassAdapter extends ClassAdapter implements Opcodes { /** name of class */ private final String className; /** * Constructor * @param cv the reader of the class * @param name the name of the class being read */ public AddAnnotationClassAdapter(ClassVisitor cv, String name) { super(cv); this.className = name; } /** * Called when adapting a method. Determine whether we need to add an * annotation and if so vary the method visitor result * @param access flags * @param name of method * @param desc descriptor * @param signature generic signature * @param exceptions exceptions thrown * @return regular method visitor or one that will apply annotations */ @Override public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions); if (mv != null) { Set<Class<? extends Annotation>> annotations = findAnnotationsForMethod(className, name, desc); if (annotations != null) { if (VERBOSE) System.out.println("Found method: " + name); return new AddAnnotationMethodAdapter(mv, annotations); } } return mv; } } /** * Class responsible for processing methods and adding annotations */ private static final class AddAnnotationMethodAdapter extends MethodAdapter implements Opcodes { /** Annotations to add to method */ private final Set<Class<? extends Annotation>> toAddAnnotations; /** Annotations found on method */ private final Set<String> presentAnnotations = new HashSet<String>(); /** * Constructor * @param mv the reader of the method * @param anns annotations to add */ public AddAnnotationMethodAdapter(MethodVisitor mv, Set<Class<? extends Annotation>> anns) { super(mv); toAddAnnotations = anns; } /** * Visit annotation remembering what we see so that we don't add the same * annotation twice * @param desc descriptor of annotation * @param visible is it runtime visible * @return default annotation reader */ @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { presentAnnotations.add(desc); if (VERBOSE) System.out.println("Found annotation: " + desc); return mv.visitAnnotation(desc, visible); } /** * We've finished processing method, check what annotations were found and * then add those that weren't */ @Override public void visitEnd() { outer: for (Class<? extends Annotation> toAddAnn : toAddAnnotations) { Type toAddAnnType = Type.getType(toAddAnn); if (VERBOSE) System.out.println("Annotation: " + toAddAnn); for (String presentAnn : presentAnnotations) { if (toAddAnnType.equals(Type.getType(presentAnn))) { if (VERBOSE) System.out.println("Annotation already present: " + toAddAnn + " " + presentAnn); continue outer; } } if (VERBOSE) System.out.println("Adding annotation: " + toAddAnn); mv.visitAnnotation("L"+toAddAnnType.getInternalName()+";", true); } mv.visitEnd(); } } }