Bug 463236: Scrapbook evaluations fail in pre-1.7 projects
diff --git a/org.eclipse.jdt.debug.ui/Snippet Support/org/eclipse/jdt/internal/debug/ui/snippeteditor/ScrapbookMain.java b/org.eclipse.jdt.debug.ui/Snippet Support/org/eclipse/jdt/internal/debug/ui/snippeteditor/ScrapbookMain.java
index 23d226f..b3c379f 100644
--- a/org.eclipse.jdt.debug.ui/Snippet Support/org/eclipse/jdt/internal/debug/ui/snippeteditor/ScrapbookMain.java
+++ b/org.eclipse.jdt.debug.ui/Snippet Support/org/eclipse/jdt/internal/debug/ui/snippeteditor/ScrapbookMain.java
@@ -23,10 +23,7 @@
 /**
  * Support class for launching a snippet evaluation.
  * <p>
- * CAUTION 1: If you touch this class, you need to make sure the line number of the {@link #nop()} method's statement gets updated in
- * {@link org.eclipse.jdt.internal.debug.ui.snippeteditor.ScrapbookLauncher#createMagicBreakpoint()}.
- * <p>
- * CAUTION 2: This class gets compiled with target=jsr14, see scripts/buildExtraJAR.xml. Don't use URLClassLoader#close() or other post-1.4 APIs!
+ * CAUTION: This class gets compiled with target=jsr14, see scripts/buildExtraJAR.xml. Don't use URLClassLoader#close() or other post-1.4 APIs!
  */
 public class ScrapbookMain {
 	
@@ -61,9 +58,14 @@
 		method.invoke(null, new Object[] {ScrapbookMain.class});
 	}
 	
+	/**
+	 * The magic "no-op" method, where {@link org.eclipse.jdt.internal.debug.ui.snippeteditor.ScrapbookLauncher#createMagicBreakpoint(String)} sets a
+	 * breakpoint.
+	 * <p>
+	 */
 	public static void nop() {
 		try {
-			Thread.sleep(100); // TODO: The line number of this statement needs to be updated in ScrapbookLauncher#createMagicBreakpoint()!
+			Thread.sleep(100);
 		} catch(InterruptedException e) {
 		}
 	}
diff --git a/org.eclipse.jdt.debug.ui/snippetsupport.jar b/org.eclipse.jdt.debug.ui/snippetsupport.jar
index bcb9938..2b871e7 100644
--- a/org.eclipse.jdt.debug.ui/snippetsupport.jar
+++ b/org.eclipse.jdt.debug.ui/snippetsupport.jar
Binary files differ
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/snippeteditor/ScrapbookLauncher.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/snippeteditor/ScrapbookLauncher.java
index dc3876d..12e9feb 100644
--- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/snippeteditor/ScrapbookLauncher.java
+++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/snippeteditor/ScrapbookLauncher.java
@@ -29,8 +29,10 @@
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.FileLocator;
 import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.Status;
 import org.eclipse.debug.core.DebugEvent;
 import org.eclipse.debug.core.DebugPlugin;
 import org.eclipse.debug.core.IDebugEventSetListener;
@@ -44,6 +46,12 @@
 import org.eclipse.debug.ui.IDebugUIConstants;
 import org.eclipse.jdt.core.IJavaProject;
 import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.ToolFactory;
+import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.core.util.IClassFileReader;
+import org.eclipse.jdt.core.util.ICodeAttribute;
+import org.eclipse.jdt.core.util.ILineNumberAttribute;
+import org.eclipse.jdt.core.util.IMethodInfo;
 import org.eclipse.jdt.debug.core.IJavaLineBreakpoint;
 import org.eclipse.jdt.debug.core.JDIDebugModel;
 import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants;
@@ -130,7 +138,8 @@
 		}
 		
 		List<IRuntimeClasspathEntry> cp = new ArrayList<IRuntimeClasspathEntry>(3);
-		IRuntimeClasspathEntry supportEntry = JavaRuntime.newArchiveRuntimeClasspathEntry(new Path(jarURL.getFile()));
+		String jarFile = jarURL.getFile();
+		IRuntimeClasspathEntry supportEntry = JavaRuntime.newArchiveRuntimeClasspathEntry(new Path(jarFile));
 		cp.add(supportEntry);
 		// get bootpath entries
 		try {
@@ -142,14 +151,14 @@
 			}
 			IRuntimeClasspathEntry[] classPath = cp.toArray(new IRuntimeClasspathEntry[cp.size()]);
 			
