/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.debugger.stepping;
import com.intellij.debugger.engine.DebugProcessImpl;
import com.intellij.debugger.engine.SuspendContextImpl;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.jdi.StackFrameProxyImpl;
import com.intellij.debugger.jdi.ThreadReferenceProxyImpl;
import com.intellij.debugger.settings.DebuggerSettings;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.ui.classFilter.ClassFilter;
import com.intellij.ui.classFilter.DebuggerClassFilterProvider;
import com.sun.jdi.Location;
import com.sun.jdi.ObjectCollectedException;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.EventRequestManager;
import com.sun.jdi.request.StepRequest;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.debugger.NoStrataPositionManagerHelperKt;
import org.jetbrains.kotlin.psi.KtFunctionLiteral;
import org.jetbrains.kotlin.psi.KtNamedFunction;
import java.util.ArrayList;
import java.util.List;
public class DebuggerSteppingHelper {
public static DebugProcessImpl.ResumeCommand createStepOverCommand(
final SuspendContextImpl suspendContext,
final boolean ignoreBreakpoints,
final KotlinSteppingCommandProvider.KotlinSourcePosition kotlinSourcePosition
) {
final DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
return debugProcess.new ResumeCommand(suspendContext) {
@Override
public void contextAction() {
boolean isDexDebug = NoStrataPositionManagerHelperKt.isDexDebug(suspendContext.getDebugProcess());
try {
StackFrameProxyImpl frameProxy = suspendContext.getFrameProxy();
if (frameProxy != null) {
Action action = KotlinSteppingCommandProviderKt.getStepOverAction(
frameProxy.location(),
kotlinSourcePosition,
frameProxy,
isDexDebug
);
createStepRequest(
suspendContext, getContextThread(),
debugProcess.getVirtualMachineProxy().eventRequestManager(),
StepRequest.STEP_LINE, StepRequest.STEP_OUT);
action.apply(debugProcess, suspendContext, ignoreBreakpoints);
return;
}
debugProcess.createStepOutCommand(suspendContext).contextAction();
}
catch (EvaluateException ignored) {
}
}
};
}
public static DebugProcessImpl.ResumeCommand createStepOutCommand(
final SuspendContextImpl suspendContext,
final boolean ignoreBreakpoints,
final List<KtNamedFunction> inlineFunctions,
final KtFunctionLiteral inlineArgument
) {
final DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
return debugProcess.new ResumeCommand(suspendContext) {
@Override
public void contextAction() {
try {
StackFrameProxyImpl frameProxy = suspendContext.getFrameProxy();
if (frameProxy != null) {
Action action = KotlinSteppingCommandProviderKt.getStepOutAction(
frameProxy.location(),
suspendContext,
inlineFunctions,
inlineArgument
);
createStepRequest(
suspendContext, getContextThread(),
debugProcess.getVirtualMachineProxy().eventRequestManager(),
StepRequest.STEP_LINE, StepRequest.STEP_OUT);
action.apply(debugProcess, suspendContext, ignoreBreakpoints);
return;
}
debugProcess.createStepOverCommand(suspendContext, ignoreBreakpoints).contextAction();
}
catch (EvaluateException ignored) {
}
}
};
}
// copied from DebugProcessImpl.doStep
private static void createStepRequest(
@NotNull SuspendContextImpl suspendContext,
@Nullable ThreadReferenceProxyImpl stepThread,
@NotNull EventRequestManager requestManager,
int size, int depth
) {
if (stepThread == null) {
return;
}
try {
ThreadReference stepThreadReference = stepThread.getThreadReference();
requestManager.deleteEventRequests(requestManager.stepRequests());
StepRequest stepRequest = requestManager.createStepRequest(stepThreadReference, size, depth);
List<ClassFilter> activeFilters = getActiveFilters();
if (!activeFilters.isEmpty()) {
String currentClassName = getCurrentClassName(stepThread);
if (currentClassName == null || !DebuggerUtilsEx.isFiltered(currentClassName, activeFilters)) {
// add class filters
for (ClassFilter filter : activeFilters) {
stepRequest.addClassExclusionFilter(filter.getPattern());
}
}
}
// suspend policy to match the suspend policy of the context:
// if all threads were suspended, then during stepping all the threads must be suspended
// if only event thread were suspended, then only this particular thread must be suspended during stepping
stepRequest.setSuspendPolicy(suspendContext.getSuspendPolicy() == EventRequest.SUSPEND_EVENT_THREAD
? EventRequest.SUSPEND_EVENT_THREAD
: EventRequest.SUSPEND_ALL);
stepRequest.enable();
}
catch (ObjectCollectedException ignored) {
}
}
// copied from DebugProcessImpl.getActiveFilters
@NotNull
private static List<ClassFilter> getActiveFilters() {
List<ClassFilter> activeFilters = new ArrayList<ClassFilter>();
DebuggerSettings settings = DebuggerSettings.getInstance();
if (settings.TRACING_FILTERS_ENABLED) {
for (ClassFilter filter : settings.getSteppingFilters()) {
if (filter.isEnabled()) {
activeFilters.add(filter);
}
}
}
for (DebuggerClassFilterProvider provider : Extensions.getExtensions(DebuggerClassFilterProvider.EP_NAME)) {
for (ClassFilter filter : provider.getFilters()) {
if (filter.isEnabled()) {
activeFilters.add(filter);
}
}
}
return activeFilters;
}
// copied from DebugProcessImpl.getActiveFilters
@Nullable
private static String getCurrentClassName(ThreadReferenceProxyImpl thread) {
try {
if (thread != null && thread.frameCount() > 0) {
StackFrameProxyImpl stackFrame = thread.frame(0);
if (stackFrame != null) {
Location location = stackFrame.location();
ReferenceType referenceType = location == null ? null : location.declaringType();
if (referenceType != null) {
return referenceType.name();
}
}
}
}
catch (EvaluateException ignored) {
}
return null;
}
}