/* * Copyright 2013-2017 consulo.io * * 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 consulo.csharp.ide.debugger; import java.util.Collections; import java.util.List; import java.util.Set; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.util.Consumer; import com.intellij.xdebugger.XDebugSession; import com.intellij.xdebugger.XSourcePosition; import com.intellij.xdebugger.evaluation.XDebuggerEvaluator; import com.intellij.xdebugger.frame.XNamedValue; import consulo.csharp.ide.debugger.expressionEvaluator.Evaluator; import consulo.csharp.ide.debugger.expressionEvaluator.ThisObjectEvaluator; import consulo.csharp.lang.CSharpFileType; import consulo.csharp.lang.CSharpLanguage; import consulo.csharp.lang.psi.impl.fragment.CSharpFragmentFactory; import consulo.csharp.lang.psi.impl.fragment.CSharpFragmentFileImpl; import consulo.csharp.lang.psi.impl.source.CSharpMethodCallExpressionImpl; import consulo.dotnet.debugger.DotNetDebugContext; import consulo.dotnet.debugger.DotNetDebuggerProvider; import consulo.dotnet.debugger.nodes.DotNetFieldOrPropertyValueNode; import consulo.dotnet.debugger.nodes.DotNetStructValueInfo; import consulo.dotnet.debugger.proxy.DotNetFieldOrPropertyProxy; import consulo.dotnet.debugger.proxy.DotNetInvalidObjectException; import consulo.dotnet.debugger.proxy.DotNetNotSuspendedException; import consulo.dotnet.debugger.proxy.DotNetStackFrameProxy; import consulo.dotnet.debugger.proxy.DotNetThrowValueException; import consulo.dotnet.debugger.proxy.DotNetTypeProxy; import consulo.dotnet.debugger.proxy.value.DotNetObjectValueProxy; import consulo.dotnet.debugger.proxy.value.DotNetStructValueProxy; import consulo.dotnet.debugger.proxy.value.DotNetValueProxy; import consulo.dotnet.psi.DotNetExpression; import consulo.dotnet.psi.DotNetLikeMethodDeclaration; import consulo.dotnet.psi.DotNetReferenceExpression; /** * @author VISTALL * @since 10.04.14 */ public class CSharpDebuggerProvider extends DotNetDebuggerProvider { private static final Logger LOGGER = Logger.getInstance(CSharpDebuggerProvider.class); @NotNull @Override public PsiFile createExpressionCodeFragment(@NotNull Project project, @NotNull PsiElement sourcePosition, @NotNull String text, boolean isPhysical) { return CSharpFragmentFactory.createExpressionFragment(project, text, sourcePosition); } @Override public void evaluate(@NotNull DotNetStackFrameProxy frame, @NotNull DotNetDebugContext debuggerContext, @NotNull String expression, @Nullable PsiElement elementAt, @NotNull XDebuggerEvaluator.XEvaluationCallback callback, @Nullable XSourcePosition sourcePosition) { if(elementAt == null) { XDebugSession session = debuggerContext.getSession(); XSourcePosition currentPosition = session.getCurrentPosition(); if(currentPosition == null) { callback.errorOccurred("cant evaluate"); return; } VirtualFile file = currentPosition.getFile(); PsiFile psiFile = PsiManager.getInstance(debuggerContext.getProject()).findFile(file); if(psiFile == null) { callback.errorOccurred("cant evaluate"); return; } elementAt = psiFile.findElementAt(currentPosition.getOffset()); if(elementAt == null) { callback.errorOccurred("cant evaluate"); return; } } CSharpFragmentFileImpl expressionFragment = CSharpFragmentFactory.createExpressionFragment(debuggerContext.getProject(), expression, elementAt); PsiElement[] children = expressionFragment.getChildren(); if(children.length == 0) { callback.errorOccurred("no expression"); return; } PsiElement fragmentElement = children[0]; DotNetExpression expressionPsi = fragmentElement instanceof DotNetExpression ? (DotNetExpression) fragmentElement : null; if(expressionPsi == null) { callback.errorOccurred("no expression"); return; } CSharpExpressionEvaluator expressionEvaluator = new CSharpExpressionEvaluator(); try { expressionPsi.accept(expressionEvaluator); CSharpEvaluateContext evaluateContext = new CSharpEvaluateContext(debuggerContext, frame, elementAt); List<Evaluator> evaluators = expressionEvaluator.getEvaluators(); if(evaluators.isEmpty()) { callback.errorOccurred("cant evaluate expression"); return; } evaluateContext.evaluate(evaluators); DotNetValueProxy targetValue = evaluateContext.popValue(); if(targetValue != null) { callback.evaluated(new CSharpWatcherNode(debuggerContext, expression, frame, targetValue)); } else { callback.errorOccurred("no value"); } } catch(DotNetThrowValueException e) { callback.errorOccurred(StringUtil.notNullize(e.getMessage(), "unknown exception")); } catch(DotNetNotSuspendedException e) { callback.errorOccurred("not suspended"); } catch(DotNetInvalidObjectException e) { callback.errorOccurred("invalid object"); } catch(Exception e) { String message = e.getMessage(); if(message == null) { message = e.getClass().getSimpleName() + " was throw"; CSharpDebuggerProvider.LOGGER.error("Exception have null message", e); } callback.errorOccurred(message); } } @Override public void evaluate(@NotNull DotNetStackFrameProxy frame, @NotNull DotNetDebugContext debuggerContext, @NotNull DotNetReferenceExpression referenceExpression, @NotNull Set<Object> visitedVariables, @NotNull Consumer<XNamedValue> consumer) { try { List<Evaluator> evaluators = ApplicationManager.getApplication().runReadAction((Computable<List<Evaluator>>) () -> { PsiElement resolvedElement = referenceExpression.resolve(); if(referenceExpression.getParent() instanceof CSharpMethodCallExpressionImpl || resolvedElement instanceof DotNetLikeMethodDeclaration) { return Collections.emptyList(); } CSharpExpressionEvaluator expressionEvaluator = new CSharpExpressionEvaluator(); referenceExpression.accept(expressionEvaluator); return expressionEvaluator.getEvaluators(); }); if(evaluators.isEmpty()) { return; } CSharpEvaluateContext evaluateContext = new CSharpEvaluateContext(debuggerContext, frame, referenceExpression); evaluateContext.evaluate(evaluators); Pair<DotNetValueProxy, Object> objectPair = evaluateContext.pop(); if(objectPair != null && objectPair.getSecond() instanceof DotNetFieldOrPropertyProxy) { DotNetFieldOrPropertyProxy fieldOrPropertyMirror = (DotNetFieldOrPropertyProxy) objectPair.getSecond(); if(visitedVariables.contains(fieldOrPropertyMirror)) { return; } visitedVariables.add(fieldOrPropertyMirror); DotNetTypeProxy parent = fieldOrPropertyMirror.getParentType(); DotNetValueProxy thisObjectValue = ThisObjectEvaluator.calcThisObject(frame, frame.getThisObject()); DotNetTypeProxy type = thisObjectValue.getType(); if(thisObjectValue instanceof DotNetObjectValueProxy && parent.equals(type)) { consumer.consume(new DotNetFieldOrPropertyValueNode(debuggerContext, fieldOrPropertyMirror, frame, (DotNetObjectValueProxy) thisObjectValue)); } else if(thisObjectValue instanceof DotNetStructValueProxy && parent.equals(type)) { DotNetStructValueProxy structValueMirror = (DotNetStructValueProxy) thisObjectValue; DotNetStructValueInfo valueInfo = new DotNetStructValueInfo(structValueMirror, null, fieldOrPropertyMirror, objectPair.getFirst()); consumer.consume(new DotNetFieldOrPropertyValueNode(debuggerContext, fieldOrPropertyMirror, frame, null, valueInfo)); } else { consumer.consume(new CSharpWatcherNode(debuggerContext, ApplicationManager.getApplication().runReadAction((Computable<String>) referenceExpression::getText), frame, objectPair .getFirst())); } } } catch(Exception e) { // ignored } } @Override public boolean isSupported(@NotNull PsiFile psiFile) { return psiFile.getFileType() == CSharpFileType.INSTANCE; } @Override public Language getEditorLanguage() { return CSharpLanguage.INSTANCE; } }