changeset 426:7088ef5476cd

Decouple annotation processors from the benchmark generators.
author shade
date Wed, 26 Feb 2014 17:06:12 +0400
parents 19a1d5fc4594
children a633786bb7b9
files jmh-core/src/main/java/org/openjdk/jmh/processor/internal/AnnUtils.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/BenchmarkGenerator.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/BenchmarkGeneratorUtils.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/ClassInfo.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/CompilerControlPlugin.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/CompilerControlProcessor.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/FieldInfo.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GenerateMicroBenchmarkProcessor.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GenerationException.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GeneratorSource.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GroupValidationPlugin.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GroupValidationProcessor.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/HelperMethodInvocation.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/HelperMethodValidationPlugin.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/HelperMethodValidationProcessor.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MetadataInfo.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodGroup.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodInfo.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodInvocation.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/ParamValidationPlugin.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/ParamValidationProcessor.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/ParameterInfo.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/Plugin.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/StateObject.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/StateObjectHandler.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/SubProcessor.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/APClassInfo.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/APFieldInfo.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/APMetadataInfo.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/APMethodInfo.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/APParameterInfo.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/AnnProcessGeneratorSource.java jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/AnnUtils.java
diffstat 33 files changed, 2518 insertions(+), 1672 deletions(-) [+]
line wrap: on
line diff
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/AnnUtils.java	Fri Feb 21 19:25:28 2014 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.openjdk.jmh.processor.internal;
-
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.PackageElement;
-import javax.lang.model.element.TypeElement;
-import java.lang.annotation.Annotation;
-
-public class AnnUtils {
-
-    /**
-     * Recursively get the annotation, until found, or reached the top of hierarchy.
-     * @param root where to start
-     * @param annotation what to look for
-     * @param <A> type of what we look for
-     * @return annotation
-     */
-    public static <A extends Annotation> A getAnnotationRecursive(Element root, Class<A> annotation) {
-        Element walk = root;
-        A result = null;
-        while (walk != null && (result = walk.getAnnotation(annotation)) == null) {
-            walk = walk.getEnclosingElement();
-        }
-        return result;
-    }
-
-    /**
-     * Get the package name part of a class
-     *
-     * @param clazz the subject
-     * @return the package name or "" if no package
-     */
-    public static String getPackageName(TypeElement clazz) {
-        Element walk = clazz;
-        while (walk.getKind() != ElementKind.PACKAGE) {
-            walk = walk.getEnclosingElement();
-        }
-        return ((PackageElement)walk).getQualifiedName().toString();
-    }
-
-    /**
-     * Get the class name along with any nested class names
-     * @param clazz the subject
-     * @return the synthetic class name in form of "parent1_parent2_classname"
-     */
-    public static String getNestedName(TypeElement clazz) {
-        String name = "";
-        Element walk = clazz;
-        while (walk.getKind() != ElementKind.PACKAGE) {
-            name = walk.getSimpleName().toString() + (name.isEmpty() ? "" : "_" + name);
-            walk = walk.getEnclosingElement();
-        }
-        return name.substring(0, name.length());
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/BenchmarkGenerator.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,981 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
+import org.openjdk.jmh.annotations.Group;
+import org.openjdk.jmh.annotations.GroupThreads;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Threads;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.logic.BlackHole;
+import org.openjdk.jmh.logic.InfraControl;
+import org.openjdk.jmh.logic.ThreadControl;
+import org.openjdk.jmh.logic.results.AverageTimeResult;
+import org.openjdk.jmh.logic.results.RawResults;
+import org.openjdk.jmh.logic.results.Result;
+import org.openjdk.jmh.logic.results.ResultRole;
+import org.openjdk.jmh.logic.results.SampleTimeResult;
+import org.openjdk.jmh.logic.results.SingleShotResult;
+import org.openjdk.jmh.logic.results.ThroughputResult;
+import org.openjdk.jmh.runner.BenchmarkRecord;
+import org.openjdk.jmh.runner.MicroBenchmarkList;
+import org.openjdk.jmh.util.internal.HashMultimap;
+import org.openjdk.jmh.util.internal.Multimap;
+import org.openjdk.jmh.util.internal.SampleBuffer;
+
+import javax.annotation.Generated;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.annotation.IncompleteAnnotationException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+
+/**
+ * @author Aleksey Shipilev (aleksey.shipilev@oracle.com)
+ */
+public class BenchmarkGenerator {
+
+    private final Set<BenchmarkInfo> benchmarkInfos;
+    private final Collection<Plugin> plugins;
+    private final CompilerControlPlugin compilerControl;
+    private final Set<String> processedBenchmarks;
+
+    public BenchmarkGenerator() {
+        benchmarkInfos = new HashSet<BenchmarkInfo>();
+        processedBenchmarks = new HashSet<String>();
+        compilerControl = new CompilerControlPlugin();
+
+        plugins = new ArrayList<Plugin>();
+        plugins.add(new HelperMethodValidationPlugin());
+        plugins.add(new GroupValidationPlugin());
+        plugins.add(new ParamValidationPlugin());
+        plugins.add(compilerControl);
+    }
+
+    public void generate(GeneratorSource source) {
+        try {
+            for (Plugin sub : plugins) {
+                sub.process(source);
+            }
+
+            // Build a Set of classes with a list of annotated methods
+            Multimap<ClassInfo, MethodInfo> clazzes = buildAnnotatedSet(source);
+
+            // Generate code for all found Classes and Methods
+            for (ClassInfo clazz : clazzes.keys()) {
+                if (!processedBenchmarks.add(clazz.getQualifiedName())) continue;
+                try {
+                    validateBenchmark(clazz, clazzes.get(clazz));
+                    BenchmarkInfo info = makeBenchmarkInfo(clazz, clazzes.get(clazz));
+                    generateClass(source, clazz, info);
+                    benchmarkInfos.add(info);
+                } catch (GenerationException ge) {
+                    source.printError(ge.getMessage(), ge.getElement());
+                }
+            }
+        } catch (Throwable t) {
+            source.printError("Annotation processor had thrown the exception: " + t);
+            t.printStackTrace(System.err);
+        }
+    }
+
+    public void complete(GeneratorSource source) {
+        for (Plugin sub : plugins) {
+            sub.finish(source);
+        }
+
+        // Processing completed, final round. Print all added methods to file
+        try {
+            PrintWriter writer = new PrintWriter(source.newResource(MicroBenchmarkList.MICROBENCHMARK_LIST.substring(1)));
+            for (BenchmarkInfo info : benchmarkInfos) {
+                for (String method : info.methodGroups.keySet()) {
+                    MethodGroup group = info.methodGroups.get(method);
+                    for (Mode m : group.getModes()) {
+                        BenchmarkRecord br = new BenchmarkRecord(
+                                info.userName + "." + method,
+                                info.generatedName + "." + method,
+                                m,
+                                group.getThreads(),
+                                group.getTotalThreadCount(),
+                                group.getWarmupIterations(),
+                                group.getWarmupTime(),
+                                group.getWarmupBatchSize(),
+                                group.getMeasurementIterations(),
+                                group.getMeasurementTime(),
+                                group.getMeasurementBatchSize(),
+                                group.getForks(),
+                                group.getWarmupForks(),
+                                group.getJVMArgs(),
+                                group.getJVMArgsPrepend(),
+                                group.getJVMArgsAppend(),
+                                group.getParams()
+                        );
+                        writer.println(br.toLine());
+                    }
+                }
+            }
+
+            writer.close();
+        } catch (IOException ex) {
+            source.printError("Error writing MicroBenchmark list " + ex);
+        }
+    }
+
+    /**
+     * Build a set of Classes which has annotated methods in them
+     *
+     * @return for all methods annotated with $annotation, returns Map<holder-class, Set<method>>
+     */
+    private Multimap<ClassInfo, MethodInfo> buildAnnotatedSet(GeneratorSource source) {
+        // Transitively close the hierarchy:
+        //   If superclass has a @GMB method, then all subclasses also have it.
+        //   We skip the generated classes, which we had probably generated during the previous rounds
+        //   of processing. Abstract classes are of no interest for us either.
+
+        Multimap<ClassInfo, MethodInfo> result = new HashMultimap<ClassInfo, MethodInfo>();
+        for (ClassInfo currentClass : source.getClasses()) {
+            if (currentClass.getQualifiedName().contains("generated")) continue;
+            if (currentClass.isAbstract()) continue;
+
+            for (MethodInfo mi : currentClass.getDeclaredMethods()) {
+                GenerateMicroBenchmark ann = mi.getAnnotation(GenerateMicroBenchmark.class);
+                if (ann != null) {
+                    result.put(currentClass, mi);
+                }
+            }
+
+            for (ClassInfo upperClass : currentClass.getSuperclasses()) {
+                if (upperClass.getQualifiedName().contains("generated")) continue;
+                for (MethodInfo mi : upperClass.getDeclaredMethods()) {
+                    GenerateMicroBenchmark ann = mi.getAnnotation(GenerateMicroBenchmark.class);
+                    if (ann != null) {
+                        result.put(currentClass, mi);
+                    }
+                }
+            }
+        }
+        return result;
+    }
+
+
+    /**
+     * Do basic benchmark validation.
+     */
+    private void validateBenchmark(ClassInfo clazz, Collection<MethodInfo> methods) {
+        if (clazz.getQualifiedName().isEmpty()) {
+            throw new GenerationException("Microbenchmark should have package other than default.", clazz);
+        }
+
+        Collection<ClassInfo> states = new ArrayList<ClassInfo>();
+
+        // validate all arguments are @State-s
+        for (MethodInfo e : methods) {
+            for (ParameterInfo var : e.getParameters()) {
+                states.add(var.getType());
+            }
+        }
+
+        // validate if enclosing class is implicit @State
+        if (clazz.getAnnotation(State.class) != null) {
+            states.add(clazz);
+        }
+
+        // validate @State classes
+        for (ClassInfo state : states) {
+            // Because of https://bugs.openjdk.java.net/browse/JDK-8031122,
+            // we need to preemptively check the annotation value, and
+            // the API can only allow that by catching the exception, argh.
+            try {
+                state.getAnnotation(State.class).value();
+            } catch (IncompleteAnnotationException iae) {
+                throw new GenerationException("The " + State.class.getSimpleName() +
+                        " annotation should have the explicit " + Scope.class.getSimpleName() + " argument",
+                        state);
+            }
+
+            if (!state.isPublic()) {
+                throw new GenerationException("The " + State.class.getSimpleName() +
+                        " annotation only supports public classes.", state);
+            }
+
+            if (state.isNested() && !state.isStatic()) {
+                throw new GenerationException("The " + State.class.getSimpleName() +
+                        " annotation does not support inner classes, make sure the class is nested (static).",
+                        state);
+            }
+
+            boolean hasDefaultConstructor = false;
+            for (MethodInfo constructor : state.getConstructors()) {
+                hasDefaultConstructor |= (constructor.getParameters().isEmpty() && constructor.isPublic());
+            }
+
+            if (!hasDefaultConstructor) {
+                throw new GenerationException("The " + State.class.getSimpleName() +
+                        " annotation can only be applied to the classes having the default public constructor.",
+                        state);
+            }
+        }
+
+        // validate against rogue fields
+        if (clazz.getAnnotation(State.class) == null || clazz.isAbstract()) {
+            for (FieldInfo fi : clazz.getFields()) {
+                // allow static fields
+                if (fi.isStatic()) continue;
+                throw new GenerationException(
+                        "Field \"" + fi.getName() + "\" is declared within " +
+                                "the class not having @" + State.class.getSimpleName() + " annotation. " +
+                                "This can result in unspecified behavior, and prohibited.", fi);
+            }
+        }
+
+        // check modifiers
+        for (MethodInfo m : methods) {
+            if (!m.isPublic()) {
+                throw new GenerationException("@" + GenerateMicroBenchmark.class.getSimpleName() +
+                        " method should be public.", m);
+            }
+
+            if (m.isAbstract()) {
+                throw new GenerationException("@" + GenerateMicroBenchmark.class.getSimpleName()
+                        + " method can not be abstract.", m);
+            }
+            if (m.isSynchronized()) {
+                if (clazz.getAnnotation(State.class) == null) {
+                    throw new GenerationException("@" + GenerateMicroBenchmark.class.getSimpleName()
+                            + " method can only be synchronized if the enclosing class is annotated with "
+                            + "@" + State.class.getSimpleName() + ".", m);
+                    }
+            }
+        }
+
+        // check annotations
+        for (MethodInfo m : methods) {
+            OperationsPerInvocation opi = m.getAnnotationRecursive(OperationsPerInvocation.class);
+            if (opi != null && opi.value() < 1) {
+                throw new GenerationException("The " + OperationsPerInvocation.class.getSimpleName() +
+                        " needs to be greater than 0.", m);
+            }
+        }
+    }
+
+    /**
+     * validate benchmark info
+     * @param info benchmark info to validate
+     */
+    private void validateBenchmarkInfo(BenchmarkInfo info) {
+        // check the @Group preconditions,
+        // ban some of the surprising configurations
+        //
+        for (MethodGroup group : info.methodGroups.values()) {
+            if (group.methods().size() == 1) {
+                MethodInfo meth = group.methods().iterator().next();
+                if (meth.getAnnotation(Group.class) == null) {
+                    for (ParameterInfo param : meth.getParameters()) {
+                        State stateAnn = param.getType().getAnnotation(State.class);
+                        if (stateAnn != null && stateAnn.value() == Scope.Group) {
+                            throw new GenerationException(
+                                    "Only @" + Group.class.getSimpleName() + " methods can reference @" + State.class.getSimpleName()
+                                            + "(" + Scope.class.getSimpleName() + "." + Scope.Group + ") states.",
+                                    meth);
+                        }
+                    }
+
+                    State stateAnn = meth.getOwner().getAnnotation(State.class);
+                    if (stateAnn != null && stateAnn.value() == Scope.Group) {
+                        throw new GenerationException(
+                                "Only @" + Group.class.getSimpleName() + " methods can implicitly reference @" + State.class.getSimpleName()
+                                        + "(" + Scope.class.getSimpleName() + "." + Scope.Group + ") states.",
+                                meth);
+                    }
+                }
+            } else {
+                for (MethodInfo m : group.methods()) {
+                    if (m.getAnnotation(Group.class) == null) {
+                        throw new GenerationException(
+                                "Internal error: multiple methods per @" + Group.class.getSimpleName()
+                                        + ", but not all methods have @" + Group.class.getSimpleName(),
+                                m);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Generate BenchmarkInfo for given class.
+     * We will figure out method groups at this point.
+     *
+     * @param clazz holder class
+     * @param methods annotated methods
+     * @return BenchmarkInfo
+     */
+    private BenchmarkInfo makeBenchmarkInfo(ClassInfo clazz, Collection<MethodInfo> methods) {
+        Map<String, MethodGroup> result = new TreeMap<String, MethodGroup>();
+
+        for (MethodInfo method : methods) {
+            Group groupAnn = method.getAnnotation(Group.class);
+            String groupName = (groupAnn != null) ? groupAnn.value() : method.getName();
+
+            if (!checkJavaIdentifier(groupName)) {
+                throw new GenerationException("Group name should be the legal Java identifier.", method);
+            }
+
+            MethodGroup group = result.get(groupName);
+            if (group == null) {
+                group = new MethodGroup(groupName);
+                result.put(groupName, group);
+            }
+
+            BenchmarkMode mbAn = method.getAnnotationRecursive(BenchmarkMode.class);
+            if (mbAn != null) {
+                group.addModes(mbAn.value());
+            }
+
+            group.addStrictFP(clazz.isStrictFP());
+            group.addStrictFP(method.isStrictFP());
+            group.addMethod(method, (method.getAnnotation(GroupThreads.class) != null) ? method.getAnnotation(GroupThreads.class).value() : 1);
+
+            // Discovering @Params, part 1:
+            //   For each parameter, walk the type hierarchy up to discover inherited @Param fields in @State objects.
+            for (ParameterInfo pi : method.getParameters()) {
+                for (FieldInfo fi : pi.getType().getFields()) {
+                    if (fi.getAnnotation(Param.class) != null) {
+                        group.addParam(fi.getName(), fi.getAnnotation(Param.class).value());
+                    }
+                }
+            }
+
+            // Discovering @Params, part 2:
+            //  Walk the type hierarchy up to discover inherited @Param fields for class.
+            for (FieldInfo fi : clazz.getFields()) {
+                if (fi.getAnnotation(Param.class) != null) {
+                    group.addParam(fi.getName(), fi.getAnnotation(Param.class).value());
+                }
+            }
+        }
+
+        // enforce the default value
+        for (MethodGroup group : result.values()) {
+            if (group.getModes().isEmpty()) {
+                group.addModes(Mode.Throughput);
+            }
+        }
+
+        String sourcePackage = clazz.getPackageName();
+        String generatedPackageName = sourcePackage + ".generated";
+        String generatedClassName = clazz.getNestedName();
+
+        BenchmarkInfo info = new BenchmarkInfo(clazz.getQualifiedName(), generatedPackageName, generatedClassName, result);
+        validateBenchmarkInfo(info);
+        return info;
+    }
+
+    public static boolean checkJavaIdentifier(String id) {
+        for (int i = 0; i < id.length(); i++) {
+            char c = id.charAt(i);
+            if (!Character.isJavaIdentifierPart(c)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Create and generate Java code for a class and it's methods
+     */
+    private void generateClass(GeneratorSource source, ClassInfo classInfo, BenchmarkInfo info) {
+        try {
+            // Create file and open an outputstream
+            PrintWriter writer = new PrintWriter(source.newFile(info.generatedName), false);
+
+            // Write package and imports
+            writer.println("package " + info.generatedPackageName + ';');
+            writer.println();
+
+            generateImport(writer);
+
+            // Write class header
+            writer.println("@" + CompilerControl.class.getSimpleName() +
+                    "(" + CompilerControl.class.getSimpleName() + "." + CompilerControl.Mode.class.getSimpleName() +
+                    "." + CompilerControl.Mode.DONT_INLINE + ")");
+            writer.println("@Generated(\"" + BenchmarkGenerator.class.getCanonicalName() + "\")");
+            writer.println("public final class " + info.generatedClassName + " {");
+            writer.println();
+            generatePadding(writer);
+
+            StateObjectHandler states = new StateObjectHandler();
+
+            // benchmark instance is implicit
+            states.bindImplicit(classInfo, "bench", Scope.Thread);
+
+            // default blackhole is implicit
+            states.bindImplicit(source.resolveClass(BlackHole.class.getCanonicalName()), "blackhole", Scope.Thread);
+
+            // Write all methods
+            for (String groupName : info.methodGroups.keySet()) {
+                for (MethodInfo method : info.methodGroups.get(groupName).methods()) {
+                    for (ParameterInfo p : method.getParameters()) {
+                        states.bindArg(method, p);
+                    }
+                }
+
+                for (Mode benchmarkKind : Mode.values()) {
+                    if (benchmarkKind == Mode.All) continue;
+                    generateMethod(benchmarkKind, writer, info.methodGroups.get(groupName), states);
+                }
+                states.clearArgs();
+            }
+
+            // Write out state initializers
+            for (String s : states.getStateInitializers()) {
+                writer.println(ident(1) + s);
+            }
+            writer.println();
+
+            // Write out the required fields
+            for (String s : states.getFields()) {
+                writer.println(ident(1) + s);
+            }
+            writer.println();
+
+            // Write out the required objects
+            for (String s : states.getStateOverrides()) {
+                writer.println(ident(1) + s);
+            }
+            writer.println();
+
+            // Finish class
+            writer.println("}");
+            writer.println();
+
+            writer.close();
+        } catch (IOException ex) {
+            throw new GenerationException("IOException", classInfo);
+        }
+    }
+
+    private void generatePadding(PrintWriter writer) {
+        for (int p = 0; p < 16; p++) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(ident(1));
+            sb.append("boolean jmh_pad_").append(p);
+            for (int q = 1; q < 16; q++) {
+                sb.append(", jmh_pad_").append(p).append("_").append(q);
+            }
+            sb.append(";");
+            writer.println(sb.toString());
+        }
+    }
+
+    private void generateImport(PrintWriter writer) {
+        Class<?>[] imports = new Class<?>[] {
+                List.class, AtomicInteger.class, AtomicIntegerFieldUpdater.class,
+                Collection.class, Collections.class, ArrayList.class, Arrays.class,
+                TimeUnit.class, Generated.class, CompilerControl.class,
+                InfraControl.class, ThreadControl.class, BlackHole.class,
+                Result.class, ThroughputResult.class, AverageTimeResult.class,
+                SampleTimeResult.class, SingleShotResult.class, SampleBuffer.class,
+                Mode.class, Fork.class, Measurement.class, Threads.class, Warmup.class,
+                BenchmarkMode.class, RawResults.class, ResultRole.class
+        };
+
+        for (Class<?> c : imports) {
+            writer.println("import " + c.getName() + ';');
+        }
+        writer.println();
+    }
+
+    /**
+     * Generate the method for a specific benchmark method
+     */
+    private void generateMethod(Mode benchmarkKind, PrintWriter writer, MethodGroup methodGroup, StateObjectHandler states) {
+        writer.println();
+        switch (benchmarkKind) {
+            case Throughput:
+                generateThroughput(writer, benchmarkKind, methodGroup, states);
+                break;
+            case AverageTime:
+                generateAverageTime(writer, benchmarkKind, methodGroup, states);
+                break;
+            case SampleTime:
+                generateSampleTime(writer, benchmarkKind, methodGroup, states);
+                break;
+            case SingleShotTime:
+                generateSingleShotTime(writer, benchmarkKind, methodGroup, states);
+                break;
+            default:
+                throw new AssertionError("Shouldn't be here");
+        }
+    }
+
+    private void generateThroughput(PrintWriter writer, Mode benchmarkKind, MethodGroup methodGroup, StateObjectHandler states) {
+        writer.println(ident(1) + "public Collection<? extends Result> " + methodGroup.getName() + "_" + benchmarkKind +
+                "(InfraControl control, ThreadControl threadControl) throws Throwable {");
+        writer.println();
+
+        methodProlog(writer, methodGroup);
+
+        boolean isSingleMethod = (methodGroup.methods().size() == 1);
+        int subGroup = -1;
+        for (MethodInfo method : methodGroup.methods()) {
+            compilerControl.defaultForceInline(method);
+
+            subGroup++;
+
+            writer.println(ident(2) + "if (threadControl.subgroup == " + subGroup + ") {");
+
+            iterationProlog(writer, 3, method, states);
+
+            // synchronize iterations prolog: announce ready
+            writer.println(ident(3) + "control.announceWarmupReady();");
+
+            // synchronize iterations prolog: catchup loop
+            writer.println(ident(3) + "while (control.warmupShouldWait) {");
+
+            invocationProlog(writer, 4, method, states, false);
+            writer.println(ident(4) + emitCall(method, states) + ';');
+            invocationEpilog(writer, 4, method, states, false);
+
+            writer.println(ident(3) + "}");
+            writer.println();
+
+            // control objects get a special treatment
+            for (StateObject so : states.getControls()) {
+                writer.println(ident(3) + so.localIdentifier + ".startMeasurement = true;");
+                writer.println(ident(3) + so.localIdentifier + ".iterationTime = control.getDuration();");
+            }
+
+            // measurement loop call
+            writer.println(ident(3) + "RawResults res = new RawResults(" + methodGroup.getOperationsPerInvocation() + "L);");
+            writer.println(ident(3) + method.getName() + "_" + benchmarkKind + "_measurementLoop" +
+                    "(control, res, " + states.getImplicit("bench").toLocal() + ", " + states.getImplicit("blackhole").toLocal() + prefix(states.getArgList(method)) + ");");
+
+            // control objects get a special treatment
+            for (StateObject so : states.getControls()) {
+                writer.println(ident(3) + so.localIdentifier + ".stopMeasurement = true;");
+            }
+
+            // synchronize iterations epilog: announce ready
+            writer.println(ident(3) + "control.announceWarmdownReady();");
+
+            // synchronize iterations epilog: catchup loop
+            writer.println(ident(3) + "while (control.warmdownShouldWait) {");
+
+            invocationProlog(writer, 4, method, states, false);
+            writer.println(ident(4) + emitCall(method, states) + ';');
+            invocationEpilog(writer, 4, method, states, false);
+
+            writer.println(ident(3) + "}");
+
+            // iteration prolog
+            iterationEpilog(writer, 3, method, states);
+
+            writer.println(ident(3) + "Collection<Result> results = new ArrayList<Result>();");
+            writer.println(ident(3) + "TimeUnit tu = (control.timeUnit != null) ? control.timeUnit : TimeUnit." + methodGroup.getOutputTimeUnit() + ";");
+            writer.println(ident(3) + "results.add(new ThroughputResult(ResultRole.PRIMARY, \"" + method.getName() + "\", res.getOperations(), res.getTime(), tu));");
+            if (!isSingleMethod) {
+                writer.println(ident(3) + "results.add(new ThroughputResult(ResultRole.SECONDARY, \"" + method.getName() + "\", res.getOperations(), res.getTime(), tu));");
+            }
+            for (String ops : states.getAuxResultNames(method)) {
+                writer.println(ident(3) + "results.add(new ThroughputResult(ResultRole.SECONDARY, \"" + ops + "\", " + states.getAuxResultAccessor(method, ops) + ", res.getTime(), tu));");
+            }
+            writer.println(ident(3) + "return results;");
+            writer.println(ident(2) + "} else");
+        }
+        writer.println(ident(3) + "throw new IllegalStateException(\"Harness failed to distribute threads among groups properly\");");
+        writer.println();
+
+        writer.println(ident(1) + "}");
+
+        // measurement loop bodies
+        for (MethodInfo method : methodGroup.methods()) {
+            String methodName = method.getName() + "_" + benchmarkKind + "_measurementLoop";
+            writer.println(ident(1) + "public " + (methodGroup.isStrictFP() ? "strictfp" : "") + " void " + methodName + "(InfraControl control, RawResults result, " + states.getImplicit("bench").toTypeDef() + ", " + states.getImplicit("blackhole").toTypeDef() + prefix(states.getTypeArgList(method)) + ") throws Throwable {");
+            writer.println(ident(2) + "long operations = 0;");
+            writer.println(ident(2) + "long realTime = 0;");
+            writer.println(ident(2) + "result.startTime = System.nanoTime();");
+            writer.println(ident(2) + "do {");
+
+            invocationProlog(writer, 3, method, states, true);
+            writer.println(ident(3) + emitCall(method, states) + ';');
+            invocationEpilog(writer, 3, method, states, true);
+
+            writer.println(ident(3) + "operations++;");
+            writer.println(ident(2) + "} while(!control.isDone);");
+            writer.println(ident(2) + "result.stopTime = System.nanoTime();");
+            writer.println(ident(2) + "result.realTime = realTime;");
+            writer.println(ident(2) + "result.operations = operations;");
+            writer.println(ident(1) + "}");
+            writer.println();
+        }
+    }
+
+    private void generateAverageTime(PrintWriter writer, Mode benchmarkKind, MethodGroup methodGroup, StateObjectHandler states) {
+        writer.println(ident(1) + "public Collection<? extends Result> " + methodGroup.getName() + "_" + benchmarkKind +
+                "(InfraControl control, ThreadControl threadControl) throws Throwable {");
+
+        methodProlog(writer, methodGroup);
+
+        boolean isSingleMethod = (methodGroup.methods().size() == 1);
+        int subGroup = -1;
+        for (MethodInfo method : methodGroup.methods()) {
+            compilerControl.defaultForceInline(method);
+
+            subGroup++;
+
+            writer.println(ident(2) + "if (threadControl.subgroup == " + subGroup + ") {");
+
+            iterationProlog(writer, 3, method, states);
+
+            // synchronize iterations prolog: announce ready
+            writer.println(ident(3) + "control.announceWarmupReady();");
+
+            // synchronize iterations prolog: catchup loop
+            writer.println(ident(3) + "while (control.warmupShouldWait) {");
+
+            invocationProlog(writer, 4, method, states, false);
+            writer.println(ident(4) + emitCall(method, states) + ';');
+            invocationEpilog(writer, 4, method, states, false);
+
+            writer.println(ident(3) + "}");
+            writer.println();
+
+            // control objects get a special treatment
+            for (StateObject so : states.getControls()) {
+                writer.println(ident(3) + so.localIdentifier + ".startMeasurement = true;");
+                writer.println(ident(3) + so.localIdentifier + ".iterationTime = control.getDuration();");
+            }
+
+            // measurement loop call
+            writer.println(ident(3) + "RawResults res = new RawResults(" + methodGroup.getOperationsPerInvocation() + "L);");
+            writer.println(ident(3) + method.getName() + "_" + benchmarkKind + "_measurementLoop(control, res, " + states.getImplicit("bench").toLocal() + ", " + states.getImplicit("blackhole").toLocal() + prefix(states.getArgList(method)) + ");");
+
+            // control objects get a special treatment
+            for (StateObject so : states.getControls()) {
+                writer.println(ident(3) + so.localIdentifier + ".stopMeasurement = true;");
+            }
+
+            // synchronize iterations epilog: announce ready
+            writer.println(ident(3) + "control.announceWarmdownReady();");
+
+            // synchronize iterations epilog: catchup loop
+            writer.println(ident(3) + "while (control.warmdownShouldWait) {");
+
+            invocationProlog(writer, 4, method, states, false);
+            writer.println(ident(4) + emitCall(method, states) + ';');
+            invocationEpilog(writer, 4, method, states, false);
+
+            writer.println(ident(3) + "}");
+
+            iterationEpilog(writer, 3, method, states);
+
+            writer.println(ident(3) + "Collection<Result> results = new ArrayList<Result>();");
+            writer.println(ident(3) + "TimeUnit tu = (control.timeUnit != null) ? control.timeUnit : TimeUnit." + methodGroup.getOutputTimeUnit() + ";");
+            writer.println(ident(3) + "results.add(new AverageTimeResult(ResultRole.PRIMARY, \"" + method.getName() + "\", res.getOperations(), res.getTime(), tu));");
+            if (!isSingleMethod) {
+                writer.println(ident(3) + "results.add(new AverageTimeResult(ResultRole.SECONDARY, \"" + method.getName() + "\", res.getOperations(), res.getTime(), tu));");
+            }
+            for (String ops : states.getAuxResultNames(method)) {
+                writer.println(ident(3) + "results.add(new AverageTimeResult(ResultRole.SECONDARY, \"" + ops + "\", " + states.getAuxResultAccessor(method, ops) + ", res.getTime(), tu));");
+            }
+            writer.println(ident(3) + "return results;");
+            writer.println(ident(2) + "} else");
+        }
+        writer.println(ident(3) + "throw new IllegalStateException(\"Harness failed to distribute threads among groups properly\");");
+        writer.println();
+
+        writer.println(ident(1) + "}");
+
+        // measurement loop bodies
+        for (MethodInfo method : methodGroup.methods()) {
+            writer.println(ident(1) + "public " + (methodGroup.isStrictFP() ? "strictfp" : "") +  " void " + method.getName() + "_" + benchmarkKind + "_measurementLoop" +
+                    "(InfraControl control, RawResults result, " + states.getImplicit("bench").toTypeDef() + ", " + states.getImplicit("blackhole").toTypeDef() +
+                    prefix(states.getTypeArgList(method)) + ") throws Throwable {");
+            writer.println(ident(2) + "long operations = 0;");
+            writer.println(ident(2) + "long realTime = 0;");
+            writer.println(ident(2) + "result.startTime = System.nanoTime();");
+            writer.println(ident(2) + "do {");
+
+            invocationProlog(writer, 3, method, states, true);
+            writer.println(ident(3) + emitCall(method, states) + ';');
+            invocationEpilog(writer, 3, method, states, true);
+
+            writer.println(ident(3) + "operations++;");
+            writer.println(ident(2) + "} while(!control.isDone);");
+            writer.println(ident(2) + "result.stopTime = System.nanoTime();");
+            writer.println(ident(2) + "result.realTime = realTime;");
+            writer.println(ident(2) + "result.operations = operations;");
+            writer.println(ident(1) + "}");
+            writer.println();
+        }
+    }
+
+    private void methodProlog(PrintWriter writer, MethodGroup methodGroup) {
+        // do nothing
+    }
+
+    private String prefix(String argList) {
+        if (argList.trim().isEmpty()) {
+            return "";
+        } else {
+            return ", " + argList;
+        }
+    }
+
+    private void generateSampleTime(PrintWriter writer, Mode benchmarkKind, MethodGroup methodGroup, StateObjectHandler states) {
+        writer.println(ident(1) + "public Collection<? extends Result> " + methodGroup.getName() + "_" + benchmarkKind +
+                "(InfraControl control, ThreadControl threadControl) throws Throwable {");
+        writer.println();
+
+        methodProlog(writer, methodGroup);
+
+        boolean isSingleMethod = (methodGroup.methods().size() == 1);
+        int subGroup = -1;
+        for (MethodInfo method : methodGroup.methods()) {
+            compilerControl.defaultForceInline(method);
+
+            subGroup++;
+
+            writer.println(ident(2) + "if (threadControl.subgroup == " + subGroup + ") {");
+
+            iterationProlog(writer, 3, method, states);
+
+            // synchronize iterations prolog: announce ready
+            writer.println(ident(3) + "control.announceWarmupReady();");
+
+            // synchronize iterations prolog: catchup loop
+            writer.println(ident(3) + "while (control.warmupShouldWait) {");
+
+            invocationProlog(writer, 4, method, states, false);
+            writer.println(ident(4) + emitCall(method, states) + ';');
+            invocationEpilog(writer, 4, method, states, false);
+
+            writer.println(ident(3) + "}");
+            writer.println();
+
+            // control objects get a special treatment
+            for (StateObject so : states.getControls()) {
+                writer.println(ident(3) + so.localIdentifier + ".startMeasurement = true;");
+                writer.println(ident(3) + so.localIdentifier + ".iterationTime = control.getDuration();");
+            }
+
+            // measurement loop call
+            writer.println(ident(3) + "SampleBuffer buffer = new SampleBuffer();");
+            writer.println(ident(3) + method.getName() + "_" + benchmarkKind + "_measurementLoop(control, buffer, " + states.getImplicit("bench").toLocal() + ", " + states.getImplicit("blackhole").toLocal() + prefix(states.getArgList(method)) + ");");
+
+            // control objects get a special treatment
+            for (StateObject so : states.getControls()) {
+                writer.println(ident(3) + so.localIdentifier + ".stopMeasurement = true;");
+            }
+
+            // synchronize iterations epilog: announce ready
+            writer.println(ident(3) + "control.announceWarmdownReady();");
+
+            // synchronize iterations epilog: catchup loop
+            writer.println(ident(3) + "while (control.warmdownShouldWait) {");
+
+            invocationProlog(writer, 4, method, states, false);
+            writer.println(ident(4) + emitCall(method, states) + ';');
+            invocationEpilog(writer, 4, method, states, false);
+
+            writer.println(ident(3) + "}");
+            writer.println();
+
+            iterationEpilog(writer, 3, method, states);
+
+            writer.println(ident(3) + "Collection<Result> results = new ArrayList<Result>();");
+            writer.println(ident(3) + "TimeUnit tu = (control.timeUnit != null) ? control.timeUnit : TimeUnit." + methodGroup.getOutputTimeUnit() + ";");
+            writer.println(ident(3) + "results.add(new SampleTimeResult(ResultRole.PRIMARY, \"" + method.getName() + "\", buffer, tu));");
+            if (!isSingleMethod) {
+                writer.println(ident(3) + "results.add(new SampleTimeResult(ResultRole.SECONDARY, \"" + method.getName() + "\", buffer, tu));");
+            }
+            writer.println(ident(3) + "return results;");
+            writer.println(ident(2) + "} else");
+        }
+        writer.println(ident(3) + "throw new IllegalStateException(\"Harness failed to distribute threads among groups properly\");");
+
+        writer.println(ident(1) + "}");
+
+        // measurement loop bodies
+        for (MethodInfo method : methodGroup.methods()) {
+            writer.println(ident(1) + "public " + (methodGroup.isStrictFP() ? "strictfp" : "") + " void " + method.getName() + "_" + benchmarkKind + "_measurementLoop(InfraControl control, SampleBuffer buffer, " + states.getImplicit("bench").toTypeDef() + ", " + states.getImplicit("blackhole").toTypeDef() + prefix(states.getTypeArgList(method)) + ") throws Throwable {");
+            writer.println(ident(2) + "long realTime = 0;");
+            writer.println(ident(2) + "int rnd = (int)System.nanoTime();");
+            writer.println(ident(2) + "int rndMask = 0;");
+            writer.println(ident(2) + "long time = 0;");
+            writer.println(ident(2) + "int currentStride = 0;");
+            writer.println(ident(2) + "do {");
+
+            invocationProlog(writer, 3, method, states, true);
+
+            writer.println(ident(3) + "rnd = (rnd * 1664525 + 1013904223);");
+            writer.println(ident(3) + "boolean sample = (rnd & rndMask) == 0;");
+            writer.println(ident(3) + "if (sample) {");
+            writer.println(ident(4) + "time = System.nanoTime();");
+            writer.println(ident(3) + "}");
+            writer.println(ident(3) + "" + emitCall(method, states) + ';');
+            writer.println(ident(3) + "if (sample) {");
+            writer.println(ident(4) + "buffer.add(System.nanoTime() - time);");
+            writer.println(ident(4) + "if (currentStride++ > 1000000) {");
+            writer.println(ident(5) + "buffer.half();");
+            writer.println(ident(5) + "currentStride = 0;");
+            writer.println(ident(5) + "rndMask = (rndMask << 1) + 1;");
+            writer.println(ident(4) + "}");
+            writer.println(ident(3) + "}");
+
+            invocationEpilog(writer, 3, method, states, true);
+
+            writer.println(ident(2) + "} while(!control.isDone);");
+
+            writer.println(ident(1) + "}");
+            writer.println();
+        }
+    }
+
+    private void generateSingleShotTime(PrintWriter writer, Mode benchmarkKind, MethodGroup methodGroup, StateObjectHandler states) {
+        writer.println(ident(1) + "public Collection<? extends Result> " + methodGroup.getName() + "_" + benchmarkKind + "(InfraControl control, ThreadControl threadControl) throws Throwable {");
+
+        methodProlog(writer, methodGroup);
+
+        writer.println(ident(2) + "long realTime = 0;");
+
+        boolean isSingleMethod = (methodGroup.methods().size() == 1);
+        int subGroup = -1;
+        for (MethodInfo method : methodGroup.methods()) {
+            compilerControl.defaultForceInline(method);
+
+            subGroup++;
+
+            writer.println(ident(2) + "if (threadControl.subgroup == " + subGroup + ") {");
+
+            iterationProlog(writer, 3, method, states);
+
+            invocationProlog(writer, 3, method, states, false);
+
+            writer.println(ident(3) + "long time1 = System.nanoTime();");
+            writer.println(ident(3) + "int batchSize = control.batchSize;");
+            writer.println(ident(3) + "for (int b = 0; b < batchSize; b++) {");
+            writer.println(ident(4) + emitCall(method, states) + ';');
+            writer.println(ident(3) + "}");
+            writer.println(ident(3) + "long time2 = System.nanoTime();");
+
+            invocationEpilog(writer, 3, method, states, false);
+
+            iterationEpilog(writer, 3, method, states);
+
+            writer.println(ident(3) + "Collection<Result> results = new ArrayList<Result>();");
+            writer.println(ident(3) + "TimeUnit tu = (control.timeUnit != null) ? control.timeUnit : TimeUnit." + methodGroup.getOutputTimeUnit() + ";");
+            writer.println(ident(3) + "results.add(new SingleShotResult(ResultRole.PRIMARY, \"" + method.getName() + "\", (realTime > 0) ? realTime : (time2 - time1), tu));");
+            if (!isSingleMethod) {
+                writer.println(ident(3) + "results.add(new SingleShotResult(ResultRole.SECONDARY, \"" + method.getName() + "\", (realTime > 0) ? realTime : (time2 - time1), tu));");
+            }
+            writer.println(ident(3) + "return results;");
+            writer.println(ident(2) + "} else");
+        }
+        writer.println(ident(3) + "throw new IllegalStateException(\"Harness failed to distribute threads among groups properly\");");
+        writer.println();
+
+        writer.println(ident(1) + "}");
+    }
+
+    private void invocationProlog(PrintWriter writer, int prefix, MethodInfo method, StateObjectHandler states, boolean pauseMeasurement) {
+        if (!states.getInvocationSetups(method).isEmpty()) {
+            for (String s : states.getInvocationSetups(method))
+                writer.println(ident(prefix) + s);
+            if (pauseMeasurement)
+                writer.println(ident(prefix) + "long rt = System.nanoTime();");
+            writer.println();
+        }
+    }
+
+    private void invocationEpilog(PrintWriter writer, int prefix, MethodInfo method, StateObjectHandler states, boolean pauseMeasurement) {
+        if (!states.getInvocationTearDowns(method).isEmpty()) {
+            writer.println();
+            if (pauseMeasurement)
+                writer.println(ident(prefix) + "realTime += (System.nanoTime() - rt);");
+            for (String s : states.getInvocationTearDowns(method))
+                writer.println(ident(prefix) + s);
+            writer.println();
+        }
+    }
+
+    private void iterationProlog(PrintWriter writer, int prefix, MethodInfo method, StateObjectHandler states) {
+        for (String s : states.getStateGetters(method)) writer.println(ident(prefix) + s);
+        writer.println();
+
+        writer.println(ident(prefix) + "control.preSetup();");
+
+        for (String s : states.getIterationSetups(method)) writer.println(ident(prefix) + s);
+        writer.println();
+    }
+
+    private void iterationEpilog(PrintWriter writer, int prefix, MethodInfo method, StateObjectHandler states) {
+        writer.println(ident(prefix) + "control.preTearDown();");
+
+        for (String s : states.getIterationTearDowns(method)) writer.println(ident(prefix) + s);
+        writer.println();
+
+        writer.println(ident(prefix) + "if (control.isLastIteration()) {");
+        for (String s : states.getRunTearDowns(method)) writer.println(ident(prefix + 1) + s);
+        for (String s : states.getStateDestructors(method)) writer.println(ident(prefix + 1) + s);
+        writer.println(ident(prefix) + "}");
+    }
+
+    private String emitCall(MethodInfo method, StateObjectHandler states) {
+        if ("void".equalsIgnoreCase(method.getReturnType())) {
+            return states.getImplicit("bench").localIdentifier + "." + method.getName() + "(" + states.getArgList(method) + ")";
+        } else {
+            return states.getImplicit("blackhole").localIdentifier + ".consume(" + states.getImplicit("bench").localIdentifier + "." + method.getName() + "(" + states.getArgList(method) + "))";
+        }
+    }
+
+    public static String ident(int prefix) {
+        char[] chars = new char[prefix*4];
+        for (int i = 0; i < prefix*4; i++) {
+            chars[i] = ' ';
+        }
+        return new String(chars);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/BenchmarkGeneratorUtils.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class BenchmarkGeneratorUtils {
+
+    public static <T extends Annotation> Collection<MethodInfo> getMethodsAnnotatedWith(GeneratorSource source, Class<T> annClass) {
+        List<MethodInfo> mis = new ArrayList<MethodInfo>();
+        for (ClassInfo ci : source.getClasses()) {
+            for (MethodInfo mi : ci.getDeclaredMethods()) {
+                if (mi.getAnnotation(annClass) != null) {
+                    mis.add(mi);
+                }
+            }
+        }
+        return mis;
+    }
+
+    public static <T extends Annotation> Collection<ClassInfo> getClassesAnnotatedWith(GeneratorSource source, Class<T> annClass) {
+        List<ClassInfo> cis = new ArrayList<ClassInfo>();
+        for (ClassInfo ci : source.getClasses()) {
+            if (ci.getAnnotation(annClass) != null) {
+                cis.add(ci);
+            }
+        }
+        return cis;
+    }
+
+    public static <T extends Annotation> Collection<FieldInfo> getFieldsAnnotatedWith(GeneratorSource source, Class<T> annClass) {
+        List<FieldInfo> mis = new ArrayList<FieldInfo>();
+        for (ClassInfo ci : source.getClasses()) {
+            for (FieldInfo mi : ci.getDeclaredFields()) {
+                if (mi.getAnnotation(annClass) != null) {
+                    mis.add(mi);
+                }
+            }
+        }
+        return mis;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/ClassInfo.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+
+/**
+ * Class metadata.
+ */
+public interface ClassInfo extends MetadataInfo {
+
+    /**
+     * Returns the fully qualified package name for class.
+     * Example:
+     *   f(java.util.HashMap) = java.util
+     *
+     * @return package name
+     */
+    String getPackageName();
+
+    /**
+     * Returns the marshalling
+     * @return
+     */
+    String getNestedName();
+
+    String getQualifiedName();
+
+    Collection<FieldInfo> getDeclaredFields();
+
+    Collection<FieldInfo> getFields();
+
+    Collection<MethodInfo> getConstructors();
+
+    Collection<MethodInfo> getDeclaredMethods();
+
+    Collection<MethodInfo> getMethods();
+
+    Collection<ClassInfo> getSuperclasses();
+
+    <T extends Annotation> T getAnnotation(Class<T> annClass);
+
+    <T extends Annotation> T getAnnotationRecursive(Class<T> annClass);
+
+    boolean isNested();
+
+    boolean isAbstract();
+
+    boolean isPublic();
+
+    boolean isStatic();
+
+    boolean isStrictFP();
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/CompilerControlPlugin.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.runner.CompilerHints;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+public class CompilerControlPlugin implements Plugin {
+
+    private final List<String> lines = new ArrayList<String>();
+
+    private final Set<MethodInfo> defaultForceInlineMethods = new TreeSet<MethodInfo>(new Comparator<MethodInfo>() {
+        @Override
+        public int compare(MethodInfo o1, MethodInfo o2) {
+            return o1.getQualifiedName().compareTo(o2.getQualifiedName());
+        }
+    });
+
+    private final Set<ClassInfo> defaultForceInlineClasses = new TreeSet<ClassInfo>(new Comparator<ClassInfo>() {
+        @Override
+        public int compare(ClassInfo o1, ClassInfo o2) {
+            return o1.getQualifiedName().compareTo(o2.getQualifiedName());
+        }
+    });
+
+    public void defaultForceInline(MethodInfo methodInfo) {
+        defaultForceInlineMethods.add(methodInfo);
+    }
+
+    public void defaultForceInline(ClassInfo classInfo) {
+        defaultForceInlineClasses.add(classInfo);
+    }
+
+    public void process(GeneratorSource source) {
+        try {
+            for (MethodInfo element : BenchmarkGeneratorUtils.getMethodsAnnotatedWith(source, CompilerControl.class)) {
+                CompilerControl ann = element.getAnnotation(CompilerControl.class);
+                if (ann == null) {
+                    throw new IllegalStateException("No annotation");
+                }
+
+                CompilerControl.Mode command = ann.value();
+                lines.add(command.command() + "," + getName(element));
+            }
+
+            for (MethodInfo element : defaultForceInlineMethods) {
+                if (element.getAnnotation(CompilerControl.class) != null) continue;
+                lines.add(CompilerControl.Mode.INLINE.command() + "," + getName(element));
+            }
+
+            for (ClassInfo element : BenchmarkGeneratorUtils.getClassesAnnotatedWith(source, CompilerControl.class)) {
+                CompilerControl ann = element.getAnnotation(CompilerControl.class);
+                if (ann == null) {
+                    throw new IllegalStateException("No annotation");
+                }
+
+                CompilerControl.Mode command = ann.value();
+                lines.add(command.command() + "," + getName(element));
+            }
+
+            for (ClassInfo element : defaultForceInlineClasses) {
+                if (element.getAnnotation(CompilerControl.class) != null) continue;
+                lines.add(CompilerControl.Mode.INLINE.command() + "," + getName(element));
+            }
+        } catch (Throwable t) {
+            source.printError("Compiler control processor had thrown the unexpected exception", t);
+        }
+    }
+
+    @Override
+    public void finish(GeneratorSource source) {
+        try {
+            PrintWriter writer = new PrintWriter(source.newResource(CompilerHints.LIST.substring(1)));
+            for (String line : lines) {
+                writer.println(line);
+            }
+            writer.close();
+        } catch (IOException ex) {
+            source.printError("Error writing compiler hint list ", ex);
+        } catch (Throwable t) {
+            source.printError("Compiler control processor had thrown the unexpected exception", t);
+        }
+    }
+
+    private static String getName(MethodInfo mi) {
+       return mi.getOwner().getQualifiedName().replaceAll("\\.", "/") + "." + mi.getName();
+    }
+
+    private static String getName(ClassInfo ci) {
+      return ci.getQualifiedName().replaceAll("\\.", "/") + ".*";
+    }
+
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/CompilerControlProcessor.java	Fri Feb 21 19:25:28 2014 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,111 +0,0 @@
-/*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.openjdk.jmh.processor.internal;
-
-import org.openjdk.jmh.annotations.CompilerControl;
-import org.openjdk.jmh.runner.CompilerHints;
-
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.RoundEnvironment;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.TypeElement;
-import javax.tools.Diagnostic.Kind;
-import javax.tools.FileObject;
-import javax.tools.StandardLocation;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-
-public class CompilerControlProcessor implements SubProcessor {
-
-    private final List<String> lines = new ArrayList<String>();
-    private final Set<Element> defaultForceInline = new TreeSet<Element>(new Comparator<Element>() {
-        @Override
-        public int compare(Element o1, Element o2) {
-            return getName(o1).compareTo(getName(o2));
-        }
-    });
-
-    public void defaultForceInline(Element element) {
-        defaultForceInline.add(element);
-    }
-
-    public void process(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv) {
-        try {
-            for (Element element : roundEnv.getElementsAnnotatedWith(CompilerControl.class)) {
-                CompilerControl ann = element.getAnnotation(CompilerControl.class);
-                if (ann == null) {
-                    throw new IllegalStateException("No annotation");
-                }
-
-                CompilerControl.Mode command = ann.value();
-                lines.add(command.command() + "," + getName(element));
-            }
-
-            for (Element element : defaultForceInline) {
-                if (element.getAnnotation(CompilerControl.class) != null) continue;
-                lines.add(CompilerControl.Mode.INLINE.command() + "," + getName(element));
-            }
-        } catch (Throwable t) {
-            processingEnv.getMessager().printMessage(Kind.ERROR, "Annotation processor had thrown exception: " + t);
-            t.printStackTrace(System.err);
-        }
-    }
-
-    @Override
-    public void finish(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv) {
-        try {
-            FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "",
-                    CompilerHints.LIST.substring(1));
-            PrintWriter writer = new PrintWriter(file.openWriter());
-            for (String line : lines) {
-                writer.println(line);
-            }
-            writer.close();
-        } catch (IOException ex) {
-            processingEnv.getMessager().printMessage(Kind.ERROR, "Error writing compiler hint list " + ex);
-        } catch (Throwable t) {
-            processingEnv.getMessager().printMessage(Kind.ERROR, "Annotation processor had thrown exception: " + t);
-            t.printStackTrace(System.err);
-        }
-    }
-
-    private static String getName(Element element) {
-        switch (element.getKind()) {
-            case CLASS:
-                return ((TypeElement)element).getQualifiedName().toString().replaceAll("\\.", "/") + ".*";
-            case METHOD:
-                return ((TypeElement)element.getEnclosingElement()).getQualifiedName().toString().replaceAll("\\.", "/") + "." + element.getSimpleName().toString();
-            default:
-                throw new GenerationException("@" + CompilerControl.class.getSimpleName() + " annotation is placed within " +
-                                "unexpected target", element);
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/FieldInfo.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+import java.lang.annotation.Annotation;
+
+public interface FieldInfo extends MetadataInfo {
+
+    String getName();
+
+    String getType();
+
+    boolean isPublic();
+
+    boolean isStatic();
+
+    <T extends Annotation> T getAnnotation(Class<T> annClass);
+
+    ClassInfo getOwner();
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GenerateMicroBenchmarkProcessor.java	Fri Feb 21 19:25:28 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GenerateMicroBenchmarkProcessor.java	Wed Feb 26 17:06:12 2014 +0400
@@ -24,71 +24,15 @@
  */
 package org.openjdk.jmh.processor.internal;
 
-import org.openjdk.jmh.annotations.BenchmarkMode;
-import org.openjdk.jmh.annotations.CompilerControl;
-import org.openjdk.jmh.annotations.Fork;
-import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
-import org.openjdk.jmh.annotations.Group;
-import org.openjdk.jmh.annotations.GroupThreads;
-import org.openjdk.jmh.annotations.Measurement;
-import org.openjdk.jmh.annotations.Mode;
-import org.openjdk.jmh.annotations.OperationsPerInvocation;
-import org.openjdk.jmh.annotations.Param;
-import org.openjdk.jmh.annotations.Scope;
-import org.openjdk.jmh.annotations.State;
-import org.openjdk.jmh.annotations.Threads;
-import org.openjdk.jmh.annotations.Warmup;
-import org.openjdk.jmh.logic.BlackHole;
-import org.openjdk.jmh.logic.InfraControl;
-import org.openjdk.jmh.logic.ThreadControl;
-import org.openjdk.jmh.logic.results.AverageTimeResult;
-import org.openjdk.jmh.logic.results.RawResults;
-import org.openjdk.jmh.logic.results.Result;
-import org.openjdk.jmh.logic.results.ResultRole;
-import org.openjdk.jmh.logic.results.SampleTimeResult;
-import org.openjdk.jmh.logic.results.SingleShotResult;
-import org.openjdk.jmh.logic.results.ThroughputResult;
-import org.openjdk.jmh.runner.BenchmarkRecord;
-import org.openjdk.jmh.runner.MicroBenchmarkList;
-import org.openjdk.jmh.util.internal.HashMultimap;
-import org.openjdk.jmh.util.internal.Multimap;
-import org.openjdk.jmh.util.internal.SampleBuffer;
+import org.openjdk.jmh.processor.internal.annotations.AnnProcessGeneratorSource;
 
-import javax.annotation.Generated;
 import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.ProcessingEnvironment;
 import javax.annotation.processing.RoundEnvironment;
 import javax.annotation.processing.SupportedAnnotationTypes;
 import javax.annotation.processing.SupportedSourceVersion;
 import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
 import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.util.ElementFilter;
-import javax.tools.Diagnostic.Kind;
-import javax.tools.FileObject;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardLocation;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.annotation.IncompleteAnnotationException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
 import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
 
 /**
  * @author staffan.friberg@oracle.com
@@ -99,976 +43,17 @@
 @SupportedSourceVersion(SourceVersion.RELEASE_6)
 public class GenerateMicroBenchmarkProcessor extends AbstractProcessor {
 
-    private final Set<BenchmarkInfo> benchmarkInfos = new HashSet<BenchmarkInfo>();
-
-    private final Collection<SubProcessor> subProcessors = new ArrayList<SubProcessor>();
-    private CompilerControlProcessor compilerControl;
-
-    @Override
-    public synchronized void init(ProcessingEnvironment processingEnv) {
-        super.init(processingEnv);
-
-        compilerControl = new CompilerControlProcessor();
-
-        subProcessors.add(new HelperMethodValidationProcessor());
-        subProcessors.add(new GroupValidationProcessor());
-        subProcessors.add(new ParamValidationProcessor());
-        subProcessors.add(compilerControl);
-    }
+    private final BenchmarkGenerator generator = new BenchmarkGenerator();
 
     @Override
     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
-        try {
-            for (SubProcessor sub : subProcessors) {
-                sub.process(roundEnv, processingEnv);
-            }
-
-            if (!roundEnv.processingOver()) {
-                TypeElement gmb = processingEnv.getElementUtils().getTypeElement(GenerateMicroBenchmark.class.getCanonicalName());
-                // Build a Set of classes with a list of annotated methods
-                Multimap<TypeElement, Element> clazzes = buildAnnotatedSet(gmb, roundEnv);
-
-                // Generate code for all found Classes and Methods
-                for (TypeElement clazz : clazzes.keys()) {
-                    try {
-                        validateBenchmark(clazz, clazzes.get(clazz));
-                        BenchmarkInfo info = makeBenchmarkInfo(clazz, clazzes.get(clazz));
-                        generateClass(clazz, info);
-                        benchmarkInfos.add(info);
-                    } catch (GenerationException ge) {
-                        processingEnv.getMessager().printMessage(Kind.ERROR, ge.getMessage(), ge.getElement());
-                    }
-                }
-            } else {
-                for (SubProcessor sub : subProcessors) {
-                    sub.finish(roundEnv, processingEnv);
-                }
-
-                // Processing completed, final round. Print all added methods to file
-                try {
-                    FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "",
-                            MicroBenchmarkList.MICROBENCHMARK_LIST.substring(1));
-                    PrintWriter writer = new PrintWriter(file.openWriter());
-                    for (BenchmarkInfo info : benchmarkInfos) {
-                        for (String method : info.methodGroups.keySet()) {
-                            MethodGroup group = info.methodGroups.get(method);
-                            for (Mode m : group.getModes()) {
-                                BenchmarkRecord br = new BenchmarkRecord(
-                                        info.userName + "." + method,
-                                        info.generatedName + "." + method,
-                                        m,
-                                        group.getThreads(),
-                                        group.getTotalThreadCount(),
-                                        group.getWarmupIterations(),
-                                        group.getWarmupTime(),
-                                        group.getWarmupBatchSize(),
-                                        group.getMeasurementIterations(),
-                                        group.getMeasurementTime(),
-                                        group.getMeasurementBatchSize(),
-                                        group.getForks(),
-                                        group.getWarmupForks(),
-                                        group.getJVMArgs(),
-                                        group.getJVMArgsPrepend(),
-                                        group.getJVMArgsAppend(),
-                                        group.getParams()
-                                );
-                                writer.println(br.toLine());
-                            }
-                        }
-                    }
-
-                    writer.close();
-                } catch (IOException ex) {
-                    processingEnv.getMessager().printMessage(Kind.ERROR, "Error writing MicroBenchmark list " + ex);
-                }
-            }
-        } catch (Throwable t) {
-            processingEnv.getMessager().printMessage(Kind.ERROR, "Annotation processor had throw exception: " + t);
-            t.printStackTrace(System.err);
-        }
-
-        return true;
-    }
-
-    private List<TypeElement> getHierarchy(TypeElement subclass) {
-        List<TypeElement> result = new ArrayList<TypeElement>();
-        TypeElement walk = subclass;
-        do {
-            result.add(walk);
-        } while ((walk = (TypeElement) processingEnv.getTypeUtils().asElement(walk.getSuperclass())) != null);
-        return result;
-    }
-
-    /**
-     * Build a set of Classes which has annotated methods in them
-     *
-     * @return for all methods annotated with $annotation, returns Map<holder-class, Set<method>>
-     */
-    private Multimap<TypeElement, Element> buildAnnotatedSet(TypeElement te, RoundEnvironment roundEnv) {
-
-        // Need to do a few rollovers to find all classes that have @GMB-annotated methods in their
-        // subclasses. This is mostly due to some of the nested classes not discoverable at once,
-        // when we need to discover the enclosing class first. With the potentially non-zero nesting
-        // depth, we need to do a few rounds. Hopefully we will just do a single stride in most
-        // cases.
-
-        Collection<TypeElement> discoveredClasses = new TreeSet<TypeElement>(new Comparator<TypeElement>() {
-            @Override
-            public int compare(TypeElement o1, TypeElement o2) {
-                return o1.getQualifiedName().toString().compareTo(o2.getQualifiedName().toString());
-            }
-        });
-
-        // Walk around until convergence...
-
-        int lastSize = -1;
-        while (discoveredClasses.size() > lastSize) {
-            lastSize = discoveredClasses.size();
-            for (Element e : roundEnv.getRootElements()) {
-                if (e.getKind() != ElementKind.CLASS) continue;
-                TypeElement walk = (TypeElement) e;
-                do {
-                    discoveredClasses.add(walk);
-                    for (TypeElement nested : ElementFilter.typesIn(walk.getEnclosedElements())) {
-                        discoveredClasses.add(nested);
-                    }
-                } while ((walk = (TypeElement) processingEnv.getTypeUtils().asElement(walk.getSuperclass())) != null);
-            }
-        }
-
-        // Transitively close the hierarchy:
-        //   If superclass has a @GMB method, then all subclasses also have it.
-        //   We skip the generated classes, which we had probably generated during the previous rounds
-        //   of processing. Abstract classes are of no interest for us either.
-
-        Multimap<TypeElement, Element> result = new HashMultimap<TypeElement, Element>();
-        for (TypeElement currentClass : discoveredClasses) {
-            if (AnnUtils.getPackageName(currentClass).contains("generated")) continue;
-            if (currentClass.getModifiers().contains(Modifier.ABSTRACT)) continue;
-
-            for (TypeElement upperClass : getHierarchy(currentClass)) {
-                if (AnnUtils.getPackageName(upperClass).contains("generated")) continue;
-                for (ExecutableElement method : ElementFilter.methodsIn(upperClass.getEnclosedElements())) {
-                    GenerateMicroBenchmark ann = method.getAnnotation(GenerateMicroBenchmark.class);
-                    if (ann != null) {
-                        result.put(currentClass, method);
-                    }
-                }
-            }
-        }
-        return result;
-    }
-
-
-    /**
-     * Do basic benchmark validation.
-     */
-    private void validateBenchmark(TypeElement clazz, Collection<? extends Element> methods) {
-        if (AnnUtils.getPackageName(clazz).isEmpty()) {
-            throw new GenerationException("Microbenchmark should have package other than default.", clazz);
-        }
-
-        Collection<TypeElement> states = new ArrayList<TypeElement>();
-
-        // validate all arguments are @State-s
-        for (Element e : methods) {
-            ExecutableElement method = (ExecutableElement) e;
-            for (VariableElement var : method.getParameters()) {
-                TypeElement argClass = (TypeElement) processingEnv.getTypeUtils().asElement(var.asType());
-                if (argClass.getAnnotation(State.class) == null) {
-                    throw new GenerationException(
-                            "The " + GenerateMicroBenchmark.class.getSimpleName() +
-                            " annotation only supports methods with @State-bearing typed parameters.",
-                            var);
-                }
-                states.add(argClass);
-            }
-        }
-
-        // validate if enclosing class is implicit @State
-        if (clazz.getAnnotation(State.class) != null) {
-            states.add(clazz);
-        }
-
-        // validate @State classes
-        for (TypeElement state : states) {
-            // Because of https://bugs.openjdk.java.net/browse/JDK-8031122,
-            // we need to preemptively check the annotation value, and
-            // the API can only allow that by catching the exception, argh.
-            try {
-                state.getAnnotation(State.class).value();
-            } catch (IncompleteAnnotationException iae) {
-                throw new GenerationException("The " + State.class.getSimpleName() +
-                        " annotation should have the explicit " + Scope.class.getSimpleName() + " argument",
-                        state);
-            }
-
-            if (!state.getModifiers().contains(Modifier.PUBLIC)) {
-                throw new GenerationException("The " + State.class.getSimpleName() +
-                        " annotation only supports public classes.", state);
-            }
-            if (state.getNestingKind().isNested() && !state.getModifiers().contains(Modifier.STATIC)) {
-                throw new GenerationException("The " + State.class.getSimpleName() +
-                        " annotation does not support inner classes, make sure the class is nested (static).",
-                        state);
-            }
-
-            boolean hasDefaultConstructor = false;
-            for (ExecutableElement constructor : ElementFilter.constructorsIn(state.getEnclosedElements())) {
-                hasDefaultConstructor |= (constructor.getParameters().isEmpty() && constructor.getModifiers().contains(Modifier.PUBLIC));
-            }
-
-            if (!hasDefaultConstructor) {
-                throw new GenerationException("The " + State.class.getSimpleName() +
-                        " annotation can only be applied to the classes having the default public constructor.",
-                        state);
-            }
-        }
-
-        // validate against rogue fields
-        if (clazz.getAnnotation(State.class) == null || clazz.getModifiers().contains(Modifier.ABSTRACT)) {
-            for (VariableElement field : ElementFilter.fieldsIn(clazz.getEnclosedElements())) {
-                // allow static fields
-                if (!field.getModifiers().contains(Modifier.STATIC)) {
-                    throw new GenerationException(
-                        "Field \"" + field + "\" is declared within " +
-                                "the class not having @" + State.class.getSimpleName() + " annotation. " +
-                                "This can result in unspecified behavior, and prohibited.", field);
-                }
-            }
-        }
-
-        // check modifiers
-        for (Element m : methods) {
-            if (!m.getModifiers().contains(Modifier.PUBLIC)) {
-                throw new GenerationException("@" + GenerateMicroBenchmark.class.getSimpleName() +
-                        " method should be public.", m);
-            }
-
-            if (m.getModifiers().contains(Modifier.ABSTRACT)) {
-                throw new GenerationException("@" + GenerateMicroBenchmark.class.getSimpleName()
-                        + " method can not be abstract.", m);
-            }
-            if (m.getModifiers().contains(Modifier.SYNCHRONIZED)) {
-                if (clazz.getAnnotation(State.class) == null) {
-                    throw new GenerationException("@" + GenerateMicroBenchmark.class.getSimpleName()
-                            + " method can only be synchronized if the enclosing class is annotated with "
-                            + "@" + State.class.getSimpleName() + ".", m);
-                    }
-            }
-        }
-
-        // check annotations
-        for (Element m : methods) {
-            OperationsPerInvocation opi = AnnUtils.getAnnotationRecursive(m, OperationsPerInvocation.class);
-            if (opi != null && opi.value() < 1) {
-                throw new GenerationException("The " + OperationsPerInvocation.class.getSimpleName() +
-                        " needs to be greater than 0.", m);
-            }
-        }
-    }
-
-    /**
-     * validate benchmark info
-     * @param info benchmark info to validate
-     */
-    private void validateBenchmarkInfo(BenchmarkInfo info) {
-        // check the @Group preconditions,
-        // ban some of the surprising configurations
-        //
-        for (MethodGroup group : info.methodGroups.values()) {
-            if (group.methods().size() == 1) {
-                ExecutableElement meth = (ExecutableElement) group.methods().iterator().next();
-                if (meth.getAnnotation(Group.class) == null) {
-                    for (VariableElement param : meth.getParameters()) {
-                        TypeElement stateType = (TypeElement) processingEnv.getTypeUtils().asElement(param.asType());
-                        State stateAnn = stateType.getAnnotation(State.class);
-                        if (stateAnn != null && stateAnn.value() == Scope.Group) {
-                            throw new GenerationException(
-                                    "Only @" + Group.class.getSimpleName() + " methods can reference @" + State.class.getSimpleName()
-                                            + "(" + Scope.class.getSimpleName() + "." + Scope.Group + ") states.",
-                                    meth);
-                        }
-                    }
-
-                    State stateAnn = meth.getEnclosingElement().getAnnotation(State.class);
-                    if (stateAnn != null && stateAnn.value() == Scope.Group) {
-                        throw new GenerationException(
-                                "Only @" + Group.class.getSimpleName() + " methods can implicitly reference @" + State.class.getSimpleName()
-                                        + "(" + Scope.class.getSimpleName() + "." + Scope.Group + ") states.",
-                                meth);
-                    }
-                }
-            } else {
-                for (Element m : group.methods()) {
-                    if (m.getAnnotation(Group.class) == null) {
-                        throw new GenerationException(
-                                "Internal error: multiple methods per @" + Group.class.getSimpleName()
-                                        + ", but not all methods have @" + Group.class.getSimpleName(),
-                                m);
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Generate BenchmarkInfo for given class.
-     * We will figure out method groups at this point.
-     *
-     * @param clazz holder class
-     * @param methods annotated methods
-     * @return BenchmarkInfo
-     */
-    private BenchmarkInfo makeBenchmarkInfo(TypeElement clazz, Collection<? extends Element> methods) {
-        Map<String, MethodGroup> result = new TreeMap<String, MethodGroup>();
-
-        boolean classStrictFP = clazz.getModifiers().contains(Modifier.STRICTFP);
-
-        for (Element method : methods) {
-
-            boolean methodStrictFP = method.getModifiers().contains(Modifier.STRICTFP);
-
-            Group groupAnn = method.getAnnotation(Group.class);
-            String groupName = (groupAnn != null) ? groupAnn.value() : method.getSimpleName().toString();
-
-            if (!checkJavaIdentifier(groupName)) {
-                throw new GenerationException("Group name should be the legal Java identifier.", method);
-            }
-
-            MethodGroup group = result.get(groupName);
-            if (group == null) {
-                group = new MethodGroup(groupName);
-                result.put(groupName, group);
-            }
-
-            BenchmarkMode mbAn = AnnUtils.getAnnotationRecursive(method, BenchmarkMode.class);
-            if (mbAn != null) {
-                group.addModes(mbAn.value());
-            }
-
-            group.addStrictFP(classStrictFP);
-            group.addStrictFP(methodStrictFP);
-            group.addMethod(method, (method.getAnnotation(GroupThreads.class) != null) ? method.getAnnotation(GroupThreads.class).value() : 1);
-
-            // Discovering @Params, part 1:
-            //   For each parameter, walk the type hierarchy up to discover inherited @Param fields in @State objects.
-            ExecutableElement execMethod = (ExecutableElement) method;
-            for (VariableElement element : execMethod.getParameters()) {
-                TypeElement walk = (TypeElement) processingEnv.getTypeUtils().asElement(element.asType());
-                do {
-                    for (VariableElement ve : ElementFilter.fieldsIn(walk.getEnclosedElements())) {
-                        if (ve.getAnnotation(Param.class) != null) {
-                            group.addParam(ve.getSimpleName().toString(), ve.getAnnotation(Param.class).value());
-                        }
-                    }
-                } while ((walk = (TypeElement) processingEnv.getTypeUtils().asElement(walk.getSuperclass())) != null);
-            }
-
-            // Discovering @Params, part 2:
-            //  Walk the type hierarchy up to discover inherited @Param fields for class.
-            TypeElement walk = clazz;
-            do {
-                for (VariableElement ve : ElementFilter.fieldsIn(walk.getEnclosedElements())) {
-                    if (ve.getAnnotation(Param.class) != null) {
-                        group.addParam(ve.getSimpleName().toString(), ve.getAnnotation(Param.class).value());
-                    }
-                }
-            } while ((walk = (TypeElement) processingEnv.getTypeUtils().asElement(walk.getSuperclass())) != null);
-        }
-
-        // enforce the default value
-        for (MethodGroup group : result.values()) {
-            if (group.getModes().isEmpty()) {
-                group.addModes(Mode.Throughput);
-            }
-        }
-
-        String sourcePackage = AnnUtils.getPackageName(clazz);
-        String generatedPackageName = sourcePackage + ".generated";
-        String generatedClassName = AnnUtils.getNestedName(clazz);
-
-        BenchmarkInfo info = new BenchmarkInfo(clazz.getQualifiedName().toString(), generatedPackageName, generatedClassName, result);
-        validateBenchmarkInfo(info);
-        return info;
-    }
-
-    public static boolean checkJavaIdentifier(String id) {
-        for (int i = 0; i < id.length(); i++) {
-            char c = id.charAt(i);
-            if (!Character.isJavaIdentifierPart(c)) {
-                return false;
-            }
+        GeneratorSource source = new AnnProcessGeneratorSource(roundEnv, processingEnv);
+        if (!roundEnv.processingOver()) {
+            generator.generate(source);
+        } else {
+            generator.complete(source);
         }
         return true;
     }
 
-    /**
-     * Create and generate Java code for a class and it's methods
-     *
-     * @param clazz
-     */
-    private void generateClass(TypeElement clazz, BenchmarkInfo info) {
-        try {
-            // Create file and open an outputstream
-            JavaFileObject jof = processingEnv.getFiler().createSourceFile(info.generatedName, clazz);
-            PrintWriter writer = new PrintWriter(jof.openWriter(), false);
-
-            // Write package and imports
-            writer.println("package " + info.generatedPackageName + ';');
-            writer.println();
-
-            generateImport(writer);
-
-            // Write class header
-            writer.println("@" + CompilerControl.class.getSimpleName() +
-                    "(" + CompilerControl.class.getSimpleName() + "." + CompilerControl.Mode.class.getSimpleName() +
-                    "." + CompilerControl.Mode.DONT_INLINE + ")");
-            writer.println("@Generated(\"" + GenerateMicroBenchmarkProcessor.class.getCanonicalName() + "\")");
-            writer.println("public final class " + info.generatedClassName + " {");
-            writer.println();
-            generatePadding(writer);
-
-            generateFields(writer);
-
-            StateObjectHandler states = new StateObjectHandler(processingEnv);
-
-            // benchmark instance is implicit
-            states.bindImplicit(clazz, "bench", Scope.Thread);
-
-            // default blackhole is implicit
-            states.bindImplicit(processingEnv.getElementUtils().getTypeElement(BlackHole.class.getCanonicalName()), "blackhole", Scope.Thread);
-
-            // Write all methods
-            for (String groupName : info.methodGroups.keySet()) {
-                for (Element method : info.methodGroups.get(groupName).methods()) {
-                    // Look for method signature and figure out state bindings
-                    ExecutableElement execMethod = (ExecutableElement) method;
-                    for (VariableElement element : execMethod.getParameters()) {
-                        TypeElement stateType = (TypeElement) processingEnv.getTypeUtils().asElement(element.asType());
-                        states.bindArg(execMethod, stateType);
-                    }
-                }
-
-                for (Mode benchmarkKind : Mode.values()) {
-                    if (benchmarkKind == Mode.All) continue;
-                    generateMethod(benchmarkKind, writer, info.methodGroups.get(groupName), states);
-                }
-                states.clearArgs();
-            }
-
-            // Write out state initializers
-            for (String s : states.getStateInitializers()) {
-                writer.println("    " + s);
-            }
-            writer.println();
-
-            // Write out the required fields
-            for (String s : states.getFields()) {
-                writer.println("    " + s);
-            }
-            writer.println();
-
-            // Write out the required objects
-            for (String s : states.getStateOverrides()) {
-                writer.println("    " + s);
-            }
-            writer.println();
-
-            // Finish class
-            writer.println("}");
-            writer.println();
-
-            writer.close();
-        } catch (IOException ex) {
-            throw new GenerationException("IOException", ex, clazz);
-        }
-    }
-
-    private void generateFields(PrintWriter writer) {
-        // nothing here
-    }
-
-    private void generatePadding(PrintWriter writer) {
-        for (int p = 0; p < 16; p++) {
-            StringBuilder sb = new StringBuilder();
-            sb.append("    boolean jmh_bench_pad_").append(p);
-            for (int q = 1; q < 16; q++) {
-                sb.append(", jmh_bench_pad_").append(p).append("_").append(q);
-            }
-            sb.append(";");
-            writer.println(sb.toString());
-        }
-    }
-
-    private void generateImport(PrintWriter writer) {
-        Class<?>[] imports = new Class<?>[] {
-                List.class, AtomicInteger.class, AtomicIntegerFieldUpdater.class,
-                Collection.class, Collections.class, ArrayList.class, Arrays.class,
-                TimeUnit.class, Generated.class, CompilerControl.class,
-                InfraControl.class, ThreadControl.class, BlackHole.class,
-                Result.class, ThroughputResult.class, AverageTimeResult.class,
-                SampleTimeResult.class, SingleShotResult.class, SampleBuffer.class,
-                Mode.class, Fork.class, Measurement.class, Threads.class, Warmup.class,
-                BenchmarkMode.class, RawResults.class, ResultRole.class
-        };
-
-        for (Class<?> c : imports) {
-            writer.println("import " + c.getName() + ';');
-        }
-        writer.println();
-    }
-
-     /**
-     * Generate the method for a specific benchmark method
-     *
-     * @param benchmarkKind
-     * @param writer
-     * @param methodGroup
-     */
-    private void generateMethod(Mode benchmarkKind, PrintWriter writer, MethodGroup methodGroup, StateObjectHandler states) {
-        writer.println();
-        switch (benchmarkKind) {
-            case Throughput:
-                generateThroughput(writer, benchmarkKind, methodGroup, states);
-                break;
-            case AverageTime:
-                generateAverageTime(writer, benchmarkKind, methodGroup, states);
-                break;
-            case SampleTime:
-                generateSampleTime(writer, benchmarkKind, methodGroup, states);
-                break;
-            case SingleShotTime:
-                generateSingleShotTime(writer, benchmarkKind, methodGroup, states);
-                break;
-            default:
-                throw new AssertionError("Shouldn't be here");
-        }
-    }
-
-    private void generateThroughput(PrintWriter writer, Mode benchmarkKind, MethodGroup methodGroup, StateObjectHandler states) {
-        writer.println(ident(1) + "public Collection<? extends Result> " + methodGroup.getName() + "_" + benchmarkKind + "(InfraControl control, ThreadControl threadControl) throws Throwable {");
-        writer.println();
-
-        methodProlog(writer, methodGroup);
-
-        boolean isSingleMethod = (methodGroup.methods().size() == 1);
-        int subGroup = -1;
-        for (Element method : methodGroup.methods()) {
-            compilerControl.defaultForceInline(method);
-
-            subGroup++;
-
-            writer.println(ident(2) + "if (threadControl.subgroup == " + subGroup + ") {");
-
-            iterationProlog(writer, 3, method, states);
-
-            // synchronize iterations prolog: announce ready
-            writer.println(ident(3) + "control.announceWarmupReady();");
-
-            // synchronize iterations prolog: catchup loop
-            writer.println(ident(3) + "while (control.warmupShouldWait) {");
-
-            invocationProlog(writer, 4, method, states, false);
-            writer.println(ident(4) + emitCall(method, states) + ';');
-            invocationEpilog(writer, 4, method, states, false);
-
-            writer.println(ident(3) + "}");
-            writer.println();
-
-            // control objects get a special treatment
-            for (StateObject so : states.getControls()) {
-                writer.println(ident(3) + so.localIdentifier + ".startMeasurement = true;");
-                writer.println(ident(3) + so.localIdentifier + ".iterationTime = control.getDuration();");
-            }
-
-            // measurement loop call
-            writer.println(ident(3) + "RawResults res = new RawResults(" + methodGroup.getOperationsPerInvocation() + "L);");
-            writer.println(ident(3) + method.getSimpleName() + "_" + benchmarkKind + "_measurementLoop(control, res, " + states.getImplicit("bench").toLocal() + ", " + states.getImplicit("blackhole").toLocal() + prefix(states.getArgList(method)) + ");");
-
-            // control objects get a special treatment
-            for (StateObject so : states.getControls()) {
-                writer.println(ident(3) + so.localIdentifier + ".stopMeasurement = true;");
-            }
-
-            // synchronize iterations epilog: announce ready
-            writer.println(ident(3) + "control.announceWarmdownReady();");
-
-            // synchronize iterations epilog: catchup loop
-            writer.println(ident(3) + "while (control.warmdownShouldWait) {");
-
-            invocationProlog(writer, 4, method, states, false);
-            writer.println(ident(4) + emitCall(method, states) + ';');
-            invocationEpilog(writer, 4, method, states, false);
-
-            writer.println(ident(3) + "}");
-
-            // iteration prolog
-            iterationEpilog(writer, 3, method, states);
-
-            writer.println(ident(3) + "Collection<Result> results = new ArrayList<Result>();");
-            writer.println(ident(3) + "TimeUnit tu = (control.timeUnit != null) ? control.timeUnit : TimeUnit." + methodGroup.getOutputTimeUnit() + ";");
-            writer.println(ident(3) + "results.add(new ThroughputResult(ResultRole.PRIMARY, \"" + method.getSimpleName() + "\", res.getOperations(), res.getTime(), tu));");
-            if (!isSingleMethod) {
-                writer.println(ident(3) + "results.add(new ThroughputResult(ResultRole.SECONDARY, \"" + method.getSimpleName() + "\", res.getOperations(), res.getTime(), tu));");
-            }
-            for (String ops : states.getAuxResultNames(method)) {
-                writer.println(ident(3) + "results.add(new ThroughputResult(ResultRole.SECONDARY, \"" + ops + "\", " + states.getAuxResultAccessor(method, ops) + ", res.getTime(), tu));");
-            }
-            writer.println(ident(3) + "return results;");
-            writer.println(ident(2) + "} else");
-        }
-        writer.println(ident(3) + "throw new IllegalStateException(\"Harness failed to distribute threads among groups properly\");");
-        writer.println();
-
-        writer.println(ident(1) + "}");
-
-        // measurement loop bodies
-        for (Element method : methodGroup.methods()) {
-            String methodName = method.getSimpleName() + "_" + benchmarkKind + "_measurementLoop";
-            writer.println("    public " + (methodGroup.isStrictFP() ? "strictfp" : "") + " void " + methodName + "(InfraControl control, RawResults result, " + states.getImplicit("bench").toTypeDef() + ", " + states.getImplicit("blackhole").toTypeDef() + prefix(states.getTypeArgList(method)) + ") throws Throwable {");
-            writer.println("        long operations = 0;");
-            writer.println("        long realTime = 0;");
-            writer.println("        result.startTime = System.nanoTime();");
-            writer.println("        do {");
-
-            invocationProlog(writer, 3, method, states, true);
-            writer.println(ident(3) + emitCall(method, states) + ';');
-            invocationEpilog(writer, 3, method, states, true);
-
-            writer.println("            operations++;");
-            writer.println("        } while(!control.isDone);");
-            writer.println("        result.stopTime = System.nanoTime();");
-            writer.println("        result.realTime = realTime;");
-            writer.println("        result.operations = operations;");
-            writer.println("    }");
-            writer.println();
-        }
-    }
-
-    private void generateAverageTime(PrintWriter writer, Mode benchmarkKind, MethodGroup methodGroup, StateObjectHandler states) {
-        writer.println(ident(1) + "public Collection<? extends Result> " + methodGroup.getName() + "_" + benchmarkKind + "(InfraControl control, ThreadControl threadControl) throws Throwable {");
-
-        methodProlog(writer, methodGroup);
-
-        boolean isSingleMethod = (methodGroup.methods().size() == 1);
-        int subGroup = -1;
-        for (Element method : methodGroup.methods()) {
-            compilerControl.defaultForceInline(method);
-
-            subGroup++;
-
-            writer.println(ident(2) + "if (threadControl.subgroup == " + subGroup + ") {");
-
-            iterationProlog(writer, 3, method, states);
-
-            // synchronize iterations prolog: announce ready
-            writer.println(ident(3) + "control.announceWarmupReady();");
-
-            // synchronize iterations prolog: catchup loop
-            writer.println(ident(3) + "while (control.warmupShouldWait) {");
-
-            invocationProlog(writer, 4, method, states, false);
-            writer.println(ident(4) + emitCall(method, states) + ';');
-            invocationEpilog(writer, 4, method, states, false);
-
-            writer.println(ident(3) + "}");
-            writer.println();
-
-            // control objects get a special treatment
-            for (StateObject so : states.getControls()) {
-                writer.println(ident(3) + so.localIdentifier + ".startMeasurement = true;");
-                writer.println(ident(3) + so.localIdentifier + ".iterationTime = control.getDuration();");
-            }
-
-            // measurement loop call
-            writer.println(ident(3) + "RawResults res = new RawResults(" + methodGroup.getOperationsPerInvocation() + "L);");
-            writer.println(ident(3) + method.getSimpleName() + "_" + benchmarkKind + "_measurementLoop(control, res, " + states.getImplicit("bench").toLocal() + ", " + states.getImplicit("blackhole").toLocal() + prefix(states.getArgList(method)) + ");");
-
-            // control objects get a special treatment
-            for (StateObject so : states.getControls()) {
-                writer.println(ident(3) + so.localIdentifier + ".stopMeasurement = true;");
-            }
-
-            // synchronize iterations epilog: announce ready
-            writer.println(ident(3) + "control.announceWarmdownReady();");
-
-            // synchronize iterations epilog: catchup loop
-            writer.println(ident(3) + "while (control.warmdownShouldWait) {");
-
-            invocationProlog(writer, 4, method, states, false);
-            writer.println(ident(4) + emitCall(method, states) + ';');
-            invocationEpilog(writer, 4, method, states, false);
-
-            writer.println(ident(3) + "}");
-
-            iterationEpilog(writer, 3, method, states);
-
-            writer.println(ident(3) + "Collection<Result> results = new ArrayList<Result>();");
-            writer.println(ident(3) + "TimeUnit tu = (control.timeUnit != null) ? control.timeUnit : TimeUnit." + methodGroup.getOutputTimeUnit() + ";");
-            writer.println(ident(3) + "results.add(new AverageTimeResult(ResultRole.PRIMARY, \"" + method.getSimpleName() + "\", res.getOperations(), res.getTime(), tu));");
-            if (!isSingleMethod) {
-                writer.println(ident(3) + "results.add(new AverageTimeResult(ResultRole.SECONDARY, \"" + method.getSimpleName() + "\", res.getOperations(), res.getTime(), tu));");
-            }
-            for (String ops : states.getAuxResultNames(method)) {
-                writer.println(ident(3) + "results.add(new AverageTimeResult(ResultRole.SECONDARY, \"" + ops + "\", " + states.getAuxResultAccessor(method, ops) + ", res.getTime(), tu));");
-            }
-            writer.println(ident(3) + "return results;");
-            writer.println(ident(2) + "} else");
-        }
-        writer.println(ident(3) + "throw new IllegalStateException(\"Harness failed to distribute threads among groups properly\");");
-        writer.println();
-
-        writer.println(ident(1) + "}");
-
-        // measurement loop bodies
-        for (Element method : methodGroup.methods()) {
-            writer.println("    public " + (methodGroup.isStrictFP() ? "strictfp" : "") +  " void " + method.getSimpleName() + "_" + benchmarkKind + "_measurementLoop(InfraControl control, RawResults result, " + states.getImplicit("bench").toTypeDef() + ", " + states.getImplicit("blackhole").toTypeDef() + prefix(states.getTypeArgList(method)) + ") throws Throwable {");
-            writer.println("        long operations = 0;");
-            writer.println("        long realTime = 0;");
-            writer.println("        result.startTime = System.nanoTime();");
-            writer.println("        do {");
-
-            invocationProlog(writer, 3, method, states, true);
-            writer.println(ident(3) + emitCall(method, states) + ';');
-            invocationEpilog(writer, 3, method, states, true);
-
-            writer.println("            operations++;");
-            writer.println("        } while(!control.isDone);");
-            writer.println("        result.stopTime = System.nanoTime();");
-            writer.println("        result.realTime = realTime;");
-            writer.println("        result.operations = operations;");
-            writer.println("    }");
-            writer.println();
-        }
-    }
-
-    private void methodProlog(PrintWriter writer, MethodGroup methodGroup) {
-        // do nothing
-    }
-
-    private String prefix(String argList) {
-        if (argList.trim().isEmpty()) {
-            return "";
-        } else {
-            return ", " + argList;
-        }
-    }
-
-    private void generateSampleTime(PrintWriter writer, Mode benchmarkKind, MethodGroup methodGroup, StateObjectHandler states) {
-        writer.println(ident(1) + "public Collection<? extends Result> " + methodGroup.getName() + "_" + benchmarkKind + "(InfraControl control, ThreadControl threadControl) throws Throwable {");
-        writer.println();
-
-        methodProlog(writer, methodGroup);
-
-        boolean isSingleMethod = (methodGroup.methods().size() == 1);
-        int subGroup = -1;
-        for (Element method : methodGroup.methods()) {
-            compilerControl.defaultForceInline(method);
-
-            subGroup++;
-
-            writer.println(ident(2) + "if (threadControl.subgroup == " + subGroup + ") {");
-
-            iterationProlog(writer, 3, method, states);
-
-            // synchronize iterations prolog: announce ready
-            writer.println(ident(3) + "control.announceWarmupReady();");
-
-            // synchronize iterations prolog: catchup loop
-            writer.println(ident(3) + "while (control.warmupShouldWait) {");
-
-            invocationProlog(writer, 4, method, states, false);
-            writer.println(ident(4) + emitCall(method, states) + ';');
-            invocationEpilog(writer, 4, method, states, false);
-
-            writer.println(ident(3) + "}");
-            writer.println();
-
-            // control objects get a special treatment
-            for (StateObject so : states.getControls()) {
-                writer.println(ident(3) + so.localIdentifier + ".startMeasurement = true;");
-                writer.println(ident(3) + so.localIdentifier + ".iterationTime = control.getDuration();");
-            }
-
-            // measurement loop call
-            writer.println(ident(3) + "SampleBuffer buffer = new SampleBuffer();");
-            writer.println(ident(3) + method.getSimpleName() + "_" + benchmarkKind + "_measurementLoop(control, buffer, " + states.getImplicit("bench").toLocal() + ", " + states.getImplicit("blackhole").toLocal() + prefix(states.getArgList(method)) + ");");
-
-            // control objects get a special treatment
-            for (StateObject so : states.getControls()) {
-                writer.println(ident(3) + so.localIdentifier + ".stopMeasurement = true;");
-            }
-
-            // synchronize iterations epilog: announce ready
-            writer.println(ident(3) + "control.announceWarmdownReady();");
-
-            // synchronize iterations epilog: catchup loop
-            writer.println(ident(3) + "while (control.warmdownShouldWait) {");
-
-            invocationProlog(writer, 4, method, states, false);
-            writer.println(ident(4) + emitCall(method, states) + ';');
-            invocationEpilog(writer, 4, method, states, false);
-
-            writer.println(ident(3) + "}");
-            writer.println();
-
-            iterationEpilog(writer, 3, method, states);
-
-            writer.println(ident(3) + "Collection<Result> results = new ArrayList<Result>();");
-            writer.println(ident(3) + "TimeUnit tu = (control.timeUnit != null) ? control.timeUnit : TimeUnit." + methodGroup.getOutputTimeUnit() + ";");
-            writer.println(ident(3) + "results.add(new SampleTimeResult(ResultRole.PRIMARY, \"" + method.getSimpleName() + "\", buffer, tu));");
-            if (!isSingleMethod) {
-                writer.println(ident(3) + "results.add(new SampleTimeResult(ResultRole.SECONDARY, \"" + method.getSimpleName() + "\", buffer, tu));");
-            }
-            writer.println(ident(3) + "return results;");
-            writer.println(ident(2) + "} else");
-        }
-        writer.println(ident(3) + "throw new IllegalStateException(\"Harness failed to distribute threads among groups properly\");");
-
-        writer.println(ident(1) + "}");
-
-        // measurement loop bodies
-        for (Element method : methodGroup.methods()) {
-            writer.println("    public " + (methodGroup.isStrictFP() ? "strictfp" : "") + " void " + method.getSimpleName() + "_" + benchmarkKind + "_measurementLoop(InfraControl control, SampleBuffer buffer, " + states.getImplicit("bench").toTypeDef() + ", " + states.getImplicit("blackhole").toTypeDef() + prefix(states.getTypeArgList(method)) + ") throws Throwable {");
-            writer.println("        long realTime = 0;");
-            writer.println("        int rnd = (int)System.nanoTime();");
-            writer.println("        int rndMask = 0;");
-            writer.println("        long time = 0;");
-            writer.println("        int currentStride = 0;");
-            writer.println("        do {");
-
-            invocationProlog(writer, 3, method, states, true);
-
-            writer.println("            rnd = (rnd * 1664525 + 1013904223);");
-            writer.println("            boolean sample = (rnd & rndMask) == 0;");
-            writer.println("            if (sample) {");
-            writer.println("                time = System.nanoTime();");
-            writer.println("            }");
-            writer.println("            " + emitCall(method, states) + ';');
-            writer.println("            if (sample) {");
-            writer.println("                buffer.add(System.nanoTime() - time);");
-            writer.println("                if (currentStride++ > 1000000) {");
-            writer.println("                    buffer.half();");
-            writer.println("                    currentStride = 0;");
-            writer.println("                    rndMask = (rndMask << 1) + 1;");
-            writer.println("                }");
-            writer.println("            }");
-
-            invocationEpilog(writer, 3, method, states, true);
-
-            writer.println("        } while(!control.isDone);");
-
-            writer.println("    }");
-            writer.println();
-        }
-    }
-
-    private void generateSingleShotTime(PrintWriter writer, Mode benchmarkKind, MethodGroup methodGroup, StateObjectHandler states) {
-        writer.println(ident(1) + "public Collection<? extends Result> " + methodGroup.getName() + "_" + benchmarkKind + "(InfraControl control, ThreadControl threadControl) throws Throwable {");
-
-        methodProlog(writer, methodGroup);
-
-        writer.println(ident(2) + "long realTime = 0;");
-
-        boolean isSingleMethod = (methodGroup.methods().size() == 1);
-        int subGroup = -1;
-        for (Element method : methodGroup.methods()) {
-            compilerControl.defaultForceInline(method);
-
-            subGroup++;
-
-            writer.println(ident(2) + "if (threadControl.subgroup == " + subGroup + ") {");
-
-            iterationProlog(writer, 3, method, states);
-
-            invocationProlog(writer, 3, method, states, false);
-
-            writer.println(ident(3) + "long time1 = System.nanoTime();");
-            writer.println(ident(3) + "int batchSize = control.batchSize;");
-            writer.println(ident(3) + "for (int b = 0; b < batchSize; b++) {");
-            writer.println(ident(4) + emitCall(method, states) + ';');
-            writer.println(ident(3) + "}");
-            writer.println(ident(3) + "long time2 = System.nanoTime();");
-
-            invocationEpilog(writer, 3, method, states, false);
-
-            iterationEpilog(writer, 3, method, states);
-
-            writer.println(ident(3) + "Collection<Result> results = new ArrayList<Result>();");
-            writer.println(ident(3) + "TimeUnit tu = (control.timeUnit != null) ? control.timeUnit : TimeUnit." + methodGroup.getOutputTimeUnit() + ";");
-            writer.println(ident(3) + "results.add(new SingleShotResult(ResultRole.PRIMARY, \"" + method.getSimpleName() + "\", (realTime > 0) ? realTime : (time2 - time1), tu));");
-            if (!isSingleMethod) {
-                writer.println(ident(3) + "results.add(new SingleShotResult(ResultRole.SECONDARY, \"" + method.getSimpleName() + "\", (realTime > 0) ? realTime : (time2 - time1), tu));");
-            }
-            writer.println(ident(3) + "return results;");
-            writer.println(ident(2) + "} else");
-        }
-        writer.println(ident(3) + "throw new IllegalStateException(\"Harness failed to distribute threads among groups properly\");");
-        writer.println();
-
-        writer.println(ident(1) + "}");
-    }
-
-    private void invocationProlog(PrintWriter writer, int prefix, Element method, StateObjectHandler states, boolean pauseMeasurement) {
-        if (!states.getInvocationSetups(method).isEmpty()) {
-            for (String s : states.getInvocationSetups(method))
-                writer.println(ident(prefix) + s);
-            if (pauseMeasurement)
-                writer.println(ident(prefix) + "long rt = System.nanoTime();");
-            writer.println();
-        }
-    }
-
-    private void invocationEpilog(PrintWriter writer, int prefix, Element method, StateObjectHandler states, boolean pauseMeasurement) {
-        if (!states.getInvocationTearDowns(method).isEmpty()) {
-            writer.println();
-            if (pauseMeasurement)
-                writer.println(ident(prefix) + "realTime += (System.nanoTime() - rt);");
-            for (String s : states.getInvocationTearDowns(method))
-                writer.println(ident(prefix) + s);
-            writer.println();
-        }
-    }
-
-    private void iterationProlog(PrintWriter writer, int prefix, Element method, StateObjectHandler states) {
-        for (String s : states.getStateGetters(method)) writer.println(ident(prefix) + s);
-        writer.println();
-
-        writer.println(ident(prefix) + "control.preSetup();");
-
-        for (String s : states.getIterationSetups(method)) writer.println(ident(prefix) + s);
-        writer.println();
-    }
-
-    private void iterationEpilog(PrintWriter writer, int prefix, Element method, StateObjectHandler states) {
-        writer.println(ident(prefix) + "control.preTearDown();");
-
-        for (String s : states.getIterationTearDowns(method)) writer.println(ident(prefix) + s);
-        writer.println();
-
-        writer.println(ident(prefix) + "if (control.isLastIteration()) {");
-        for (String s : states.getRunTearDowns(method)) writer.println(ident(prefix + 1) + s);
-        for (String s : states.getStateDestructors(method)) writer.println(ident(prefix + 1) + s);
-        writer.println(ident(prefix) + "}");
-    }
-
-    private String emitCall(Element method, StateObjectHandler states) {
-        ExecutableElement element = (ExecutableElement) method;
-        if ("void".equalsIgnoreCase(element.getReturnType().toString())) {
-            return states.getImplicit("bench").localIdentifier + "." + method.getSimpleName() + "(" + states.getArgList(method) + ")";
-        } else {
-            return states.getImplicit("blackhole").localIdentifier + ".consume(" + states.getImplicit("bench").localIdentifier + "." + method.getSimpleName() + "(" + states.getArgList(method) + "))";
-        }
-    }
-
-    public static String ident(int prefix) {
-        char[] chars = new char[prefix*4];
-        for (int i = 0; i < prefix*4; i++) {
-            chars[i] = ' ';
-        }
-        return new String(chars);
-    }
-
-
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GenerationException.java	Fri Feb 21 19:25:28 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GenerationException.java	Wed Feb 26 17:06:12 2014 +0400
@@ -24,23 +24,16 @@
  */
 package org.openjdk.jmh.processor.internal;
 
-import javax.lang.model.element.Element;
-
 public class GenerationException extends RuntimeException {
 
-    private final Element element;
+    private final MetadataInfo element;
 
-    public GenerationException(String message, Element element) {
+    public GenerationException(String message, MetadataInfo element) {
         super(message);
         this.element = element;
     }
 
-    public GenerationException(String message, Throwable cause, Element element) {
-        super(message, cause);
-        this.element = element;
-    }
-
-    public Element getElement() {
+    public MetadataInfo getElement() {
         return element;
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GeneratorSource.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Collection;
+
+public interface GeneratorSource {
+
+    Collection<ClassInfo> getClasses();
+
+    ClassInfo resolveClass(String className);
+
+    Writer newResource(String path) throws IOException;
+
+    Writer newFile(String objectName) throws IOException;
+
+    void printError(String message);
+
+    void printError(String message, MetadataInfo element);
+
+    void printError(String message, Throwable throwable);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GroupValidationPlugin.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Group;
+import org.openjdk.jmh.annotations.GroupThreads;
+import org.openjdk.jmh.annotations.Threads;
+
+public class GroupValidationPlugin implements Plugin {
+
+    @Override
+    public void process(GeneratorSource source) {
+        try {
+            for (MethodInfo element : BenchmarkGeneratorUtils.getMethodsAnnotatedWith(source, CompilerControl.class)) {
+                if (element.getAnnotation(Threads.class) != null) {
+                    source.printError("@" + Threads.class.getSimpleName() + " annotation is placed within " +
+                                    "the benchmark method with @" + Group.class.getSimpleName() + " annotation. " +
+                                    "This has ambiguous behavioral effect, and prohibited. " +
+                                    "Did you mean @" + GroupThreads.class.getSimpleName() + " instead?",
+                            element);
+                }
+            }
+        } catch (Throwable t) {
+            source.printError("Group validation processor had thrown the unexpected exception", t);
+        }
+    }
+
+    @Override
+    public void finish(GeneratorSource source) {
+        // do nothing
+    }
+
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/GroupValidationProcessor.java	Fri Feb 21 19:25:28 2014 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-/*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.openjdk.jmh.processor.internal;
-
-import org.openjdk.jmh.annotations.Group;
-import org.openjdk.jmh.annotations.GroupThreads;
-import org.openjdk.jmh.annotations.Threads;
-
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.RoundEnvironment;
-import javax.lang.model.element.Element;
-import javax.tools.Diagnostic.Kind;
-
-public class GroupValidationProcessor implements SubProcessor {
-
-    @Override
-    public void process(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv) {
-        try {
-            for (Element element : roundEnv.getElementsAnnotatedWith(Group.class)) {
-                if (element.getAnnotation(Threads.class) != null) {
-                    processingEnv.getMessager().printMessage(Kind.ERROR,
-                            "@" + Threads.class.getSimpleName() + " annotation is placed within " +
-                                    "the benchmark method with @" + Group.class.getSimpleName() + " annotation. " +
-                                    "This has ambiguous behavioral effect, and prohibited. " +
-                                    "Did you mean @" + GroupThreads.class.getSimpleName() + " instead?",
-                            element);
-                }
-            }
-        } catch (Throwable t) {
-            processingEnv.getMessager().printMessage(Kind.ERROR, "Annotation processor had throw exception: " + t);
-            t.printStackTrace(System.err);
-        }
-    }
-
-    @Override
-    public void finish(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv) {
-        // do nothing
-    }
-
-}
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/HelperMethodInvocation.java	Fri Feb 21 19:25:28 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/HelperMethodInvocation.java	Wed Feb 26 17:06:12 2014 +0400
@@ -27,13 +27,13 @@
 import org.openjdk.jmh.annotations.Level;
 
 public class HelperMethodInvocation implements Comparable<HelperMethodInvocation> {
-    public final String name;
+    public final MethodInfo method;
     public final StateObject state;
     public final Level helperLevel;
     public final HelperType type;
 
-    HelperMethodInvocation(String name, StateObject state, Level helperLevel, HelperType type) {
-        this.name = name;
+    public HelperMethodInvocation(MethodInfo method, StateObject state, Level helperLevel, HelperType type) {
+        this.method = method;
         this.state = state;
         this.helperLevel = helperLevel;
         this.type = type;
@@ -47,7 +47,7 @@
         HelperMethodInvocation that = (HelperMethodInvocation) o;
 
         if (helperLevel != that.helperLevel) return false;
-        if (!name.equals(that.name)) return false;
+        if (!method.equals(that.method)) return false;
         if (!state.equals(that.state)) return false;
         if (type != that.type) return false;
 
@@ -56,7 +56,7 @@
 
     @Override
     public int hashCode() {
-        int result = name.hashCode();
+        int result = method.hashCode();
         result = 31 * result + state.hashCode();
         result = 31 * result + helperLevel.hashCode();
         result = 31 * result + type.hashCode();
@@ -65,6 +65,6 @@
 
     @Override
     public int compareTo(HelperMethodInvocation o) {
-        return name.compareTo(o.name);
+        return method.compareTo(o.method);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/HelperMethodValidationPlugin.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+
+public class HelperMethodValidationPlugin implements Plugin {
+
+    @Override
+    public void process(GeneratorSource source) {
+        try {
+            for (MethodInfo element : BenchmarkGeneratorUtils.getMethodsAnnotatedWith(source, Setup.class)) {
+                // OK to have these annotation for @State objects
+                if (element.getOwner().getAnnotation(State.class) != null) continue;
+
+                // Abstract classes are not instantiated, assume OK
+                if (element.getOwner().isAbstract()) continue;
+
+                source.printError(
+                        "@" + Setup.class.getSimpleName() + " annotation is placed within " +
+                                "the class not having @" + State.class.getSimpleName() + " annotation. " +
+                                "This has no behavioral effect, and prohibited.",
+                        element);
+            }
+
+            for (MethodInfo element : BenchmarkGeneratorUtils.getMethodsAnnotatedWith(source, TearDown.class)) {
+                // OK to have these annotation for @State objects
+                if (element.getOwner().getAnnotation(State.class) != null) continue;
+
+                // Abstract classes are not instantiated, assume OK
+                if (element.getOwner().isAbstract()) continue;
+
+                source.printError(
+                            "@" + TearDown.class.getSimpleName() + " annotation is placed within " +
+                                    "the class not having @" + State.class.getSimpleName() + " annotation. " +
+                                    "This can be futile if no @State-bearing subclass is used.",
+                            element);
+            }
+
+        } catch (Throwable t) {
+            source.printError("Helper method validation processor had thrown the unexpected exception.", t);
+        }
+    }
+
+    @Override
+    public void finish(GeneratorSource source) {
+        // do nothing
+    }
+
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/HelperMethodValidationProcessor.java	Fri Feb 21 19:25:28 2014 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-/*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.openjdk.jmh.processor.internal;
-
-import org.openjdk.jmh.annotations.Setup;
-import org.openjdk.jmh.annotations.State;
-import org.openjdk.jmh.annotations.TearDown;
-
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.RoundEnvironment;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.Modifier;
-import javax.tools.Diagnostic.Kind;
-
-public class HelperMethodValidationProcessor implements SubProcessor {
-
-    @Override
-    public void process(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv) {
-        try {
-            for (Element element : roundEnv.getElementsAnnotatedWith(Setup.class)) {
-                // OK to have these annotation for @State objects
-                if (element.getEnclosingElement().getAnnotation(State.class) != null) continue;
-
-                // Abstract classes are not instantiated, assume OK
-                if (element.getEnclosingElement().getModifiers().contains(Modifier.ABSTRACT)) continue;
-
-                    processingEnv.getMessager().printMessage(Kind.ERROR,
-                            "@" + Setup.class.getSimpleName() + " annotation is placed within " +
-                                    "the class not having @" + State.class.getSimpleName() + " annotation. " +
-                                    "This has no behavioral effect, and prohibited.",
-                            element);
-            }
-
-            for (Element element : roundEnv.getElementsAnnotatedWith(TearDown.class)) {
-                // OK to have these annotation for @State objects
-                if (element.getEnclosingElement().getAnnotation(State.class) != null) continue;
-
-                // Abstract classes are not instantiated, assume OK
-                if (element.getEnclosingElement().getModifiers().contains(Modifier.ABSTRACT)) continue;
-
-                processingEnv.getMessager().printMessage(Kind.ERROR,
-                            "@" + TearDown.class.getSimpleName() + " annotation is placed within " +
-                                    "the class not having @" + State.class.getSimpleName() + " annotation. " +
-                                    "This can be futile if no @State-bearing subclass is used.",
-                            element);
-            }
-
-        } catch (Throwable t) {
-            processingEnv.getMessager().printMessage(Kind.ERROR, "Annotation processor had throw exception: " + t);
-            t.printStackTrace(System.err);
-        }
-    }
-
-    @Override
-    public void finish(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv) {
-        // do nothing
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MetadataInfo.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+public interface MetadataInfo {
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodGroup.java	Fri Feb 21 19:25:28 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodGroup.java	Wed Feb 26 17:06:12 2014 +0400
@@ -35,7 +35,6 @@
 import org.openjdk.jmh.runner.parameters.TimeValue;
 import org.openjdk.jmh.util.internal.Optional;
 
-import javax.lang.model.element.Element;
 import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -55,7 +54,7 @@
     private final Map<String, String[]> params;
     private boolean strictFP;
 
-    MethodGroup(String name) {
+    public MethodGroup(String name) {
         this.name = name;
         this.methods = new TreeSet<MethodInvocation>();
         this.modes = EnumSet.noneOf(Mode.class);
@@ -84,14 +83,14 @@
         return name.compareTo(o.name);
     }
 
-    public void addMethod(Element method, int threads) {
+    public void addMethod(MethodInfo method, int threads) {
         methods.add(new MethodInvocation(method, threads));
     }
 
-    public Collection<Element> methods() {
-        Collection<Element> result = new ArrayList<Element>();
+    public Collection<MethodInfo> methods() {
+        Collection<MethodInfo> result = new ArrayList<MethodInfo>();
         for (MethodInvocation m : methods) {
-            result.add(m.element);
+            result.add(m.method);
         }
         return result;
     }
@@ -242,10 +241,10 @@
     private <T extends Annotation> T getFinal(Class<T> klass) {
         T finalAnn = null;
         for (MethodInvocation mi : methods) {
-            T ann = AnnUtils.getAnnotationRecursive(mi.element, klass);
+            T ann = mi.method.getAnnotationRecursive(klass);
             if (ann != null && finalAnn != null) {
                 if (!finalAnn.equals(ann)) {
-                    throw new GenerationException("Colliding annotations: " + ann + " vs. " + finalAnn, mi.element);
+                    throw new GenerationException("Colliding annotations: " + ann + " vs. " + finalAnn, mi.method);
                 }
             }
             finalAnn = ann;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodInfo.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+import java.lang.annotation.Annotation;
+import java.util.Collection;
+
+public interface MethodInfo extends Comparable<MethodInfo>, MetadataInfo {
+
+    ClassInfo getOwner();
+
+    String getName();
+    String getQualifiedName();
+    String getReturnType();
+
+    Collection<ParameterInfo> getParameters();
+
+    <T extends Annotation> T getAnnotation(Class<T> annClass);
+    <T extends Annotation> T getAnnotationRecursive(Class<T> annClass);
+
+    boolean isPublic();
+    boolean isAbstract();
+    boolean isSynchronized();
+    boolean isStrictFP();
+
+
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodInvocation.java	Fri Feb 21 19:25:28 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/MethodInvocation.java	Wed Feb 26 17:06:12 2014 +0400
@@ -24,19 +24,17 @@
  */
 package org.openjdk.jmh.processor.internal;
 
-import javax.lang.model.element.Element;
-
 public class MethodInvocation implements Comparable<MethodInvocation> {
-    public final Element element;
+    public final MethodInfo method;
     public final int threads;
 
-    public MethodInvocation(Element element, int threads) {
-        this.element = element;
+    public MethodInvocation(MethodInfo method, int threads) {
+        this.method = method;
         this.threads = threads;
     }
 
     @Override
     public int compareTo(MethodInvocation o) {
-        return element.getSimpleName().toString().compareTo(o.element.getSimpleName().toString());
+        return method.compareTo(o.method);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/ParamValidationPlugin.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.State;
+
+public class ParamValidationPlugin implements Plugin {
+
+    @Override
+    public void process(GeneratorSource source) {
+        try {
+            for (FieldInfo element : BenchmarkGeneratorUtils.getFieldsAnnotatedWith(source, Param.class)) {
+                if (!element.isPublic()) {
+                    source.printError(
+                            "@" + Param.class.getSimpleName() + " annotation is not acceptable on non-public field.",
+                            element
+                    );
+                }
+
+                if (element.isStatic()) {
+                    source.printError(
+                            "@" + Param.class.getSimpleName() + " annotation is not acceptable on static fields.",
+                            element
+                    );
+                }
+
+                if (element.getOwner().getAnnotationRecursive(State.class) == null) {
+                    source.printError(
+                            "@" + Param.class.getSimpleName() + " annotation should be placed in @" + State.class.getSimpleName() +
+                                    "-annotated class.",
+                            element
+                    );
+                }
+
+
+                String[] values = element.getAnnotation(Param.class).value();
+
+                if (values.length >= 1 && !values[0].equalsIgnoreCase(Param.BLANK_ARGS)) {
+                    String type = element.getType();
+                    for (String val : values) {
+                        if (!isConforming(val, type)) {
+                            source.printError(
+                                    "Some @" + Param.class.getSimpleName() + " values can not be converted to target type: " +
+                                    "\"" + val + "\" can not be converted to " + type,
+                                    element
+                            );
+                        }
+                    }
+                }
+
+            }
+        } catch (Throwable t) {
+            source.printError("Param validation processor had thrown the unexpected exception.", t);
+        }
+    }
+
+    private boolean isConforming(String val, String type) {
+        if (type.equalsIgnoreCase("java.lang.String")) {
+            return true;
+        }
+        if (type.equalsIgnoreCase("boolean") || type.equalsIgnoreCase("java.lang.Boolean")) {
+            return (val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false"));
+        }
+        if (type.equalsIgnoreCase("byte") || type.equalsIgnoreCase("java.lang.Byte")) {
+            try {
+                Byte.valueOf(val);
+                return true;
+            } catch (NumberFormatException nfe){
+            }
+        }
+        if (type.equalsIgnoreCase("char") || type.equalsIgnoreCase("java.lang.Character")) {
+            return (val.length() != 0);
+        }
+        if (type.equalsIgnoreCase("short") || type.equalsIgnoreCase("java.lang.Short")) {
+            try {
+                Short.valueOf(val);
+                return true;
+            } catch (NumberFormatException nfe){
+            }
+        }
+        if (type.equalsIgnoreCase("int") || type.equalsIgnoreCase("java.lang.Integer")) {
+            try {
+                Integer.valueOf(val);
+                return true;
+            } catch (NumberFormatException nfe){
+            }
+        }
+        if (type.equalsIgnoreCase("float") || type.equalsIgnoreCase("java.lang.Float")) {
+            try {
+                Float.valueOf(val);
+                return true;
+            } catch (NumberFormatException nfe){
+            }
+        }
+        if (type.equalsIgnoreCase("long") || type.equalsIgnoreCase("java.lang.Long")) {
+            try {
+                Long.valueOf(val);
+                return true;
+            } catch (NumberFormatException nfe){
+            }
+        }
+        if (type.equalsIgnoreCase("double") || type.equalsIgnoreCase("java.lang.Double")) {
+            try {
+                Double.valueOf(val);
+                return true;
+            } catch (NumberFormatException nfe){
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void finish(GeneratorSource source) {
+        // do nothing
+    }
+
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/ParamValidationProcessor.java	Fri Feb 21 19:25:28 2014 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,156 +0,0 @@
-/*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.openjdk.jmh.processor.internal;
-
-import org.openjdk.jmh.annotations.Param;
-import org.openjdk.jmh.annotations.State;
-
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.RoundEnvironment;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.VariableElement;
-import javax.tools.Diagnostic.Kind;
-
-public class ParamValidationProcessor implements SubProcessor {
-
-    @Override
-    public void process(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv) {
-        try {
-            for (Element element : roundEnv.getElementsAnnotatedWith(Param.class)) {
-                if (element.getKind() != ElementKind.FIELD) {
-                    processingEnv.getMessager().printMessage(Kind.ERROR,
-                            "@" + Param.class.getSimpleName() + " annotation is acceptable on fields only.",
-                            element
-                    );
-                }
-
-                if (!element.getModifiers().contains(Modifier.PUBLIC)) {
-                    processingEnv.getMessager().printMessage(Kind.ERROR,
-                            "@" + Param.class.getSimpleName() + " annotation is not acceptable on non-public field.",
-                            element
-                    );
-                }
-
-                if (element.getModifiers().contains(Modifier.STATIC)) {
-                    processingEnv.getMessager().printMessage(Kind.ERROR,
-                            "@" + Param.class.getSimpleName() + " annotation is not acceptable on static fields.",
-                            element
-                    );
-                }
-
-                if (AnnUtils.getAnnotationRecursive(element, State.class) == null) {
-                    processingEnv.getMessager().printMessage(Kind.ERROR,
-                            "@" + Param.class.getSimpleName() + " annotation should be placed in @" + State.class.getSimpleName() +
-                                    "-annotated class.",
-                            element
-                    );
-                }
-
-                VariableElement ve = (VariableElement) element;
-                String[] values = element.getAnnotation(Param.class).value();
-
-                if (values.length >= 1 && !values[0].equalsIgnoreCase(Param.BLANK_ARGS)) {
-                    String type = ve.asType().toString();
-                    for (String val : values) {
-                        if (!isConforming(val, type)) {
-                            processingEnv.getMessager().printMessage(Kind.ERROR,
-                                    "Some @" + Param.class.getSimpleName() + " values can not be converted to target type: " +
-                                    "\"" + val + "\" can not be converted to " + type,
-                                    element
-                            );
-                        }
-                    }
-                }
-
-            }
-        } catch (Throwable t) {
-            processingEnv.getMessager().printMessage(Kind.ERROR, "Annotation processor had throw exception: " + t);
-            t.printStackTrace(System.err);
-        }
-    }
-
-    private boolean isConforming(String val, String type) {
-        if (type.equalsIgnoreCase("java.lang.String")) {
-            return true;
-        }
-        if (type.equalsIgnoreCase("boolean") || type.equalsIgnoreCase("java.lang.Boolean")) {
-            return (val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false"));
-        }
-        if (type.equalsIgnoreCase("byte") || type.equalsIgnoreCase("java.lang.Byte")) {
-            try {
-                Byte.valueOf(val);
-                return true;
-            } catch (NumberFormatException nfe){
-            }
-        }
-        if (type.equalsIgnoreCase("char") || type.equalsIgnoreCase("java.lang.Character")) {
-            return (val.length() != 0);
-        }
-        if (type.equalsIgnoreCase("short") || type.equalsIgnoreCase("java.lang.Short")) {
-            try {
-                Short.valueOf(val);
-                return true;
-            } catch (NumberFormatException nfe){
-            }
-        }
-        if (type.equalsIgnoreCase("int") || type.equalsIgnoreCase("java.lang.Integer")) {
-            try {
-                Integer.valueOf(val);
-                return true;
-            } catch (NumberFormatException nfe){
-            }
-        }
-        if (type.equalsIgnoreCase("float") || type.equalsIgnoreCase("java.lang.Float")) {
-            try {
-                Float.valueOf(val);
-                return true;
-            } catch (NumberFormatException nfe){
-            }
-        }
-        if (type.equalsIgnoreCase("long") || type.equalsIgnoreCase("java.lang.Long")) {
-            try {
-                Long.valueOf(val);
-                return true;
-            } catch (NumberFormatException nfe){
-            }
-        }
-        if (type.equalsIgnoreCase("double") || type.equalsIgnoreCase("java.lang.Double")) {
-            try {
-                Double.valueOf(val);
-                return true;
-            } catch (NumberFormatException nfe){
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public void finish(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv) {
-        // do nothing
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/ParameterInfo.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+public interface ParameterInfo extends MetadataInfo {
+    ClassInfo getType();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/Plugin.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal;
+
+public interface Plugin {
+
+    void process(GeneratorSource source);
+
+    void finish(GeneratorSource source);
+
+}
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/StateObject.java	Fri Feb 21 19:25:28 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/StateObject.java	Wed Feb 26 17:06:12 2014 +0400
@@ -26,7 +26,6 @@
 
 import org.openjdk.jmh.annotations.Scope;
 
-import javax.lang.model.element.VariableElement;
 import java.util.Collection;
 import java.util.Comparator;
 import java.util.Map;
@@ -92,9 +91,9 @@
         return params.get(name);
     }
 
-    public void addParam(VariableElement ve) {
-        String type = ve.asType().toString();
-        String name = ve.getSimpleName().toString();
+    public void addParam(FieldInfo fieldInfo) {
+        String type = fieldInfo.getType();
+        String name = fieldInfo.getName();
         if (type.equalsIgnoreCase("java.lang.String")) {
             params.put(name, "control.getParam(\"" + name + "\")");
             return;
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/StateObjectHandler.java	Fri Feb 21 19:25:28 2014 +0400
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/StateObjectHandler.java	Wed Feb 26 17:06:12 2014 +0400
@@ -36,14 +36,6 @@
 import org.openjdk.jmh.util.internal.Multimap;
 import org.openjdk.jmh.util.internal.TreesetMultimap;
 
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ElementKind;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.Modifier;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.util.ElementFilter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -57,7 +49,6 @@
 
 public class StateObjectHandler {
 
-    private final ProcessingEnvironment processingEnv;
     private final Multimap<String, StateObject> args;
     private final Map<String, StateObject> implicits;
     private final Set<StateObject> stateObjects;
@@ -75,8 +66,7 @@
     private final Multimap<String, String> auxNames = new HashMultimap<String, String>();
     private final Map<String, String> auxAccessors = new HashMap<String, String>();
 
-    public StateObjectHandler(ProcessingEnvironment processingEnv) {
-        this.processingEnv = processingEnv;
+    public StateObjectHandler() {
         this.args = new HashMultimap<String, StateObject>();
         this.implicits = new HashMap<String, StateObject>();
         this.stateObjects = new HashSet<StateObject>();
@@ -100,27 +90,27 @@
         return type.substring(type.lastIndexOf(".") + 1);
     }
 
-    public void bindArg(ExecutableElement execMethod, TypeElement type) {
-        State ann = type.getAnnotation(State.class);
+    public void bindArg(MethodInfo mi, ParameterInfo pi) {
+        State ann = pi.getType().getAnnotation(State.class);
         if (ann != null) {
-            bindState(execMethod, type, ann.value(), null);
+            bindState(mi, pi.getType(), ann.value(), null);
         } else {
-            throw new IllegalStateException("The method parameter is not a @State: " + type);
+            throw new IllegalStateException("The method parameter is not a @State: " + pi);
         }
     }
 
-    public void bindImplicit(TypeElement type, String label) {
-        bindImplicit(type, label, Scope.Thread);
+    public void bindImplicit(ClassInfo ci, String label) {
+        bindImplicit(ci, label, Scope.Thread);
     }
 
-    public void bindImplicit(TypeElement type, String label, Scope scope) {
-        State ann = type.getAnnotation(State.class);
-        bindState(null, type, (ann != null) ? ann.value() : scope, label);
+    public void bindImplicit(ClassInfo ci, String label, Scope scope) {
+        State ann = ci.getAnnotation(State.class);
+        bindState(null, ci, (ann != null) ? ann.value() : scope, label);
     }
 
-    private void bindState(ExecutableElement execMethod, TypeElement element, Scope scope, String implicitLabel) {
+    private void bindState(MethodInfo execMethod, ClassInfo ci, Scope scope, String implicitLabel) {
         Integer index;
-        String className = element.getQualifiedName().toString();
+        String className = ci.getQualifiedName();
         switch (scope) {
             case Benchmark: {
                 index = globalIndexByType.get(className);
@@ -157,43 +147,45 @@
         } else {
             String identifier = collapseTypeName(className) + index;
             so = new StateObject(className, getJMHtype(className), scope, "f_" + identifier, "l_" + identifier);
-            args.put(execMethod.getSimpleName().toString(), so);
+            args.put(execMethod.getName(), so);
         }
 
         // auxiliary result, produce the accessors
-        if (element.getAnnotation(AuxCounters.class) != null) {
+        if (ci.getAnnotation(AuxCounters.class) != null) {
             if (scope != Scope.Thread) {
                 throw new GenerationException("@" + AuxCounters.class.getSimpleName() +
-                        " can only be used with " + Scope.class.getSimpleName() + "." + Scope.Thread + " states.", element);
+                        " can only be used with " + Scope.class.getSimpleName() + "." + Scope.Thread + " states.", ci);
             }
 
-            for (Element sub : element.getEnclosedElements()) {
-                if (sub.getKind() == ElementKind.FIELD && sub.getModifiers().contains(Modifier.PUBLIC)) {
-                    String fieldType = sub.asType().toString();
+            for (FieldInfo sub : ci.getDeclaredFields()) {
+                if (sub.isPublic()) {
+                    String fieldType = sub.getType();
                     if (fieldType.equals("int") || fieldType.equals("long")) {
-                        String name = sub.getSimpleName().toString();
-                        String meth = execMethod.getSimpleName().toString();
+                        String name = sub.getName();
+                        String meth = execMethod.getName();
                         auxNames.put(meth, name);
                         String prev = auxAccessors.put(meth + name, so.localIdentifier + "." + name);
                         if (prev != null) {
                             throw new GenerationException("Conflicting @" + AuxCounters.class.getSimpleName() +
                                 " counters. Make sure there are no @" + State.class.getSimpleName() + "-s with the same counter " +
-                                " injected into this method.", element);
+                                " injected into this method.", sub);
                         }
                     }
                 }
+            }
 
-                if (sub.getKind() == ElementKind.METHOD && sub.getModifiers().contains(Modifier.PUBLIC)) {
-                    String returnType = ((ExecutableElement) sub).getReturnType().toString();
+            for (MethodInfo sub : ci.getDeclaredMethods()) {
+                if (sub.isPublic()) {
+                    String returnType = sub.getReturnType();
                     if (returnType.equals("int") || returnType.equals("long")) {
-                        String name = sub.getSimpleName().toString();
-                        String meth = execMethod.getSimpleName().toString();
+                        String name = sub.getName();
+                        String meth = execMethod.getName();
                         auxNames.put(meth, name);
                         String prev = auxAccessors.put(meth + name, so.localIdentifier + "." + name + "()");
                         if (prev != null) {
                             throw new GenerationException("Conflicting @" + AuxCounters.class.getSimpleName() +
                                     " counters. Make sure there are no @" + State.class.getSimpleName() + "-s with the same counter " +
-                                    " injected into this method.", element);
+                                    " injected into this method.", sub);
                         }
                     }
                 }
@@ -203,37 +195,32 @@
         stateObjects.add(so);
 
         // walk the type hierarchy up to discover inherited @Params
-        TypeElement walk1 = element;
-        do {
-            for (VariableElement ve : ElementFilter.fieldsIn(walk1.getEnclosedElements())) {
-                if (ve.getAnnotation(Param.class) != null) {
-                    so.addParam(ve);
-                }
+        for (FieldInfo fi : ci.getFields()) {
+            if (fi.getAnnotation(Param.class) != null) {
+                so.addParam(fi);
             }
-        } while ((walk1 = (TypeElement) processingEnv.getTypeUtils().asElement(walk1.getSuperclass())) != null);
+        }
 
-        // walk the type hierarchy up to discover inherited helper methods
-        TypeElement walk2 = element;
-        do {
-            for (ExecutableElement m : ElementFilter.methodsIn(walk2.getEnclosedElements())) {
-                Setup setupAnn = m.getAnnotation(Setup.class);
-                if (setupAnn != null) {
-                    helpersByState.put(so, new HelperMethodInvocation(m.getSimpleName().toString(), so, setupAnn.value(), HelperType.SETUP));
-                }
+        // put the @State objects helper methods
+        for (MethodInfo mi : ci.getMethods()) {
+            Setup setupAnn = mi.getAnnotation(Setup.class);
+            if (setupAnn != null) {
+                helpersByState.put(so, new HelperMethodInvocation(mi, so, setupAnn.value(), HelperType.SETUP));
+            }
 
-                TearDown tearDownAnn = m.getAnnotation(TearDown.class);
-                if (tearDownAnn != null) {
-                    helpersByState.put(so, new HelperMethodInvocation(m.getSimpleName().toString(), so, tearDownAnn.value(), HelperType.TEARDOWN));
-                }
+            TearDown tearDownAnn = mi.getAnnotation(TearDown.class);
+            if (tearDownAnn != null) {
+                helpersByState.put(so, new HelperMethodInvocation(mi, so, tearDownAnn.value(), HelperType.TEARDOWN));
             }
-        } while ((walk2 = (TypeElement) processingEnv.getTypeUtils().asElement(walk2.getSuperclass())) != null);
+        }
+
     }
 
-    public String getArgList(Element method) {
+    public String getArgList(MethodInfo methodInfo) {
         StringBuilder sb = new StringBuilder();
 
         int i = 0;
-        for (StateObject so : args.get(method.getSimpleName().toString())) {
+        for (StateObject so : args.get(methodInfo.getName())) {
             if (i != 0) {
                 sb.append(", ");
             }
@@ -243,11 +230,11 @@
         return sb.toString();
     }
 
-    public String getTypeArgList(Element method) {
+    public String getTypeArgList(MethodInfo methodInfo) {
         StringBuilder sb = new StringBuilder();
 
         int i = 0;
-        for (StateObject so : args.get(method.getSimpleName().toString())) {
+        for (StateObject so : args.get(methodInfo.getName())) {
             if (i != 0) {
                 sb.append(", ");
             }
@@ -265,9 +252,9 @@
         return r;
     }
 
-    public Collection<String> getHelperBlock(String method, Level helperLevel, HelperType type) {
+    public Collection<String> getHelperBlock(MethodInfo method, Level helperLevel, HelperType type) {
 
-        Collection<StateObject> states = cons(args.get(method), implicits.values(), getControls());
+        Collection<StateObject> states = cons(args.get(method.getName()), implicits.values(), getControls());
 
         // Look for the offending methods.
         // This will be used to skip the irrelevant blocks for state objects down the stream.
@@ -289,7 +276,7 @@
                 result.add("if (!" + so.localIdentifier + ".ready" + helperLevel + ") {");
                 for (HelperMethodInvocation mi : helpersByState.get(so)) {
                     if (mi.helperLevel == helperLevel && mi.type == HelperType.SETUP) {
-                        result.add("    " + so.localIdentifier + "." + mi.name + "();");
+                        result.add("    " + so.localIdentifier + "." + mi.method.getName() + "();");
                     }
                 }
                 result.add("    " + so.localIdentifier + ".ready" + helperLevel + " = true;");
@@ -300,7 +287,7 @@
                 result.add("if (" + so.localIdentifier + ".ready" + helperLevel + ") {");
                 for (HelperMethodInvocation mi : helpersByState.get(so)) {
                     if (mi.helperLevel == helperLevel && mi.type == HelperType.TEARDOWN) {
-                        result.add("    " + so.localIdentifier + "." + mi.name + "();");
+                        result.add("    " + so.localIdentifier + "." + mi.method.getName() + "();");
                     }
                 }
                 result.add("    " + so.localIdentifier + ".ready" + helperLevel + " = false;");
@@ -321,7 +308,7 @@
                 result.add("    if (!" + so.localIdentifier + ".ready" + helperLevel + ") {");
                 for (HelperMethodInvocation mi : helpersByState.get(so)) {
                     if (mi.helperLevel == helperLevel && mi.type == HelperType.SETUP) {
-                        result.add("        " + so.localIdentifier + "." + mi.name + "();");
+                        result.add("        " + so.localIdentifier + "." + mi.method.getName() + "();");
                     }
                 }
                 result.add("        " + so.localIdentifier + ".ready" + helperLevel + " = true;");
@@ -339,7 +326,7 @@
                 result.add("    if (" + so.localIdentifier + ".ready" + helperLevel + ") {");
                 for (HelperMethodInvocation mi : helpersByState.get(so)) {
                     if (mi.helperLevel == helperLevel && mi.type == HelperType.TEARDOWN) {
-                        result.add("        " + so.localIdentifier + "." + mi.name + "();");
+                        result.add("        " + so.localIdentifier + "." + mi.method.getName() + "();");
                     }
                 }
                 result.add("        " + so.localIdentifier + ".ready" + helperLevel + " = false;");
@@ -353,28 +340,28 @@
         return result;
     }
 
-    public Collection<String> getInvocationSetups(Element method) {
-        return getHelperBlock(method.getSimpleName().toString(), Level.Invocation, HelperType.SETUP);
+    public Collection<String> getInvocationSetups(MethodInfo method) {
+        return getHelperBlock(method, Level.Invocation, HelperType.SETUP);
     }
 
-    public Collection<String> getInvocationTearDowns(Element method) {
-        return getHelperBlock(method.getSimpleName().toString(), Level.Invocation, HelperType.TEARDOWN);
+    public Collection<String> getInvocationTearDowns(MethodInfo method) {
+        return getHelperBlock(method, Level.Invocation, HelperType.TEARDOWN);
     }
 
-    public Collection<String> getIterationSetups(Element method) {
-        return getHelperBlock(method.getSimpleName().toString(), Level.Iteration, HelperType.SETUP);
+    public Collection<String> getIterationSetups(MethodInfo method) {
+        return getHelperBlock(method, Level.Iteration, HelperType.SETUP);
     }
 
-    public Collection<String> getIterationTearDowns(Element method) {
-        return getHelperBlock(method.getSimpleName().toString(), Level.Iteration, HelperType.TEARDOWN);
+    public Collection<String> getIterationTearDowns(MethodInfo method) {
+        return getHelperBlock(method, Level.Iteration, HelperType.TEARDOWN);
     }
 
-    public Collection<String> getRunSetups(Element method) {
-        return getHelperBlock(method.getSimpleName().toString(), Level.Trial, HelperType.SETUP);
+    public Collection<String> getRunSetups(MethodInfo method) {
+        return getHelperBlock(method, Level.Trial, HelperType.SETUP);
     }
 
-    public Collection<String> getRunTearDowns(Element method) {
-        return getHelperBlock(method.getSimpleName().toString(), Level.Trial, HelperType.TEARDOWN);
+    public Collection<String> getRunTearDowns(MethodInfo method) {
+        return getHelperBlock(method, Level.Trial, HelperType.TEARDOWN);
     }
 
     public List<String> getStateInitializers() {
@@ -400,7 +387,7 @@
             for (HelperMethodInvocation hmi : helpersByState.get(so)) {
                 if (hmi.helperLevel != Level.Trial) continue;
                 if (hmi.type != HelperType.SETUP) continue;
-                result.add("            " + so.fieldIdentifier + "." + hmi.name + "();");
+                result.add("            " + so.fieldIdentifier + "." + hmi.method.getName() + "();");
             }
             result.add("            " + so.fieldIdentifier + ".ready" + Level.Trial + " = true;");
             result.add("        }");
@@ -423,7 +410,7 @@
             for (HelperMethodInvocation hmi : helpersByState.get(so)) {
                 if (hmi.helperLevel != Level.Trial) continue;
                 if (hmi.type != HelperType.SETUP) continue;
-                result.add("                val." + hmi.name + "();");
+                result.add("                val." + hmi.method.getName() + "();");
             }
             result.add("                " + "val.ready" + Level.Trial + " = true;");
             result.add("          " + so.fieldIdentifier + " = val;");
@@ -452,7 +439,7 @@
             for (HelperMethodInvocation hmi : helpersByState.get(so)) {
                 if (hmi.helperLevel != Level.Trial) continue;
                 if (hmi.type != HelperType.SETUP) continue;
-                result.add("            local." + hmi.name + "();");
+                result.add("            local." + hmi.method.getName() + "();");
             }
             result.add("            " + "local.ready" + Level.Trial + " = true;");
             result.add("            " + so.fieldIdentifier + "_map.put(groupId, val);");
@@ -464,8 +451,8 @@
         return result;
     }
 
-    public Collection<String> getStateDestructors(Element method) {
-        Collection<StateObject> sos = cons(args.get(method.getSimpleName().toString()), implicits.values());
+    public Collection<String> getStateDestructors(MethodInfo method) {
+        Collection<StateObject> sos = cons(args.get(method.getName()), implicits.values());
 
         List<String> result = new ArrayList<String>();
         for (StateObject so : sos) {
@@ -489,9 +476,9 @@
         return result;
     }
 
-    public List<String> getStateGetters(Element method) {
+    public List<String> getStateGetters(MethodInfo method) {
         List<String> result = new ArrayList<String>();
-        for (StateObject so : cons(args.get(method.getSimpleName().toString()), implicits.values(), getControls())) {
+        for (StateObject so : cons(args.get(method.getName()), implicits.values(), getControls())) {
             switch (so.scope) {
                 case Benchmark:
                 case Thread:
@@ -562,9 +549,9 @@
     public static void padding(List<String> lines, String suffix) {
         for (int p = 0; p < 16; p++) {
             StringBuilder sb = new StringBuilder();
-            sb.append("    boolean jmh_b3_pad_").append(p);
+            sb.append("    boolean jmh_").append(suffix).append("_pad_").append(p);
             for (int q = 1; q < 16; q++) {
-                sb.append(", jmh_b3_pad_").append(p).append("_").append(q);
+                sb.append(", jmh_").append(suffix).append("_pad_").append(p).append("_").append(q);
             }
             sb.append(";");
             lines.add(sb.toString());
@@ -606,12 +593,12 @@
         return s;
     }
 
-    public Collection<String> getAuxResultNames(Element method) {
-        return auxNames.get(method.getSimpleName().toString());
+    public Collection<String> getAuxResultNames(MethodInfo method) {
+        return auxNames.get(method.getName());
     }
 
-    public String getAuxResultAccessor(Element method, String name) {
-        return auxAccessors.get(method.getSimpleName().toString() + name);
+    public String getAuxResultAccessor(MethodInfo method, String name) {
+        return auxAccessors.get(method.getName() + name);
     }
 
 }
--- a/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/SubProcessor.java	Fri Feb 21 19:25:28 2014 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package org.openjdk.jmh.processor.internal;
-
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.annotation.processing.RoundEnvironment;
-
-public interface SubProcessor {
-
-    void process(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv);
-
-    void finish(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv);
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/APClassInfo.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal.annotations;
+
+import org.openjdk.jmh.processor.internal.ClassInfo;
+import org.openjdk.jmh.processor.internal.FieldInfo;
+import org.openjdk.jmh.processor.internal.MethodInfo;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.util.ElementFilter;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class APClassInfo extends APMetadataInfo implements ClassInfo {
+
+    private final TypeElement el;
+
+    public APClassInfo(ProcessingEnvironment processEnv, TypeElement element) {
+        super(processEnv, element);
+        this.el = element;
+    }
+
+    @Override
+    public <T extends Annotation> T getAnnotation(Class<T> annClass) {
+        return el.getAnnotation(annClass);
+    }
+
+    @Override
+    public <T extends Annotation> T getAnnotationRecursive(Class<T> annClass) {
+        return AnnUtils.getAnnotationRecursive(el, annClass);
+    }
+
+    @Override
+    public Collection<MethodInfo> getConstructors() {
+        Collection<MethodInfo> mis = new ArrayList<MethodInfo>();
+        for (ExecutableElement e : ElementFilter.constructorsIn(el.getEnclosedElements())) {
+            mis.add(new APMethodInfo(processEnv, this, e));
+        }
+        return mis;
+    }
+
+    @Override
+    public boolean isNested() {
+        return el.getNestingKind().isNested();
+    }
+
+    @Override
+    public String getNestedName() {
+        return AnnUtils.getNestedName(el);
+    }
+
+    @Override
+    public String getQualifiedName() {
+        return el.getQualifiedName().toString();
+    }
+
+    @Override
+    public Collection<FieldInfo> getDeclaredFields() {
+        List<FieldInfo> ls = new ArrayList<FieldInfo>();
+        for (VariableElement e : ElementFilter.fieldsIn(el.getEnclosedElements())) {
+            ls.add(new APFieldInfo(processEnv, e));
+        }
+        return ls;
+    }
+
+    @Override
+    public Collection<FieldInfo> getFields() {
+        List<FieldInfo> ls = new ArrayList<FieldInfo>();
+        ls.addAll(getDeclaredFields());
+        for (ClassInfo cl : getSuperclasses()) {
+            ls.addAll(cl.getDeclaredFields());
+        }
+        return ls;
+    }
+
+    @Override
+    public Collection<MethodInfo> getDeclaredMethods() {
+        Collection<MethodInfo> mis = new ArrayList<MethodInfo>();
+        for (ExecutableElement e : ElementFilter.methodsIn(el.getEnclosedElements())) {
+            mis.add(new APMethodInfo(processEnv, this, e));
+        }
+        return mis;
+    }
+
+    @Override
+    public Collection<MethodInfo> getMethods() {
+        List<MethodInfo> ls = new ArrayList<MethodInfo>();
+        ls.addAll(getDeclaredMethods());
+        for (ClassInfo cl : getSuperclasses()) {
+            ls.addAll(cl.getDeclaredMethods());
+        }
+        return ls;
+    }
+
+    @Override
+    public String getPackageName() {
+        return AnnUtils.getPackageName(el);
+    }
+
+    @Override
+    public Collection<ClassInfo> getSuperclasses() {
+        List<ClassInfo> list = new ArrayList<ClassInfo>();
+        TypeElement walk = el;
+        while ((walk = (TypeElement) processEnv.getTypeUtils().asElement(walk.getSuperclass())) != null) {
+            list.add(new APClassInfo(processEnv, walk));
+        }
+        return list;
+    }
+
+    @Override
+    public boolean isAbstract() {
+        return el.getModifiers().contains(Modifier.ABSTRACT);
+    }
+
+    @Override
+    public boolean isPublic() {
+        return el.getModifiers().contains(Modifier.PUBLIC);
+    }
+
+    @Override
+    public boolean isStatic() {
+        return el.getModifiers().contains(Modifier.STATIC);
+    }
+
+    @Override
+    public boolean isStrictFP() {
+        return el.getModifiers().contains(Modifier.STRICTFP);
+    }
+
+    public String toString() {
+        return getQualifiedName();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/APFieldInfo.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal.annotations;
+
+import org.openjdk.jmh.processor.internal.ClassInfo;
+import org.openjdk.jmh.processor.internal.FieldInfo;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import java.lang.annotation.Annotation;
+
+public class APFieldInfo extends APMetadataInfo implements FieldInfo {
+
+    private final VariableElement ve;
+
+    public APFieldInfo(ProcessingEnvironment processEnv, VariableElement ve) {
+        super(processEnv, ve);
+        this.ve = ve;
+    }
+
+    @Override
+    public String getName() {
+        return ve.getSimpleName().toString();
+    }
+
+    @Override
+    public String getType() {
+        return ve.asType().toString();
+    }
+
+    @Override
+    public boolean isPublic() {
+        return ve.getModifiers().contains(Modifier.PUBLIC);
+    }
+
+    @Override
+    public boolean isStatic() {
+        return ve.getModifiers().contains(Modifier.STATIC);
+    }
+
+    @Override
+    public <T extends Annotation> T getAnnotation(Class<T> annClass) {
+        return ve.getAnnotation(annClass);
+    }
+
+    @Override
+    public ClassInfo getOwner() {
+        return new APClassInfo(processEnv, (TypeElement)ve.getEnclosingElement());
+    }
+
+    public String toString() {
+        return getType() + " " + getName();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/APMetadataInfo.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal.annotations;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+
+public class APMetadataInfo {
+
+    protected final ProcessingEnvironment processEnv;
+    private final Element element;
+
+    public APMetadataInfo(ProcessingEnvironment processEnv, Element element) {
+        this.processEnv = processEnv;
+        this.element = element;
+    }
+
+    public Element getElement() {
+        return element;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/APMethodInfo.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal.annotations;
+
+import org.openjdk.jmh.processor.internal.ClassInfo;
+import org.openjdk.jmh.processor.internal.MethodInfo;
+import org.openjdk.jmh.processor.internal.ParameterInfo;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.VariableElement;
+import java.lang.annotation.Annotation;
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class APMethodInfo extends APMetadataInfo implements MethodInfo {
+
+    private final ClassInfo ci;
+    private final ExecutableElement el;
+
+    public APMethodInfo(ProcessingEnvironment processEnv, ClassInfo ci, ExecutableElement el) {
+        super(processEnv, el);
+        this.ci = ci;
+        this.el = el;
+    }
+
+    @Override
+    public ClassInfo getOwner() {
+        return ci;
+    }
+
+    @Override
+    public String getName() {
+        return el.getSimpleName().toString();
+    }
+
+    @Override
+    public String getReturnType() {
+        return el.getReturnType().toString();
+    }
+
+    @Override
+    public Collection<ParameterInfo> getParameters() {
+        Collection<ParameterInfo> pis = new ArrayList<ParameterInfo>();
+        for (VariableElement v : el.getParameters()) {
+            pis.add(new APParameterInfo(processEnv, v));
+        }
+        return pis;
+    }
+
+    @Override
+    public <T extends Annotation> T getAnnotation(Class<T> annClass) {
+        return el.getAnnotation(annClass);
+    }
+
+    @Override
+    public <T extends Annotation> T getAnnotationRecursive(Class<T> annClass) {
+        return AnnUtils.getAnnotationRecursive(el, annClass);
+    }
+
+    @Override
+    public boolean isPublic() {
+        return el.getModifiers().contains(Modifier.PUBLIC);
+    }
+
+    @Override
+    public boolean isAbstract() {
+        return el.getModifiers().contains(Modifier.ABSTRACT);
+    }
+
+    @Override
+    public boolean isSynchronized() {
+        return el.getModifiers().contains(Modifier.SYNCHRONIZED);
+    }
+
+    @Override
+    public boolean isStrictFP() {
+        return el.getModifiers().contains(Modifier.STRICTFP);
+    }
+
+    @Override
+    public String getQualifiedName() {
+        return ci.getQualifiedName() + "." + el.toString();
+    }
+
+    @Override
+    public int compareTo(MethodInfo o) {
+        return getQualifiedName().compareTo(o.getQualifiedName());
+    }
+
+    public String toString() {
+        return getOwner() + " " + getName() ;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/APParameterInfo.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal.annotations;
+
+import org.openjdk.jmh.processor.internal.ClassInfo;
+import org.openjdk.jmh.processor.internal.ParameterInfo;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+public class APParameterInfo extends APMetadataInfo implements ParameterInfo {
+    private final VariableElement ve;
+    private final TypeElement stateType;
+
+    public APParameterInfo(ProcessingEnvironment processEnv, VariableElement ve) {
+        super(processEnv, ve);
+        this.ve = ve;
+        this.stateType = (TypeElement) processEnv.getTypeUtils().asElement(ve.asType());
+    }
+
+    @Override
+    public ClassInfo getType() {
+        return new APClassInfo(processEnv, stateType);
+    }
+
+    public String toString() {
+        return getType() + " " + ve.getSimpleName();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/AnnProcessGeneratorSource.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal.annotations;
+
+import org.openjdk.jmh.processor.internal.ClassInfo;
+import org.openjdk.jmh.processor.internal.GeneratorSource;
+import org.openjdk.jmh.processor.internal.MetadataInfo;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.ElementFilter;
+import javax.tools.Diagnostic;
+import javax.tools.StandardLocation;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.TreeSet;
+
+public class AnnProcessGeneratorSource implements GeneratorSource {
+
+    private final RoundEnvironment roundEnv;
+    private final ProcessingEnvironment processingEnv;
+
+    public AnnProcessGeneratorSource(RoundEnvironment roundEnv, ProcessingEnvironment processingEnv) {
+        this.roundEnv = roundEnv;
+        this.processingEnv = processingEnv;
+    }
+
+    @Override
+    public Collection<ClassInfo> getClasses() {
+        // Need to do a few rollovers to find all classes that have @GMB-annotated methods in their
+        // subclasses. This is mostly due to some of the nested classes not discoverable at once,
+        // when we need to discover the enclosing class first. With the potentially non-zero nesting
+        // depth, we need to do a few rounds. Hopefully we will just do a single stride in most
+        // cases.
+
+        Collection<TypeElement> discoveredClasses = new TreeSet<TypeElement>(new Comparator<TypeElement>() {
+            @Override
+            public int compare(TypeElement o1, TypeElement o2) {
+                return o1.getQualifiedName().toString().compareTo(o2.getQualifiedName().toString());
+            }
+        });
+
+        // Walk around until convergence...
+
+        int lastSize = -1;
+        while (discoveredClasses.size() > lastSize) {
+            lastSize = discoveredClasses.size();
+            for (Element e : roundEnv.getRootElements()) {
+                if (e.getKind() != ElementKind.CLASS) continue;
+                TypeElement walk = (TypeElement) e;
+                do {
+                    discoveredClasses.add(walk);
+                    for (TypeElement nested : ElementFilter.typesIn(walk.getEnclosedElements())) {
+                        discoveredClasses.add(nested);
+                    }
+                } while ((walk = (TypeElement) processingEnv.getTypeUtils().asElement(walk.getSuperclass())) != null);
+            }
+        }
+
+        return convert(discoveredClasses);
+    }
+
+    protected Collection<ClassInfo> convert(Collection<TypeElement> els) {
+        List<ClassInfo> list = new ArrayList<ClassInfo>();
+        for (TypeElement el : els) {
+            list.add(new APClassInfo(processingEnv, el));
+        }
+        return list;
+    }
+
+    @Override
+    public ClassInfo resolveClass(String className) {
+        return new APClassInfo(processingEnv, processingEnv.getElementUtils().getTypeElement(className));
+    }
+
+    @Override
+    public Writer newResource(String path) throws IOException {
+        return processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", path).openWriter();
+    }
+
+    @Override
+    public Writer newFile(String objectName) throws IOException {
+        return processingEnv.getFiler().createSourceFile(objectName).openWriter();
+    }
+
+    @Override
+    public void printError(String message) {
+        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message);
+    }
+
+    @Override
+    public void printError(String message, MetadataInfo element) {
+        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message, ((APMetadataInfo)element).getElement());
+    }
+
+    @Override
+    public void printError(String message, Throwable throwable) {
+        printError(message + " " + throwable);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jmh-core/src/main/java/org/openjdk/jmh/processor/internal/annotations/AnnUtils.java	Wed Feb 26 17:06:12 2014 +0400
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.jmh.processor.internal.annotations;
+
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import java.lang.annotation.Annotation;
+
+public class AnnUtils {
+
+    /**
+     * Recursively get the annotation, until found, or reached the top of hierarchy.
+     * @param root where to start
+     * @param annotation what to look for
+     * @param <A> type of what we look for
+     * @return annotation
+     */
+    public static <A extends Annotation> A getAnnotationRecursive(Element root, Class<A> annotation) {
+        Element walk = root;
+        A result = null;
+        while (walk != null && (result = walk.getAnnotation(annotation)) == null) {
+            walk = walk.getEnclosingElement();
+        }
+        return result;
+    }
+
+    /**
+     * Get the package name part of a class
+     *
+     * @param clazz the subject
+     * @return the package name or "" if no package
+     */
+    public static String getPackageName(TypeElement clazz) {
+        Element walk = clazz;
+        while (walk.getKind() != ElementKind.PACKAGE) {
+            walk = walk.getEnclosingElement();
+        }
+        return ((PackageElement)walk).getQualifiedName().toString();
+    }
+
+    /**
+     * Get the class name along with any nested class names
+     * @param clazz the subject
+     * @return the synthetic class name in form of "parent1_parent2_classname"
+     */
+    public static String getNestedName(TypeElement clazz) {
+        String name = "";
+        Element walk = clazz;
+        while (walk.getKind() != ElementKind.PACKAGE) {
+            name = walk.getSimpleName().toString() + (name.isEmpty() ? "" : "_" + name);
+            walk = walk.getEnclosingElement();
+        }
+        return name.substring(0, name.length());
+    }
+}