-			return doLaunch(javaProject, page, classPath);
+			return doLaunch(javaProject, page, classPath, jarFile);
 		} catch (CoreException e) {
 			JDIDebugUIPlugin.errorDialog("Unable to launch scrapbook VM", e); //$NON-NLS-1$
 		}
 		return null;
 	}
 
-	private ILaunch doLaunch(IJavaProject p, IFile page, IRuntimeClasspathEntry[] classPath) {
+	private ILaunch doLaunch(IJavaProject p, IFile page, IRuntimeClasspathEntry[] classPath, String jarFile) {
 		try {
 			if (fVMsToScrapbooks.isEmpty()) {
 				// register for debug events if a scrapbook is not currently running
@@ -230,7 +239,7 @@
 			ILaunch launch = config.launch(ILaunchManager.DEBUG_MODE, null);
 			if (launch != null) {
 				IDebugTarget dt = launch.getDebugTarget();
-				IBreakpoint magicBreakpoint = createMagicBreakpoint();
+				IBreakpoint magicBreakpoint = createMagicBreakpoint(jarFile);
 				fScrapbookToVMs.put(page, dt);
 				fVMsToScrapbooks.put(dt, page);
 				fVMsToBreakpoints.put(dt, magicBreakpoint);
@@ -245,18 +254,35 @@
 	}
 	
 	/**
-	 * Creates an "invisible" line breakpoint. 
+	 * Creates an "invisible" line breakpoint.
+	 * 
+	 * @param jarFile
+	 *            path to the snippetsupport.jar file
 	 * @return the new 'magic' breakpoint
-	 * @throws CoreException if an exception occurs
+	 * @throws CoreException
+	 *             if an exception occurs
 	 */
-	IBreakpoint createMagicBreakpoint() throws CoreException {
-		// set a breakpoint on the "Thread.sleep(100);" line of the no-op method of ScrapbookMain
+	IBreakpoint createMagicBreakpoint(String jarFile) throws CoreException {
+		// set a breakpoint on the "Thread.sleep(100);" line of the "ScrapbookMainnop()" method
 		String typeName = "org.eclipse.jdt.internal.debug.ui.snippeteditor.ScrapbookMain"; //$NON-NLS-1$
-		int lineNumber = 66; // TODO: This line number needs to be adjusted when ScrapbookMain is modified.
 
-		fMagicBreakpoint = JDIDebugModel.createLineBreakpoint(ResourcesPlugin.getWorkspace().getRoot(), typeName, lineNumber, -1, -1, 0, false, null);
-		fMagicBreakpoint.setPersisted(false);
-		return fMagicBreakpoint;
+		IClassFileReader reader = ToolFactory.createDefaultClassFileReader(jarFile, typeName.replace('.', '/')
+				+ ".class", IClassFileReader.METHOD_INFOS | IClassFileReader.METHOD_BODIES); //$NON-NLS-1$
+		IMethodInfo[] methodInfos = reader.getMethodInfos();
+		for (IMethodInfo methodInfo : methodInfos) {
+			if (!CharOperation.equals("nop".toCharArray(), methodInfo.getName())) {//$NON-NLS-1$
+				continue;
+			}
+			ICodeAttribute codeAttribute = methodInfo.getCodeAttribute();
+			ILineNumberAttribute lineNumberAttribute = codeAttribute.getLineNumberAttribute();
+			int[][] lineNumberTable = lineNumberAttribute.getLineNumberTable();
+			int lineNumber = lineNumberTable[0][1];
+
+			fMagicBreakpoint = JDIDebugModel.createLineBreakpoint(ResourcesPlugin.getWorkspace().getRoot(), typeName, lineNumber, -1, -1, 0, false, null);
+			fMagicBreakpoint.setPersisted(false);
+			return fMagicBreakpoint;
+		}
+		throw new CoreException(new Status(IStatus.ERROR, JDIDebugUIPlugin.getUniqueIdentifier(), IJavaDebugUIConstants.INTERNAL_ERROR, "An error occurred creating the evaluation breakpoint location.", null)); //$NON-NLS-1$
 	}
 
 	/**