Issue error when analyzing instrumented classes (GitHub #108)
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassAnalyzerTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassAnalyzerTest.java
new file mode 100644
index 0000000..c60901c
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/ClassAnalyzerTest.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2013 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Marc R. Hoffmann - initial API and implementation
+ *    
+ *******************************************************************************/
+package org.jacoco.core.internal.analysis;
+
+import org.jacoco.core.internal.instr.InstrSupport;
+import org.junit.Before;
+import org.junit.Test;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Unit tests for {@link ClassAnalyzer}.
+ */
+public class ClassAnalyzerTest {
+
+	private ClassAnalyzer analyzer;
+
+	@Before
+	public void setup() {
+		analyzer = new ClassAnalyzer(0x0000, null, new StringPool());
+		analyzer.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, "Foo", null,
+				"java/lang/Object", null);
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testAnalyzeInstrumentedClass1() {
+		analyzer.visitField(InstrSupport.DATAFIELD_ACC,
+				InstrSupport.DATAFIELD_NAME, InstrSupport.DATAFIELD_DESC, null,
+				null);
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testAnalyzeInstrumentedClass2() {
+		analyzer.visitMethod(InstrSupport.INITMETHOD_ACC,
+				InstrSupport.INITMETHOD_NAME, InstrSupport.INITMETHOD_DESC,
+				null, null);
+	}
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/InstrSupportTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/InstrSupportTest.java
index f10fe6f..1d8004a 100644
--- a/org.jacoco.core.test/src/org/jacoco/core/internal/instr/InstrSupportTest.java
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/instr/InstrSupportTest.java
@@ -34,6 +34,21 @@
 	}
 
 	@Test
+	public void testAssertNotIntrumentedPositive() {
+		InstrSupport.assertNotInstrumented("run", "Foo");
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testAssertNotIntrumentedField() {
+		InstrSupport.assertNotInstrumented("$jacocoData", "Foo");
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void testAssertNotIntrumentedMethod() {
+		InstrSupport.assertNotInstrumented("$jacocoInit", "Foo");
+	}
+
+	@Test
 	public void testPushIntM2147483648() {
 		InstrSupport.push(trace, -2147483648);
 		assertInstruction("LDC -2147483648");
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java
index ee8f05a..9fa5288 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/ClassAnalyzer.java
@@ -14,6 +14,8 @@
 import org.jacoco.core.analysis.IMethodCoverage;
 import org.jacoco.core.internal.flow.ClassProbesVisitor;
 import org.jacoco.core.internal.flow.MethodProbesVisitor;
+import org.jacoco.core.internal.instr.InstrSupport;
+import org.objectweb.asm.FieldVisitor;
 import org.objectweb.asm.Opcodes;
 
 /**
@@ -72,6 +74,8 @@
 	public MethodProbesVisitor visitMethod(final int access, final String name,
 			final String desc, final String signature, final String[] exceptions) {
 
+		InstrSupport.assertNotInstrumented(name, coverage.getName());
+
 		// TODO: Use filter hook
 		if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
 			return null;
@@ -92,6 +96,13 @@
 	}
 
 	@Override
+	public FieldVisitor visitField(final int access, final String name,
+			final String desc, final String signature, final Object value) {
+		InstrSupport.assertNotInstrumented(name, coverage.getName());
+		return super.visitField(access, name, desc, signature, value);
+	}
+
+	@Override
 	public void visitTotalProbeCount(final int count) {
 		// nothing to do
 	}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java
index 796a004..d9fb76e 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/ClassInstrumenter.java
@@ -11,8 +11,6 @@
  *******************************************************************************/
 package org.jacoco.core.internal.instr;
 
-import static java.lang.String.format;
-
 import org.jacoco.core.internal.flow.ClassProbesVisitor;
 import org.jacoco.core.internal.flow.MethodProbesVisitor;
 import org.jacoco.core.runtime.IExecutionDataAccessorGenerator;
@@ -78,7 +76,7 @@
 	@Override
 	public FieldVisitor visitField(final int access, final String name,
 			final String desc, final String signature, final Object value) {
-		assertNotInstrumented(name, InstrSupport.DATAFIELD_NAME);
+		InstrSupport.assertNotInstrumented(name, className);
 		return super.visitField(access, name, desc, signature, value);
 	}
 
@@ -86,7 +84,7 @@
 	public MethodProbesVisitor visitMethod(final int access, final String name,
 			final String desc, final String signature, final String[] exceptions) {
 
-		assertNotInstrumented(name, InstrSupport.INITMETHOD_NAME);
+		InstrSupport.assertNotInstrumented(name, className);
 
 		final MethodVisitor mv = cv.visitMethod(access, name, desc, signature,
 				exceptions);
@@ -119,27 +117,6 @@
 		super.visitEnd();
 	}
 
-	/**
-	 * Ensures that the given member does not correspond to a internal member
-	 * created by the instrumentation process. This would mean that the class
-	 * has been instrumented twice.
-	 * 
-	 * @param member
-	 *            name of the member to check
-	 * @param instrMember
-	 *            name of a instrumentation member
-	 * @throws IllegalStateException
-	 *             thrown if the member has the same name than the
-	 *             instrumentation member
-	 */
-	private void assertNotInstrumented(final String member,
-			final String instrMember) throws IllegalStateException {
-		if (member.equals(instrMember)) {
-			throw new IllegalStateException(format(
-					"Class %s is already instrumented.", className));
-		}
-	}
-
 	// === probe array strategies ===
 
 	private class ClassTypeStrategy implements IProbeArrayStrategy {
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java b/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
index 8bb4602..8c74a4b 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/instr/InstrSupport.java
@@ -11,6 +11,8 @@
  *******************************************************************************/
 package org.jacoco.core.internal.instr;
 
+import static java.lang.String.format;
+
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 
@@ -61,6 +63,27 @@
 			| Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
 
 	/**
+	 * Ensures that the given member does not correspond to a internal member
+	 * created by the instrumentation process. This would mean that the class is
+	 * already instrumented.
+	 * 
+	 * @param member
+	 *            name of the member to check
+	 * @param owner
+	 *            name of the class owning the member
+	 * @throws IllegalStateException
+	 *             thrown if the member has the same name than the
+	 *             instrumentation member
+	 */
+	public static void assertNotInstrumented(final String member,
+			final String owner) throws IllegalStateException {
+		if (member.equals(DATAFIELD_NAME) || member.equals(INITMETHOD_NAME)) {
+			throw new IllegalStateException(format(
+					"Class %s is already instrumented.", owner));
+		}
+	}
+
+	/**
 	 * Generates the instruction to push the given int value on the stack.
 	 * Implementation taken from
 	 * {@link org.objectweb.asm.commons.GeneratorAdapter#push(int)}.
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index a002b95..306c671 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -38,6 +38,8 @@
 <ul>
   <li>More context information when exceptions occur during analysis or
       instrumentation (GitHub #104).</li>
+  <li>If analysis is performed on offline instrumented classes - which is an
+      build configuration error - an exception is now thrown (GitHub #108).</li>
 </ul>