package com.jetbrains.lang.dart.ide.runner.server.vmService.frame;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.ui.ColoredTextContainer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.SmartList;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.evaluation.XDebuggerEvaluator;
import com.intellij.xdebugger.frame.XCompositeNode;
import com.intellij.xdebugger.frame.XStackFrame;
import com.intellij.xdebugger.frame.XValueChildrenList;
import com.jetbrains.lang.dart.ide.runner.server.vmService.DartVmServiceDebugProcess;
import org.dartlang.vm.service.consumer.GetObjectConsumer;
import org.dartlang.vm.service.element.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class DartVmServiceStackFrame extends XStackFrame {
@NotNull private final DartVmServiceDebugProcess myDebugProcess;
@NotNull private final String myIsolateId;
@NotNull private final Frame myVmFrame;
@Nullable private final InstanceRef myException;
@Nullable private final XSourcePosition mySourcePosition;
@Nullable private final List<Frame> myVmFrames;
private boolean myIsDroppableFrame;
public DartVmServiceStackFrame(@NotNull final DartVmServiceDebugProcess debugProcess,
@NotNull final String isolateId,
@NotNull final Frame vmFrame,
@Nullable List<Frame> vmFrames,
@Nullable final InstanceRef exception) {
myDebugProcess = debugProcess;
myIsolateId = isolateId;
myVmFrame = vmFrame;
myVmFrames = vmFrames;
myException = exception;
if (vmFrame.getLocation() == null) {
mySourcePosition = null;
}
else {
mySourcePosition = debugProcess.getSourcePosition(isolateId, vmFrame.getLocation().getScript(), vmFrame.getLocation().getTokenPos());
}
}
@NotNull
public String getIsolateId() {
return myIsolateId;
}
@Nullable
@Override
public XSourcePosition getSourcePosition() {
return mySourcePosition;
}
public int getFrameIndex() {
return myVmFrames == null ? 0 : myVmFrames.indexOf(myVmFrame);
}
public void setIsDroppableFrame(boolean value) {
myIsDroppableFrame = value;
}
private boolean isLastFrame() {
if (myVmFrames == null) {
return true;
}
return getFrameIndex() == (myVmFrames.size() - 1);
}
@Override
public void customizePresentation(@NotNull final ColoredTextContainer component) {
final String name = StringUtil.trimEnd(myVmFrame.getCode().getName(), "="); // trim setter postfix
final boolean causal = myVmFrame.getKind() == FrameKind.AsyncCausal;
component.append(name, causal ? SimpleTextAttributes.REGULAR_ITALIC_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES);
if (mySourcePosition != null) {
final String text = " (" + mySourcePosition.getFile().getName() + ":" + (mySourcePosition.getLine() + 1) + ")";
component.append(text, SimpleTextAttributes.GRAY_ATTRIBUTES);
}
component.setIcon(AllIcons.Debugger.StackFrame);
}
@NotNull
@Override
public Object getEqualityObject() {
return myVmFrame.getLocation().getScript().getId() + ":" + myVmFrame.getCode().getId();
}
@Override
public void computeChildren(@NotNull final XCompositeNode node) {
if (myException != null) {
final DartVmServiceValue exception = new DartVmServiceValue(myDebugProcess, myIsolateId, "exception", myException, null, null, true);
node.addChildren(XValueChildrenList.singleton(exception), false);
}
final ElementList<BoundVariable> vars = myVmFrame.getVars();
if (vars == null) {
node.addChildren(XValueChildrenList.EMPTY, true);
return;
}
BoundVariable thisVar = null;
for (BoundVariable var : vars) {
if ("this".equals(var.getName())) {
// in some cases "this" var is not the first one in the list, no idea why
thisVar = var;
break;
}
}
addStaticFieldsIfPresentAndThenAllVars(node, thisVar, vars);
}
private void addStaticFieldsIfPresentAndThenAllVars(@NotNull final XCompositeNode node,
@Nullable final BoundVariable thisVar,
@NotNull final ElementList<BoundVariable> vars) {
if (thisVar == null) {
addVars(node, vars);
return;
}
myDebugProcess.getVmServiceWrapper().getObject(myIsolateId, thisVar.getValue().getClassRef().getId(), new GetObjectConsumer() {
@Override
public void received(Obj classObj) {
final SmartList<FieldRef> staticFields = new SmartList<>();
for (FieldRef fieldRef : ((ClassObj)classObj).getFields()) {
if (fieldRef.isStatic()) {
staticFields.add(fieldRef);
}
}
if (!staticFields.isEmpty()) {
final XValueChildrenList list = new XValueChildrenList();
list.addTopGroup(new DartStaticFieldsGroup(myDebugProcess, myIsolateId, ((ClassObj)classObj).getName(), staticFields));
node.addChildren(list, false);
}
addVars(node, vars);
}
@Override
public void received(Sentinel sentinel) {
node.setErrorMessage(sentinel.getValueAsString());
}
@Override
public void onError(RPCError error) {
node.setErrorMessage(error.getMessage());
}
});
}
private void addVars(@NotNull final XCompositeNode node, @NotNull final ElementList<BoundVariable> vars) {
final XValueChildrenList childrenList = new XValueChildrenList(vars.size());
for (BoundVariable var : vars) {
final InstanceRef value = var.getValue();
if (value != null) {
final DartVmServiceValue.LocalVarSourceLocation varLocation =
"this".equals(var.getName())
? null
: new DartVmServiceValue.LocalVarSourceLocation(myVmFrame.getLocation().getScript(), var.getDeclarationTokenPos());
childrenList.add(new DartVmServiceValue(myDebugProcess, myIsolateId, var.getName(), value, varLocation, null, false));
}
}
node.addChildren(childrenList, true);
}
@Nullable
@Override
public XDebuggerEvaluator getEvaluator() {
return new DartVmServiceEvaluator(myDebugProcess, myIsolateId, myVmFrame);
}
public boolean isInDartSdkPatchFile() {
return mySourcePosition != null && (mySourcePosition.getFile() instanceof LightVirtualFile);
}
public boolean canDrop() {
return myIsDroppableFrame && !isLastFrame();
}
public void dropFrame() {
myDebugProcess.dropFrame(this);
}
}