/******************************************************************************* * Copyright (c) 2012 Pivotal Software, Inc. * 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: * Pivotal Software, Inc. - initial API and implementation *******************************************************************************/ package org.grails.ide.eclipse.groovy.debug.core; import org.eclipse.contribution.jdt.debug.IDebugProvider; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugException; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.debug.core.IJavaBreakpoint; import org.eclipse.jdt.debug.core.IJavaBreakpointListener; import org.eclipse.jdt.debug.core.IJavaDebugTarget; import org.eclipse.jdt.debug.core.IJavaObject; import org.eclipse.jdt.debug.core.IJavaStackFrame; import org.eclipse.jdt.debug.core.IJavaThread; import org.eclipse.jdt.debug.eval.IEvaluationListener; import org.eclipse.jdt.groovy.core.util.ContentTypeUtils; import org.eclipse.jdt.groovy.core.util.ReflectionUtils; import org.eclipse.jdt.internal.debug.core.breakpoints.ConditionalBreakpointHandler; import org.grails.ide.eclipse.groovy.debug.core.breakpoints.GroovyConditionalBreakpointHandler; import org.grails.ide.eclipse.groovy.debug.core.evaluation.GroovyJDIEvaluator; import com.sun.jdi.Location; /** * Provides Groovy-aware debug support in the display view. * @author Andrew Eisenberg * @author Andy Clement * @since 2.5.1 */ public class GroovyDebugProvider implements IDebugProvider { private static final String[] EXTRA_STEP_FILTERS = new String[] { // can't put either of these on the app side // or else step into won't work // "org.codehaus.groovy.runtime.*", // "org.codehaus.groovy.runtime.MetaClassHelper", // yay! this works "groovy.*", // must do these explicitly since we can't do org.codehaus.groovy.runtime.* "org.codehaus.groovy.runtime.typehandling.*", "org.codehaus.groovy.runtime.wrappers.*", "org.codehaus.groovy.runtime.metaclass.*", "org.codehaus.groovy.runtime.GroovyCategorySupport*", "org.codehaus.groovy.runtime.ScriptBytecodeAdapter", "org.codehaus.groovy.runtime.InvokerHelper", "org.codehaus.groovy.runtime.ArrayUtil", "org.codehaus.groovy.runtime.DefaultGroovyMethods", "org.codehaus.groovy.runtime.callsite.*", "org.codehaus.groovy.runtime.dgmimpl.*", "org.codehaus.groovy.runtime.dgm*", "org.codehaus.groovy.util.*", "org.codehaus.groovy.reflection.*", "org.codehaus.groovy.tools.*", "org.codehaus.groovy.classgen.*", "org.codehaus.groovy.control.*", // spring stuff "org.springframework.*", // some other important ones "java.*", "groovyjarjar.*", "com.sun.*", "sun.net.*", "sun.nio.*", // the following 3 cannot be filtered in the debugged side // otherwise some step-intos will not work // "sun.misc.*", // "sun.reflect.*", // "sun.*", }; public void performEvaluation(String snippet, IJavaObject object, IJavaStackFrame frame, IEvaluationListener listener, IJavaProject javaProject, int evaluationDetail, boolean hitBreakpoints) throws DebugException { GroovyJDIEvaluator evaluator = new GroovyJDIEvaluator(javaProject, (IJavaDebugTarget) frame.getDebugTarget()); try { evaluator.evaluate(snippet, object, frame, listener, evaluationDetail, hitBreakpoints); } catch (CoreException e) { if (e instanceof DebugException) { throw (DebugException) e; } else { throw new DebugException(e.getStatus()); } } } public boolean shouldPerformEvaluation(IJavaStackFrame frame) throws DebugException { String sourcePath = frame.getSourcePath(); if (!GroovyDebugCoreActivator.isDisplayViewEnabled()) { return false; } return (ContentTypeUtils.isGroovyLikeFileName(sourcePath)) || (sourcePath != null && sourcePath.endsWith("gsp")); } public String[] augmentStepFilters(String[] origStepFilters) { if (!GroovyDebugCoreActivator.isStepFilteringEnabled()) { return origStepFilters; } if (origStepFilters == null || origStepFilters.length == 0) { return EXTRA_STEP_FILTERS; } else { // look for 'org.codehaus.groovy' to remove int ocgIndex = 0; while (ocgIndex < origStepFilters.length) { if (origStepFilters[ocgIndex].equals("org.codehaus.groovy.*")) { break; } ocgIndex++; } String[] removedStepFilters; if (ocgIndex < origStepFilters.length) { removedStepFilters = new String[origStepFilters.length-1]; System.arraycopy(origStepFilters, 0, removedStepFilters, 0, ocgIndex); System.arraycopy(origStepFilters, ocgIndex+1, removedStepFilters, ocgIndex, removedStepFilters.length - ocgIndex); } else { removedStepFilters = origStepFilters; } String[] newStepFilters = new String[removedStepFilters.length + EXTRA_STEP_FILTERS.length]; System.arraycopy(removedStepFilters, 0, newStepFilters, 0, removedStepFilters.length); System.arraycopy(EXTRA_STEP_FILTERS, 0, newStepFilters, removedStepFilters.length, EXTRA_STEP_FILTERS.length); return newStepFilters; } } /** * @return true iff this is a native frame with a sun. package prefix */ public boolean shouldPerformExtraStep(Location location) throws DebugException { if (!GroovyDebugCoreActivator.isStepFilteringEnabled()) { return false; } if (location.lineNumber() <= 0) { // synthetic frame // System.out.println("Filtered: " + location.declaringType().name() + "." + location.method() + " :: " + location); return true; } // hmmmmm.....may not need this any more String name = location.declaringType().name(); if (name.startsWith("sun.") || name.startsWith("groovy.lang.") || name.startsWith("org.codehaus.groovy")) { // System.out.println("Filtered: " + location.declaringType().name() + "." + location.method() + "()"); return true; } return false; } /* * Closely follow the logic of ConditionalBreakpointListener.breakpointHit() */ public int conditionalBreakpointHit(IJavaThread thread, IJavaBreakpoint breakpoint, ConditionalBreakpointHandler handler) { try { GroovyConditionalBreakpointHandler groovyHandler = new GroovyConditionalBreakpointHandler(); int result = groovyHandler.conditionalBreakpointHit(thread, breakpoint); updateErrors(handler, groovyHandler); // FIXADE I'm not exactly sure why this is necessary, but when this is here, // we avoid an occasional InvalidStackFrameException // I think this has something to do with not sending too many requests to the debugged app // at once. // It seems to be that the more complicated the expression being evaluated, the // higher the wait number must be. // If you're feeling adventurous, then I suggest you remove the synchonized block // and try to make things work without the wait. synchronized (this) { try { wait(50); } catch (InterruptedException e) { } } return result; } catch (Exception e) { GroovyDebugCoreActivator.log(e); return IJavaBreakpointListener.SUSPEND; } } private void updateErrors(ConditionalBreakpointHandler handler, GroovyConditionalBreakpointHandler groovyHandler) { ReflectionUtils.setPrivateField(ConditionalBreakpointHandler.class, "fHasErrors", handler, groovyHandler.hasErrors()); } public boolean isAlwaysInteretingLaunch() { return GroovyDebugCoreActivator.isStepFilteringEnabledOnAll(); } }