autoboxing support for debugger evaluators: binary/unary expressions, assignments...
[fedora-idea.git] / java / debugger / impl / src / com / intellij / debugger / actions / SetValueAction.java
blob288275b3a16b1dbf882bc557d04e5ccc7e737ab1
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.debugger.actions;
18 import com.intellij.debugger.DebuggerBundle;
19 import com.intellij.debugger.DebuggerInvocationUtil;
20 import com.intellij.debugger.DebuggerManagerEx;
21 import com.intellij.debugger.engine.ContextUtil;
22 import com.intellij.debugger.engine.evaluation.*;
23 import com.intellij.debugger.engine.evaluation.expression.*;
24 import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
25 import com.intellij.debugger.engine.events.SuspendContextCommandImpl;
26 import com.intellij.debugger.impl.*;
27 import com.intellij.debugger.jdi.LocalVariableProxyImpl;
28 import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
29 import com.intellij.debugger.ui.DebuggerExpressionComboBox;
30 import com.intellij.debugger.ui.EditorEvaluationCommand;
31 import com.intellij.debugger.ui.impl.DebuggerTreeRenderer;
32 import com.intellij.debugger.ui.impl.watch.*;
33 import com.intellij.debugger.ui.tree.render.HexRenderer;
34 import com.intellij.debugger.ui.tree.render.NodeRenderer;
35 import com.intellij.debugger.ui.tree.render.ValueLabelRenderer;
36 import com.intellij.openapi.actionSystem.AnActionEvent;
37 import com.intellij.openapi.editor.Editor;
38 import com.intellij.openapi.progress.ProcessCanceledException;
39 import com.intellij.openapi.progress.util.ProgressIndicatorListenerAdapter;
40 import com.intellij.openapi.progress.util.ProgressWindowWithNotification;
41 import com.intellij.openapi.project.Project;
42 import com.intellij.openapi.ui.Messages;
43 import com.intellij.psi.PsiDocumentManager;
44 import com.intellij.psi.PsiFile;
45 import com.intellij.ui.SimpleColoredComponent;
46 import com.intellij.util.IJSwingUtilities;
47 import com.sun.jdi.*;
49 import javax.swing.*;
52 * Class SetValueAction
53 * @author Jeka
55 public class SetValueAction extends DebuggerAction {
56 public void update(AnActionEvent e) {
57 boolean enable = false;
58 DebuggerTreeNodeImpl node = getSelectedNode(e.getDataContext());
59 if (node != null) {
60 NodeDescriptorImpl descriptor = node.getDescriptor();
61 if(descriptor instanceof ValueDescriptorImpl){
62 ValueDescriptorImpl valueDescriptor = ((ValueDescriptorImpl)descriptor);
63 enable = valueDescriptor.canSetValue();
66 e.getPresentation().setVisible(enable);
69 private void update(final DebuggerContextImpl context) {
70 DebuggerInvocationUtil.swingInvokeLater(context.getProject(), new Runnable() {
71 public void run() {
72 context.getDebuggerSession().refresh(false);
74 });
75 //node.setState(context);
78 public void actionPerformed(final AnActionEvent event) {
79 final DebuggerTreeNodeImpl node = getSelectedNode(event.getDataContext());
80 if (node == null) {
81 return;
83 final NodeDescriptorImpl descriptor = node.getDescriptor();
84 if (!(descriptor instanceof ValueDescriptorImpl)) {
85 return;
87 if(!((ValueDescriptorImpl)descriptor).canSetValue()) {
88 return;
91 final DebuggerTree tree = getTree(event.getDataContext());
92 final DebuggerContextImpl debuggerContext = getDebuggerContext(event.getDataContext());
93 tree.saveState(node);
95 if (descriptor instanceof FieldDescriptorImpl) {
96 FieldDescriptorImpl fieldDescriptor = (FieldDescriptorImpl)descriptor;
97 final Field field = fieldDescriptor.getField();
98 if (!field.isStatic()) {
99 final ObjectReference object = fieldDescriptor.getObject();
100 if (object != null) {
101 askAndSet(node, debuggerContext, new SetValueRunnable() {
102 public void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException, InvalidTypeException, EvaluateException {
103 object.setValue(field, preprocessValue(evaluationContext, newValue, field.type()));
104 update(debuggerContext);
107 public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
108 ClassNotLoadedException,
109 IncompatibleThreadStateException,
110 InvalidTypeException,
111 EvaluateException {
112 return evaluationContext.getDebugProcess().loadClass(evaluationContext, className, field.declaringType().classLoader());
117 else {
118 // field is static
119 ReferenceType refType = field.declaringType();
120 if (refType instanceof ClassType) {
121 final ClassType classType = (ClassType)refType;
122 askAndSet(node, debuggerContext, new SetValueRunnable() {
123 public void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException, InvalidTypeException, EvaluateException {
124 classType.setValue(field, preprocessValue(evaluationContext, newValue, field.type()));
125 update(debuggerContext);
128 public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
129 ClassNotLoadedException,
130 IncompatibleThreadStateException,
131 InvalidTypeException,
132 EvaluateException {
133 return evaluationContext.getDebugProcess().loadClass(evaluationContext, className,
134 field.declaringType().classLoader());
140 else if (descriptor instanceof LocalVariableDescriptorImpl) {
141 LocalVariableDescriptorImpl localDescriptor = (LocalVariableDescriptorImpl)descriptor;
142 final LocalVariableProxyImpl local = localDescriptor.getLocalVariable();
143 if (local != null) {
144 askAndSet(node, debuggerContext, new SetValueRunnable() {
145 public void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException,
146 InvalidTypeException,
147 EvaluateException {
148 debuggerContext.getFrameProxy().setValue(local, preprocessValue(evaluationContext, newValue, local.getType()));
149 update(debuggerContext);
152 public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
153 ClassNotLoadedException,
154 IncompatibleThreadStateException,
155 InvalidTypeException,
156 EvaluateException {
157 return evaluationContext.getDebugProcess().loadClass(evaluationContext, className,
158 evaluationContext.getClassLoader());
163 else if (descriptor instanceof ArrayElementDescriptorImpl) {
164 final ArrayElementDescriptorImpl elementDescriptor = (ArrayElementDescriptorImpl)descriptor;
165 final ArrayReference array = elementDescriptor.getArray();
166 if (array != null) {
167 if (VirtualMachineProxyImpl.isCollected(array)) {
168 // will only be the case if debugger does not use ObjectReference.disableCollection() because of Patches.IBM_JDK_DISABLE_COLLECTION_BUG
169 Messages.showWarningDialog(tree, DebuggerBundle.message("evaluation.error.array.collected") + "\n"+ DebuggerBundle.message("warning.recalculate"), DebuggerBundle.message("title.set.value"));
170 node.getParent().calcValue();
171 return;
173 final ArrayType arrType = (ArrayType)array.referenceType();
174 askAndSet(node, debuggerContext, new SetValueRunnable() {
175 public void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException, InvalidTypeException, EvaluateException {
176 array.setValue(elementDescriptor.getIndex(), preprocessValue(evaluationContext, newValue, arrType.componentType()));
177 update(debuggerContext);
180 public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
181 ClassNotLoadedException,
182 IncompatibleThreadStateException,
183 InvalidTypeException,
184 EvaluateException {
185 return evaluationContext.getDebugProcess().loadClass(evaluationContext, className, arrType.classLoader());
190 else if (descriptor instanceof EvaluationDescriptor) {
191 final EvaluationDescriptor evaluationDescriptor = (EvaluationDescriptor)descriptor;
192 if (evaluationDescriptor.canSetValue()) {
193 askAndSet(node, debuggerContext, new SetValueRunnable() {
194 public void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException, InvalidTypeException, EvaluateException {
195 final Modifier modifier = evaluationDescriptor.getModifier();
196 modifier.setValue(preprocessValue(evaluationContext, newValue, modifier.getExpectedType()));
197 update(debuggerContext);
200 public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
201 ClassNotLoadedException,
202 IncompatibleThreadStateException,
203 InvalidTypeException,
204 EvaluateException {
205 return evaluationContext.getDebugProcess().loadClass(evaluationContext, className,
206 evaluationContext.getClassLoader());
213 private Value preprocessValue(EvaluationContextImpl context, Value value, Type varType) throws EvaluateException {
214 if (value != null && "java.lang.String".equals(varType.name()) && !(value instanceof StringReference)) {
215 String v = DebuggerUtilsEx.getValueAsString(context, value);
216 if (v != null) {
217 value = context.getSuspendContext().getDebugProcess().getVirtualMachineProxy().mirrorOf(v);
220 if(value instanceof DoubleValue) {
221 double dValue = ((DoubleValue) value).doubleValue();
222 if(varType instanceof FloatType && Float.MIN_VALUE <= dValue && dValue <= Float.MAX_VALUE){
223 value = context.getSuspendContext().getDebugProcess().getVirtualMachineProxy().mirrorOf((float)dValue);
226 if (value != null) {
227 if (varType instanceof PrimitiveType) {
228 if (!(value instanceof PrimitiveValue)) {
229 value = (Value)new UnBoxingEvaluator(new IdentityEvaluator(value)).evaluate(context);
232 else if (UnBoxingEvaluator.isTypeUnboxable(varType.name())) {
233 // variable is not primitive and boxing/unboxing is applicable
234 if (value instanceof PrimitiveValue) {
235 value = (Value)new BoxingEvaluator(new IdentityEvaluator(value)).evaluate(context);
239 return value;
242 private static interface SetValueRunnable {
243 void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException,
244 InvalidTypeException,
245 EvaluateException,
246 IncompatibleThreadStateException;
247 ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws EvaluateException,
248 InvocationException,
249 ClassNotLoadedException,
250 IncompatibleThreadStateException,
251 InvalidTypeException;
254 private static void setValue(String expressionToShow, ExpressionEvaluator evaluator, EvaluationContextImpl evaluationContext, SetValueRunnable setValueRunnable) throws EvaluateException {
255 Value value;
256 try {
257 value = evaluator.evaluate(evaluationContext);
259 setValueRunnable.setValue(evaluationContext, value);
261 catch (IllegalArgumentException ex) {
262 throw EvaluateExceptionUtil.createEvaluateException(ex.getMessage());
264 catch (InvalidTypeException ex) {
265 throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.type.mismatch"));
267 catch (IncompatibleThreadStateException e) {
268 throw EvaluateExceptionUtil.createEvaluateException(e);
270 catch (ClassNotLoadedException ex) {
271 if (!evaluationContext.isAutoLoadClasses()) {
272 throw EvaluateExceptionUtil.createEvaluateException(ex);
274 final ReferenceType refType;
275 try {
276 refType = setValueRunnable.loadClass(evaluationContext, ex.className());
277 if (refType != null) {
278 //try again
279 setValue(expressionToShow, evaluator, evaluationContext, setValueRunnable);
282 catch (InvocationException e) {
283 throw EvaluateExceptionUtil.createEvaluateException(e);
285 catch (ClassNotLoadedException e) {
286 throw EvaluateExceptionUtil.createEvaluateException(e);
288 catch (IncompatibleThreadStateException e) {
289 throw EvaluateExceptionUtil.createEvaluateException(e);
291 catch (InvalidTypeException e) {
292 throw EvaluateExceptionUtil.createEvaluateException(e);
294 catch (ObjectCollectedException e) {
295 throw EvaluateExceptionUtil.OBJECT_WAS_COLLECTED;
300 private void askAndSet(final DebuggerTreeNodeImpl node, final DebuggerContextImpl debuggerContext, final SetValueRunnable setValueRunnable) {
301 ProgressWindowWithNotification progressWindow = new ProgressWindowWithNotification(true, debuggerContext.getProject());
303 SuspendContextCommandImpl askSetAction = new DebuggerContextCommandImpl(debuggerContext) {
304 public Priority getPriority() {
305 return Priority.HIGH;
308 public void threadAction() {
309 final NodeDescriptorImpl descriptor = node.getDescriptor();
310 String initialString = "";
311 if (descriptor instanceof ValueDescriptorImpl) {
312 Value currentValue = ((ValueDescriptorImpl) descriptor).getValue();
313 if (currentValue instanceof StringReference) {
314 initialString = DebuggerUtilsEx.getValueOrErrorAsString(debuggerContext.createEvaluationContext(), currentValue);
315 initialString = initialString == null ? "" : "\"" + DebuggerUtilsEx.translateStringValue(initialString) + "\"";
317 else if (currentValue instanceof PrimitiveValue) {
318 ValueLabelRenderer renderer = ((ValueDescriptorImpl) descriptor).getRenderer(debuggerContext.getDebugProcess());
319 initialString = getDisplayableString((PrimitiveValue) currentValue, renderer instanceof NodeRenderer && HexRenderer.UNIQUE_ID.equals(renderer.getUniqueId()));
322 final String initialString1 = initialString;
323 final Project project = debuggerContext.getProject();
324 DebuggerInvocationUtil.swingInvokeLater(project, new Runnable() {
325 public void run() {
326 showEditor(new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, initialString1), node, debuggerContext, setValueRunnable);
333 progressWindow.setTitle(DebuggerBundle.message("title.evaluating"));
334 debuggerContext.getDebugProcess().getManagerThread().startProgress(askSetAction, progressWindow);
337 private void showEditor(final TextWithImports initialString,
338 final DebuggerTreeNodeImpl node,
339 final DebuggerContextImpl debuggerContext,
340 final SetValueRunnable setValueRunnable) {
341 final JPanel editorPanel = new JPanel();
342 editorPanel.setLayout(new BoxLayout(editorPanel, BoxLayout.X_AXIS));
343 SimpleColoredComponent label = new SimpleColoredComponent();
344 label.setIcon(node.getIcon());
345 DebuggerTreeRenderer.getDescriptorTitle(debuggerContext, node.getDescriptor()).appendToComponent(label);
346 editorPanel.add(label);
348 final DebuggerExpressionComboBox comboBox = new DebuggerExpressionComboBox(
349 debuggerContext.getProject(),
350 PositionUtil.getContextElement(debuggerContext),
351 "setValue", DefaultCodeFragmentFactory.getInstance());
352 comboBox.setText(initialString);
353 comboBox.selectAll();
354 editorPanel.add(comboBox);
356 final DebuggerTreeInplaceEditor editor = new DebuggerTreeInplaceEditor(node) {
357 public JComponent createInplaceEditorComponent() {
358 return editorPanel;
361 public JComponent getPreferredFocusedComponent() {
362 return comboBox;
365 public Editor getEditor() {
366 return comboBox.getEditor();
369 public JComponent getEditorComponent() {
370 return comboBox.getEditorComponent();
373 private void flushValue() {
374 Editor editor = getEditor();
375 if(editor == null) {
376 return;
379 final TextWithImports text = comboBox.getText();
381 PsiFile psiFile = PsiDocumentManager.getInstance(debuggerContext.getProject()).getPsiFile(editor.getDocument());
383 final ProgressWindowWithNotification progressWindow = new ProgressWindowWithNotification(true, getProject());
384 EditorEvaluationCommand evaluationCommand = new EditorEvaluationCommand(getEditor(), psiFile, debuggerContext, progressWindow) {
385 public void threadAction() {
386 try {
387 evaluate();
389 catch(EvaluateException e) {
390 progressWindow.cancel();
392 catch(ProcessCanceledException e) {
393 progressWindow.cancel();
395 finally{
396 if (!progressWindow.isCanceled()) {
397 DebuggerInvocationUtil.swingInvokeLater(debuggerContext.getProject(), new Runnable() {
398 public void run() {
399 comboBox.addRecent(text);
400 cancelEditing();
407 protected Object evaluate(final EvaluationContextImpl evaluationContext) throws EvaluateException {
408 ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(evaluationContext.getProject(), new com.intellij.debugger.EvaluatingComputable<ExpressionEvaluator>() {
409 public ExpressionEvaluator compute() throws EvaluateException {
410 return EvaluatorBuilderImpl.getInstance().build(text, ContextUtil.getContextElement(evaluationContext), ContextUtil.getSourcePosition(evaluationContext));
414 SetValueAction.setValue(text.getText(), evaluator, evaluationContext, new SetValueRunnable() {
415 public void setValue(EvaluationContextImpl evaluationContext, Value newValue) throws ClassNotLoadedException,
416 InvalidTypeException,
417 EvaluateException,
418 IncompatibleThreadStateException {
419 if(!progressWindow.isCanceled()) {
420 setValueRunnable.setValue(evaluationContext, newValue);
421 node.calcValue();
425 public ReferenceType loadClass(EvaluationContextImpl evaluationContext, String className) throws InvocationException,
426 ClassNotLoadedException,
427 EvaluateException,
428 IncompatibleThreadStateException,
429 InvalidTypeException {
430 return setValueRunnable.loadClass(evaluationContext, className);
434 return null;
438 progressWindow.addListener(new ProgressIndicatorListenerAdapter() {
439 //should return whether to stop processing
440 public void stopped() {
441 if(!progressWindow.isCanceled()) {
442 IJSwingUtilities.invoke(new Runnable() {
443 public void run() {
444 cancelEditing();
453 progressWindow.setTitle(DebuggerBundle.message("progress.set.value"));
454 debuggerContext.getDebugProcess().getManagerThread().startProgress(evaluationCommand, progressWindow);
457 public void cancelEditing() {
458 try {
459 super.cancelEditing();
461 finally {
462 comboBox.dispose();
466 public void doOKAction() {
467 try {
468 flushValue();
470 finally {
471 comboBox.dispose();
477 final DebuggerStateManager stateManager = DebuggerManagerEx.getInstanceEx(debuggerContext.getProject()).getContextManager();
479 stateManager.addListener(new DebuggerContextListener() {
480 public void changeEvent(DebuggerContextImpl newContext, int event) {
481 stateManager.removeListener(this);
482 editor.cancelEditing();
486 node.getTree().hideTooltip();
488 editor.show();
491 @SuppressWarnings({"HardCodedStringLiteral"})
492 private static String getDisplayableString(PrimitiveValue value, boolean showAsHex) {
493 if (value instanceof CharValue) {
494 long longValue = value.longValue();
495 return showAsHex ? "0x" + Long.toHexString(longValue).toUpperCase() : Long.toString(longValue);
497 if (value instanceof ByteValue) {
498 byte val = value.byteValue();
499 String strValue = Integer.toHexString(val).toUpperCase();
500 if (strValue.length() > 2) {
501 strValue = strValue.substring(strValue.length() - 2);
503 return showAsHex ? "0x" + strValue : value.toString();
505 if (value instanceof ShortValue) {
506 short val = value.shortValue();
507 String strValue = Integer.toHexString(val).toUpperCase();
508 if (strValue.length() > 4) {
509 strValue = strValue.substring(strValue.length() - 4);
511 return showAsHex ? "0x" + strValue : value.toString();
513 if (value instanceof IntegerValue) {
514 int val = value.intValue();
515 return showAsHex ? "0x" + Integer.toHexString(val).toUpperCase() : value.toString();
517 if (value instanceof LongValue) {
518 long val = value.longValue();
519 return showAsHex ? "0x" + Long.toHexString(val).toUpperCase() + "L" : value.toString() + "L";
521 return DebuggerUtilsEx.translateStringValue(value.toString());