5523463d8359b0251f99865cc535f3d2c7144b49
[idea/community.git] / java / debugger / impl / src / com / intellij / debugger / engine / evaluation / expression / EvaluatorBuilderImpl.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /*
18  * Class EvaluatorBuilderImpl
19  * @author Jeka
20  */
21 package com.intellij.debugger.engine.evaluation.expression;
22
23 import com.intellij.codeInsight.daemon.JavaErrorMessages;
24 import com.intellij.codeInsight.daemon.impl.HighlightInfo;
25 import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
26 import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
27 import com.intellij.debugger.DebuggerBundle;
28 import com.intellij.debugger.SourcePosition;
29 import com.intellij.debugger.engine.ContextUtil;
30 import com.intellij.debugger.engine.DebuggerUtils;
31 import com.intellij.debugger.engine.JVMName;
32 import com.intellij.debugger.engine.JVMNameUtil;
33 import com.intellij.debugger.engine.evaluation.*;
34 import com.intellij.debugger.impl.DebuggerUtilsEx;
35 import com.intellij.openapi.diagnostic.Logger;
36 import com.intellij.openapi.project.Project;
37 import com.intellij.psi.*;
38 import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
39 import com.intellij.psi.search.GlobalSearchScope;
40 import com.intellij.psi.tree.IElementType;
41 import com.intellij.psi.util.PsiTreeUtil;
42 import com.intellij.psi.util.PsiTypesUtil;
43 import com.intellij.psi.util.PsiUtil;
44 import com.intellij.psi.util.TypeConversionUtil;
45 import com.intellij.util.IncorrectOperationException;
46 import com.sun.jdi.Value;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
49
50 import java.util.*;
51
52 public class EvaluatorBuilderImpl implements EvaluatorBuilder {
53   private static final EvaluatorBuilderImpl ourInstance = new EvaluatorBuilderImpl();
54
55   private EvaluatorBuilderImpl() {
56   }
57
58   public static EvaluatorBuilder getInstance() {
59     return ourInstance;
60   }
61
62   public static ExpressionEvaluator build(final TextWithImports text, @Nullable PsiElement contextElement, final SourcePosition position) throws EvaluateException {
63     if (contextElement == null) {
64       throw EvaluateExceptionUtil.CANNOT_FIND_SOURCE_CLASS;
65     }
66
67     final Project project = contextElement.getProject();
68
69     CodeFragmentFactory factory = DebuggerUtilsEx.findAppropriateCodeFragmentFactory(text, contextElement);
70     PsiCodeFragment codeFragment = factory.createCodeFragment(text, contextElement, project);
71     if (codeFragment == null) {
72       throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", text.getText()));
73     }
74     codeFragment.forceResolveScope(GlobalSearchScope.allScope(project));
75     DebuggerUtils.checkSyntax(codeFragment);
76
77     return factory.getEvaluatorBuilder().build(codeFragment, position);
78   }
79
80   @Override
81   public ExpressionEvaluator build(final PsiElement codeFragment, final SourcePosition position) throws EvaluateException {
82     return new Builder(position).buildElement(codeFragment);
83   }
84
85   private static class Builder extends JavaElementVisitor {
86     private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl");
87     private Evaluator myResult = null;
88     private PsiClass myContextPsiClass;
89     private CodeFragmentEvaluator myCurrentFragmentEvaluator;
90     private final Set<JavaCodeFragment> myVisitedFragments = new HashSet<JavaCodeFragment>();
91     @Nullable
92     private final SourcePosition myPosition;
93
94     private Builder(@Nullable SourcePosition position) {
95       myPosition = position;
96     }
97
98     @Override
99     public void visitCodeFragment(JavaCodeFragment codeFragment) {
100       myVisitedFragments.add(codeFragment);
101       ArrayList<Evaluator> evaluators = new ArrayList<Evaluator>();
102
103       CodeFragmentEvaluator oldFragmentEvaluator = myCurrentFragmentEvaluator;
104       myCurrentFragmentEvaluator = new CodeFragmentEvaluator(oldFragmentEvaluator);
105
106       for (PsiElement child = codeFragment.getFirstChild(); child != null; child = child.getNextSibling()) {
107         child.accept(this);
108         if(myResult != null) {
109           evaluators.add(myResult);
110         }
111         myResult = null;
112       }
113
114       myCurrentFragmentEvaluator.setStatements(evaluators.toArray(new Evaluator[evaluators.size()]));
115       myResult = myCurrentFragmentEvaluator;
116
117       myCurrentFragmentEvaluator = oldFragmentEvaluator;
118     }
119
120     @Override
121     public void visitErrorElement(PsiErrorElement element) {
122       throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", element.getText()));
123     }
124
125     @Override
126     public void visitAssignmentExpression(PsiAssignmentExpression expression) {
127       final PsiExpression rExpression = expression.getRExpression();
128       if(rExpression == null) {
129         throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
130       }
131
132       rExpression.accept(this);
133       Evaluator rEvaluator = myResult;
134
135       final PsiExpression lExpression = expression.getLExpression();
136       final PsiType lType = lExpression.getType();
137       if(lType == null) {
138         throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", lExpression.getText()));
139       }
140
141       IElementType assignmentType = expression.getOperationTokenType();
142       PsiType rType = rExpression.getType();
143       if(!TypeConversionUtil.areTypesAssignmentCompatible(lType, rExpression) && rType != null) {
144         throwEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", expression.getOperationSign().getText()));
145       }
146       lExpression.accept(this);
147       Evaluator lEvaluator = myResult;
148
149       rEvaluator = handleAssignmentBoxingAndPrimitiveTypeConversions(lType, rType, rEvaluator);
150
151       if (assignmentType != JavaTokenType.EQ) {
152         IElementType opType = TypeConversionUtil.convertEQtoOperation(assignmentType);
153         final PsiType typeForBinOp = TypeConversionUtil.calcTypeForBinaryExpression(lType, rType, opType, true);
154         if (typeForBinOp == null || rType == null) {
155           throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText()));
156         }
157         rEvaluator = createBinaryEvaluator(lEvaluator, lType, rEvaluator, rType, opType, typeForBinOp);
158       }
159       myResult = new AssignmentEvaluator(lEvaluator, rEvaluator);
160     }
161
162     // returns rEvaluator possibly wrapped with boxing/unboxing and casting evaluators
163     private static Evaluator handleAssignmentBoxingAndPrimitiveTypeConversions(PsiType lType, PsiType rType, Evaluator rEvaluator) {
164       final PsiType unboxedLType = PsiPrimitiveType.getUnboxedType(lType);
165
166       if (unboxedLType != null) {
167         if (rType instanceof PsiPrimitiveType && !PsiType.NULL.equals(rType)) {
168           if (!rType.equals(unboxedLType)) {
169             rEvaluator = new TypeCastEvaluator(rEvaluator, unboxedLType.getCanonicalText(), true);
170           }
171           rEvaluator = new BoxingEvaluator(rEvaluator);
172         }
173       }
174       else {
175         // either primitive type or not unboxable type
176         if (lType instanceof PsiPrimitiveType) {
177           if (rType instanceof PsiClassType) {
178             rEvaluator = new UnBoxingEvaluator(rEvaluator);
179           }
180           final PsiPrimitiveType unboxedRType = PsiPrimitiveType.getUnboxedType(rType);
181           final PsiType _rType = unboxedRType != null? unboxedRType : rType;
182           if (_rType instanceof PsiPrimitiveType && !PsiType.NULL.equals(_rType)) {
183             if (!lType.equals(_rType)) {
184               rEvaluator = new TypeCastEvaluator(rEvaluator, lType.getCanonicalText(), true);
185             }
186           }
187         }
188       }
189       return rEvaluator;
190     }
191
192     @Override
193     public void visitTryStatement(PsiTryStatement statement) {
194       throw new EvaluateRuntimeException(new UnsupportedExpressionException(statement.getText()));
195     }
196
197     @Override
198     public void visitStatement(PsiStatement statement) {
199       throwEvaluateException(DebuggerBundle.message("evaluation.error.statement.not.supported", statement.getText()));
200     }
201
202     @Override
203     public void visitBlockStatement(PsiBlockStatement statement) {
204       PsiStatement[] statements = statement.getCodeBlock().getStatements();
205       Evaluator [] evaluators = new Evaluator[statements.length];
206       for (int i = 0; i < statements.length; i++) {
207         PsiStatement psiStatement = statements[i];
208         psiStatement.accept(this);
209         evaluators[i] = new DisableGC(myResult);
210         myResult = null;
211       }
212       myResult = new BlockStatementEvaluator(evaluators);
213     }
214
215     @Override
216     public void visitLabeledStatement(PsiLabeledStatement labeledStatement) {
217       PsiStatement statement = labeledStatement.getStatement();
218       if (statement != null) {
219         statement.accept(this);
220       }
221     }
222
223     private static String getLabel(PsiElement element) {
224       String label = null;
225       if(element.getParent() instanceof PsiLabeledStatement) {
226         label = ((PsiLabeledStatement)element.getParent()).getName();
227       }
228       return label;
229     }
230
231     @Override
232     public void visitDoWhileStatement(PsiDoWhileStatement statement) {
233       Evaluator bodyEvaluator = accept(statement.getBody());
234       Evaluator conditionEvaluator = accept(statement.getCondition());
235       if (conditionEvaluator != null) {
236         myResult = new DoWhileStatementEvaluator(new UnBoxingEvaluator(conditionEvaluator), bodyEvaluator, getLabel(statement));
237       }
238     }
239
240     @Override
241     public void visitWhileStatement(PsiWhileStatement statement) {
242       Evaluator bodyEvaluator = accept(statement.getBody());
243       Evaluator conditionEvaluator = accept(statement.getCondition());
244       if (conditionEvaluator != null) {
245         myResult = new WhileStatementEvaluator(new UnBoxingEvaluator(conditionEvaluator), bodyEvaluator, getLabel(statement));
246       }
247     }
248
249     @Override
250     public void visitForStatement(PsiForStatement statement) {
251       Evaluator initializerEvaluator = accept(statement.getInitialization());
252       Evaluator conditionEvaluator = accept(statement.getCondition());
253       if (conditionEvaluator != null) {
254         conditionEvaluator = new UnBoxingEvaluator(conditionEvaluator);
255       }
256       Evaluator updateEvaluator = accept(statement.getUpdate());
257       Evaluator bodyEvaluator = accept(statement.getBody());
258       if (bodyEvaluator != null) {
259         myResult = new ForStatementEvaluator(initializerEvaluator, conditionEvaluator, updateEvaluator, bodyEvaluator, getLabel(statement));
260       }
261     }
262
263     @Override
264     public void visitForeachStatement(PsiForeachStatement statement) {
265       try {
266         String iterationParameterName = statement.getIterationParameter().getName();
267         myCurrentFragmentEvaluator.setInitialValue(iterationParameterName, null);
268         SyntheticVariableEvaluator iterationParameterEvaluator = new SyntheticVariableEvaluator(myCurrentFragmentEvaluator, iterationParameterName);
269
270         Evaluator iteratedValueEvaluator = accept(statement.getIteratedValue());
271         Evaluator bodyEvaluator = accept(statement.getBody());
272         if (bodyEvaluator != null) {
273           myResult = new ForeachStatementEvaluator(iterationParameterEvaluator, iteratedValueEvaluator, bodyEvaluator, getLabel(statement));
274         }
275       }
276       catch (EvaluateException e) {
277         throw new EvaluateRuntimeException(e);
278       }
279     }
280
281     @Nullable
282     private Evaluator accept(@Nullable PsiElement element) {
283       if (element == null || element instanceof PsiEmptyStatement) {
284         return null;
285       }
286       element.accept(this);
287       return myResult;
288     }
289
290     @Override
291     public void visitIfStatement(PsiIfStatement statement) {
292       PsiStatement thenBranch = statement.getThenBranch();
293       if(thenBranch == null) return;
294       thenBranch.accept(this);
295       Evaluator thenEvaluator = myResult;
296
297       PsiStatement elseBranch = statement.getElseBranch();
298       Evaluator elseEvaluator = null;
299       if(elseBranch != null){
300         elseBranch.accept(this);
301         elseEvaluator = myResult;
302       }
303
304       PsiExpression condition = statement.getCondition();
305       if(condition == null) return;
306       condition.accept(this);
307
308       myResult = new IfStatementEvaluator(new UnBoxingEvaluator(myResult), thenEvaluator, elseEvaluator);
309     }
310
311     @Override
312     public void visitBreakStatement(PsiBreakStatement statement) {
313       PsiIdentifier labelIdentifier = statement.getLabelIdentifier();
314       myResult = BreakContinueStatementEvaluator.createBreakEvaluator(labelIdentifier != null ? labelIdentifier.getText() : null);
315     }
316
317     @Override
318     public void visitContinueStatement(PsiContinueStatement statement) {
319       PsiIdentifier labelIdentifier = statement.getLabelIdentifier();
320       myResult = BreakContinueStatementEvaluator.createContinueEvaluator(labelIdentifier != null ? labelIdentifier.getText() : null);
321     }
322
323     @Override
324     public void visitExpressionStatement(PsiExpressionStatement statement) {
325       statement.getExpression().accept(this);
326     }
327
328     @Override
329     public void visitExpression(PsiExpression expression) {
330       if (LOG.isDebugEnabled()) {
331         LOG.debug("visitExpression " + expression);
332       }
333     }
334
335     @Override
336     public void visitPolyadicExpression(PsiPolyadicExpression wideExpression) {
337       if (LOG.isDebugEnabled()) {
338         LOG.debug("visitPolyadicExpression " + wideExpression);
339       }
340       PsiExpression[] operands = wideExpression.getOperands();
341       operands[0].accept(this);
342       Evaluator result = myResult;
343       PsiType lType = operands[0].getType();
344       for (int i = 1; i < operands.length; i++) {
345         PsiExpression expression = operands[i];
346         if (expression == null) {
347           throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", wideExpression.getText()));
348           return;
349         }
350         expression.accept(this);
351         Evaluator rResult = myResult;
352         IElementType opType = wideExpression.getOperationTokenType();
353         PsiType rType = expression.getType();
354         if (rType == null) {
355           throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText()));
356         }
357         final PsiType typeForBinOp = TypeConversionUtil.calcTypeForBinaryExpression(lType, rType, opType, true);
358         if (typeForBinOp == null) {
359           throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", wideExpression.getText()));
360         }
361         myResult = createBinaryEvaluator(result, lType, rResult, rType, opType, typeForBinOp);
362         lType = typeForBinOp;
363         result = myResult;
364       }
365     }
366
367     // constructs binary evaluator handling unboxing and numeric promotion issues
368     private static BinaryExpressionEvaluator createBinaryEvaluator(Evaluator lResult,
369                                                                    PsiType lType,
370                                                                    Evaluator rResult,
371                                                                    @NotNull PsiType rType,
372                                                                    @NotNull IElementType operation,
373                                                                    @NotNull PsiType expressionExpectedType) {
374       // handle unboxing if necessary
375       if (isUnboxingInBinaryExpressionApplicable(lType, rType, operation)) {
376         if (rType instanceof PsiClassType && UnBoxingEvaluator.isTypeUnboxable(rType.getCanonicalText())) {
377           rResult = new UnBoxingEvaluator(rResult);
378         }
379         if (lType instanceof PsiClassType && UnBoxingEvaluator.isTypeUnboxable(lType.getCanonicalText())) {
380           lResult = new UnBoxingEvaluator(lResult);
381         }
382       }
383       if (isBinaryNumericPromotionApplicable(lType, rType, operation)) {
384         PsiType _lType = lType;
385         final PsiPrimitiveType unboxedLType = PsiPrimitiveType.getUnboxedType(lType);
386         if (unboxedLType != null) {
387           _lType = unboxedLType;
388         }
389
390         PsiType _rType = rType;
391         final PsiPrimitiveType unboxedRType = PsiPrimitiveType.getUnboxedType(rType);
392         if (unboxedRType != null) {
393           _rType = unboxedRType;
394         }
395
396         // handle numeric promotion
397         if (PsiType.DOUBLE.equals(_lType)) {
398           if (TypeConversionUtil.areTypesConvertible(_rType, PsiType.DOUBLE)) {
399             rResult = new TypeCastEvaluator(rResult, PsiType.DOUBLE.getCanonicalText(), true);
400           }
401         }
402         else if (PsiType.DOUBLE.equals(_rType)) {
403           if (TypeConversionUtil.areTypesConvertible(_lType, PsiType.DOUBLE)) {
404             lResult = new TypeCastEvaluator(lResult, PsiType.DOUBLE.getCanonicalText(), true);
405           }
406         }
407         else if (PsiType.FLOAT.equals(_lType)) {
408           if (TypeConversionUtil.areTypesConvertible(_rType, PsiType.FLOAT)) {
409             rResult = new TypeCastEvaluator(rResult, PsiType.FLOAT.getCanonicalText(), true);
410           }
411         }
412         else if (PsiType.FLOAT.equals(_rType)) {
413           if (TypeConversionUtil.areTypesConvertible(_lType, PsiType.FLOAT)) {
414             lResult = new TypeCastEvaluator(lResult, PsiType.FLOAT.getCanonicalText(), true);
415           }
416         }
417         else if (PsiType.LONG.equals(_lType)) {
418           if (TypeConversionUtil.areTypesConvertible(_rType, PsiType.LONG)) {
419             rResult = new TypeCastEvaluator(rResult, PsiType.LONG.getCanonicalText(), true);
420           }
421         }
422         else if (PsiType.LONG.equals(_rType)) {
423           if (TypeConversionUtil.areTypesConvertible(_lType, PsiType.LONG)) {
424             lResult = new TypeCastEvaluator(lResult, PsiType.LONG.getCanonicalText(), true);
425           }
426         }
427         else {
428           if (!PsiType.INT.equals(_lType) && TypeConversionUtil.areTypesConvertible(_lType, PsiType.INT)) {
429             lResult = new TypeCastEvaluator(lResult, PsiType.INT.getCanonicalText(), true);
430           }
431           if (!PsiType.INT.equals(_rType) && TypeConversionUtil.areTypesConvertible(_rType, PsiType.INT)) {
432             rResult = new TypeCastEvaluator(rResult, PsiType.INT.getCanonicalText(), true);
433           }
434         }
435       }
436
437       return new BinaryExpressionEvaluator(lResult, rResult, operation, expressionExpectedType.getCanonicalText());
438     }
439
440     private static boolean isBinaryNumericPromotionApplicable(PsiType lType, PsiType rType, IElementType opType) {
441       if (lType == null || rType == null) {
442         return false;
443       }
444       if (!TypeConversionUtil.isNumericType(lType) || !TypeConversionUtil.isNumericType(rType)) {
445         return false;
446       }
447       if (opType == JavaTokenType.EQEQ || opType == JavaTokenType.NE) {
448         if (PsiType.NULL.equals(lType) || PsiType.NULL.equals(rType)) {
449           return false;
450         }
451         if (lType instanceof PsiClassType && rType instanceof PsiClassType) {
452           return false;
453         }
454         if (lType instanceof PsiClassType) {
455           return PsiPrimitiveType.getUnboxedType(lType) != null; // should be unboxable
456         }
457         if (rType instanceof PsiClassType) {
458           return PsiPrimitiveType.getUnboxedType(rType) != null; // should be unboxable
459         }
460         return true;
461       }
462
463       return opType == JavaTokenType.ASTERISK ||
464           opType == JavaTokenType.DIV         ||
465           opType == JavaTokenType.PERC        ||
466           opType == JavaTokenType.PLUS        ||
467           opType == JavaTokenType.MINUS       ||
468           opType == JavaTokenType.LT          ||
469           opType == JavaTokenType.LE          ||
470           opType == JavaTokenType.GT          ||
471           opType == JavaTokenType.GE          ||
472           opType == JavaTokenType.AND         ||
473           opType == JavaTokenType.XOR         ||
474           opType == JavaTokenType.OR;
475
476     }
477
478     private static boolean isUnboxingInBinaryExpressionApplicable(PsiType lType, PsiType rType, IElementType opCode) {
479       if (PsiType.NULL.equals(lType) || PsiType.NULL.equals(rType)) {
480         return false;
481       }
482       // handle '==' and '!=' separately
483       if (opCode == JavaTokenType.EQEQ || opCode == JavaTokenType.NE) {
484         return lType instanceof PsiPrimitiveType && rType instanceof PsiClassType ||
485                lType instanceof PsiClassType     && rType instanceof PsiPrimitiveType;
486       }
487       // all other operations at least one should be of class type
488       return lType instanceof PsiClassType || rType instanceof PsiClassType;
489     }
490
491     /**
492      * @param type
493      * @return promotion type to cast to or null if no casting needed
494      */
495     @Nullable
496     private static PsiType calcUnaryNumericPromotionType(PsiPrimitiveType type) {
497       if (PsiType.BYTE.equals(type) || PsiType.SHORT.equals(type) || PsiType.CHAR.equals(type) || PsiType.INT.equals(type)) {
498         return PsiType.INT;
499       }
500       return null;
501     }
502
503     @Override
504     public void visitDeclarationStatement(PsiDeclarationStatement statement) {
505       List<Evaluator> evaluators = new ArrayList<Evaluator>();
506
507       PsiElement[] declaredElements = statement.getDeclaredElements();
508       for (PsiElement declaredElement : declaredElements) {
509         if (declaredElement instanceof PsiLocalVariable) {
510           if (myCurrentFragmentEvaluator != null) {
511             final PsiLocalVariable localVariable = (PsiLocalVariable)declaredElement;
512
513             final PsiType lType = localVariable.getType();
514
515             PsiElementFactory elementFactory = JavaPsiFacade.getInstance(localVariable.getProject()).getElementFactory();
516             try {
517               PsiExpression initialValue = elementFactory.createExpressionFromText(PsiTypesUtil.getDefaultValueOfType(lType), null);
518               Object value = JavaConstantExpressionEvaluator.computeConstantExpression(initialValue, true);
519               myCurrentFragmentEvaluator.setInitialValue(localVariable.getName(), value);
520             }
521             catch (IncorrectOperationException e) {
522               LOG.error(e);
523             }
524             catch (EvaluateException e) {
525               throw new EvaluateRuntimeException(e);
526             }
527
528             PsiExpression initializer = localVariable.getInitializer();
529             if (initializer != null) {
530               try {
531                 if (!TypeConversionUtil.areTypesAssignmentCompatible(lType, initializer)) {
532                   throwEvaluateException(
533                     DebuggerBundle.message("evaluation.error.incompatible.variable.initializer.type", localVariable.getName()));
534                 }
535                 final PsiType rType = initializer.getType();
536                 initializer.accept(this);
537                 Evaluator rEvaluator = myResult;
538
539                 PsiExpression localVarReference = elementFactory.createExpressionFromText(localVariable.getName(), initializer);
540
541                 localVarReference.accept(this);
542                 Evaluator lEvaluator = myResult;
543                 rEvaluator = handleAssignmentBoxingAndPrimitiveTypeConversions(localVarReference.getType(), rType, rEvaluator);
544
545                 Evaluator assignment = new AssignmentEvaluator(lEvaluator, rEvaluator);
546                 evaluators.add(assignment);
547               }
548               catch (IncorrectOperationException e) {
549                 LOG.error(e);
550               }
551             }
552           }
553           else {
554             throw new EvaluateRuntimeException(new EvaluateException(
555               DebuggerBundle.message("evaluation.error.local.variable.declarations.not.supported"), null));
556           }
557         }
558         else {
559           throw new EvaluateRuntimeException(new EvaluateException(
560             DebuggerBundle.message("evaluation.error.unsupported.declaration", declaredElement.getText()), null));
561         }
562       }
563
564       if(!evaluators.isEmpty()) {
565         CodeFragmentEvaluator codeFragmentEvaluator = new CodeFragmentEvaluator(myCurrentFragmentEvaluator);
566         codeFragmentEvaluator.setStatements(evaluators.toArray(new Evaluator[evaluators.size()]));
567         myResult = codeFragmentEvaluator;
568       } else {
569         myResult = null;
570       }
571     }
572
573     @Override
574     public void visitConditionalExpression(PsiConditionalExpression expression) {
575       if (LOG.isDebugEnabled()) {
576         LOG.debug("visitConditionalExpression " + expression);
577       }
578       final PsiExpression thenExpression = expression.getThenExpression();
579       final PsiExpression elseExpression = expression.getElseExpression();
580       if (thenExpression == null || elseExpression == null){
581         throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
582       }
583       PsiExpression condition = expression.getCondition();
584       condition.accept(this);
585       if (myResult == null) {
586         throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", condition.getText())); return;
587       }
588       Evaluator conditionEvaluator = new UnBoxingEvaluator(myResult);
589       thenExpression.accept(this);
590       if (myResult == null) {
591         throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", thenExpression.getText())); return;
592       }
593       Evaluator thenEvaluator = myResult;
594       elseExpression.accept(this);
595       if (myResult == null) {
596         throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", elseExpression.getText())); return;
597       }
598       Evaluator elseEvaluator = myResult;
599       myResult = new ConditionalExpressionEvaluator(conditionEvaluator, thenEvaluator, elseEvaluator);
600     }
601
602     @Override
603     public void visitReferenceExpression(PsiReferenceExpression expression) {
604       if (LOG.isDebugEnabled()) {
605         LOG.debug("visitReferenceExpression " + expression);
606       }
607       PsiExpression qualifier = expression.getQualifierExpression();
608       JavaResolveResult resolveResult = expression.advancedResolve(true);
609       PsiElement element = resolveResult.getElement();
610
611       if (element instanceof PsiLocalVariable || element instanceof PsiParameter) {
612         final Value labeledValue = element.getUserData(CodeFragmentFactoryContextWrapper.LABEL_VARIABLE_VALUE_KEY);
613         if (labeledValue != null) {
614           myResult = new IdentityEvaluator(labeledValue);
615           return;
616         }
617         //synthetic variable
618         final PsiFile containingFile = element.getContainingFile();
619         if(containingFile instanceof PsiCodeFragment && myCurrentFragmentEvaluator != null && myVisitedFragments.contains(containingFile)) {
620           // psiVariable may live in PsiCodeFragment not only in debugger editors, for example Fabrique has such variables.
621           // So treat it as synthetic var only when this code fragment is located in DebuggerEditor,
622           // that's why we need to check that containing code fragment is the one we visited
623           myResult = new SyntheticVariableEvaluator(myCurrentFragmentEvaluator, ((PsiVariable)element).getName());
624           return;
625         }
626         // local variable
627         final PsiVariable psiVar = (PsiVariable)element;
628         final String localName = psiVar.getName();
629         PsiClass variableClass = getContainingClass(psiVar);
630         if (getContextPsiClass() == null || getContextPsiClass().equals(variableClass)) {
631           final LocalVariableEvaluator localVarEvaluator = new LocalVariableEvaluator(localName, ContextUtil.isJspImplicit(element));
632           if (psiVar instanceof PsiParameter) {
633             final PsiParameter param = (PsiParameter)psiVar;
634             final PsiParameterList paramList = PsiTreeUtil.getParentOfType(param, PsiParameterList.class, true);
635             if (paramList != null) {
636               localVarEvaluator.setParameterIndex(paramList.getParameterIndex(param));
637             }
638           }
639           myResult = localVarEvaluator;
640           return;
641         }
642         // the expression references final var outside the context's class (in some of the outer classes)
643         int iterationCount = 0;
644         PsiClass aClass = getOuterClass(getContextPsiClass());
645         while (aClass != null && !aClass.equals(variableClass)) {
646           iterationCount++;
647           aClass = getOuterClass(aClass);
648         }
649         if (aClass != null) {
650           PsiExpression initializer = psiVar.getInitializer();
651           if(initializer != null) {
652             Object value = JavaPsiFacade.getInstance(psiVar.getProject()).getConstantEvaluationHelper().computeConstantExpression(initializer);
653             if(value != null) {
654               PsiType type = resolveResult.getSubstitutor().substitute(psiVar.getType());
655               myResult = new LiteralEvaluator(value, type.getCanonicalText());
656               return;
657             }
658           }
659           Evaluator objectEvaluator = new ThisEvaluator(iterationCount);
660           //noinspection HardCodedStringLiteral
661           final PsiClass classAt = myPosition != null? JVMNameUtil.getClassAt(myPosition) : null;
662           FieldEvaluator.TargetClassFilter filter = FieldEvaluator.createClassFilter(classAt != null? classAt : getContextPsiClass());
663           myResult = new FieldEvaluator(objectEvaluator, filter, "val$" + localName);
664           return;
665         }
666         throwEvaluateException(DebuggerBundle.message("evaluation.error.local.variable.missing.from.class.closure", localName));
667       }
668       else if (element instanceof PsiField) {
669         final PsiField psiField = (PsiField)element;
670         final PsiClass fieldClass = psiField.getContainingClass();
671         if(fieldClass == null) {
672           throwEvaluateException(DebuggerBundle.message("evaluation.error.cannot.resolve.field.class", psiField.getName())); return;
673         }
674         Evaluator objectEvaluator;
675         if (psiField.hasModifierProperty(PsiModifier.STATIC)) {
676           objectEvaluator = new TypeEvaluator(JVMNameUtil.getContextClassJVMQualifiedName(SourcePosition.createFromElement(psiField)));
677         }
678         else if(qualifier != null) {
679           qualifier.accept(this);
680           objectEvaluator = myResult;
681         }
682         else if (fieldClass.equals(getContextPsiClass()) || getContextPsiClass().isInheritor(fieldClass, true)) {
683             objectEvaluator = new ThisEvaluator();
684         }
685         else {  // myContextPsiClass != fieldClass && myContextPsiClass is not a subclass of fieldClass
686           int iterationCount = 0;
687           PsiClass aClass = getContextPsiClass();
688           while (aClass != null && !(aClass.equals(fieldClass) || aClass.isInheritor(fieldClass, true))) {
689             iterationCount++;
690             aClass = getOuterClass(aClass);
691           }
692           if (aClass == null) {
693             throwEvaluateException(DebuggerBundle.message("evaluation.error.cannot.sources.for.field.class", psiField.getName()));
694           }
695           objectEvaluator = new ThisEvaluator(iterationCount);
696         }
697         myResult = new FieldEvaluator(objectEvaluator, FieldEvaluator.createClassFilter(fieldClass), psiField.getName());
698       }
699       else {
700         //let's guess what this could be
701         PsiElement nameElement = expression.getReferenceNameElement(); // get "b" part
702         String name;
703         if (nameElement instanceof PsiIdentifier) {
704           name = nameElement.getText();
705         }
706         else {
707           //noinspection HardCodedStringLiteral
708           final String elementDisplayString = nameElement != null ? nameElement.getText() : "(null)";
709           throwEvaluateException(DebuggerBundle.message("evaluation.error.identifier.expected", elementDisplayString));
710           return;
711         }
712
713         if(qualifier != null) {
714           final PsiElement qualifierTarget = qualifier instanceof PsiReferenceExpression
715                                              ? ((PsiReferenceExpression)qualifier).resolve() : null;
716           if (qualifierTarget instanceof PsiClass) {
717             // this is a call to a 'static' field
718             PsiClass psiClass = (PsiClass)qualifierTarget;
719             final JVMName typeName = JVMNameUtil.getJVMQualifiedName(psiClass);
720             myResult = new FieldEvaluator(new TypeEvaluator(typeName), FieldEvaluator.createClassFilter(psiClass), name);
721           }
722           else {
723             qualifier.accept(this);
724             if (myResult == null) {
725               throwEvaluateException(DebuggerBundle.message("evaluation.error.cannot.evaluate.qualifier", qualifier.getText()));
726             }
727
728             myResult = new FieldEvaluator(myResult, FieldEvaluator.createClassFilter(qualifier.getType()), name);
729           }
730         }
731         else {
732           myResult = new LocalVariableEvaluator(name, false);
733         }
734       }
735     }
736
737     private static void throwEvaluateException(String message) throws EvaluateRuntimeException {
738       //noinspection ThrowableResultOfMethodCallIgnored
739       throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(message));
740     }
741
742     @Override
743     public void visitSuperExpression(PsiSuperExpression expression) {
744       if (LOG.isDebugEnabled()) {
745         LOG.debug("visitSuperExpression " + expression);
746       }
747       final int iterationCount = calcIterationCount(expression.getQualifier());
748       myResult = new SuperEvaluator(iterationCount);
749     }
750
751     @Override
752     public void visitThisExpression(PsiThisExpression expression) {
753       if (LOG.isDebugEnabled()) {
754         LOG.debug("visitThisExpression " + expression);
755       }
756       final int iterationCount = calcIterationCount(expression.getQualifier());
757       myResult = new ThisEvaluator(iterationCount);
758     }
759
760     private int calcIterationCount(final PsiJavaCodeReferenceElement qualifier) {
761       if (qualifier != null) {
762         return calcIterationCount(qualifier.resolve(), qualifier.getText());
763       }
764       return 0;
765     }
766
767     private int calcIterationCount(PsiElement targetClass, String name) {
768       int iterationCount = 0;
769       if (targetClass == null || getContextPsiClass() == null) {
770         throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", name));
771       }
772       try {
773         PsiClass aClass = getContextPsiClass();
774         while (aClass != null && !aClass.equals(targetClass)) {
775           iterationCount++;
776           aClass = getOuterClass(aClass);
777         }
778       }
779       catch (Exception e) {
780         //noinspection ThrowableResultOfMethodCallIgnored
781         throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(e));
782       }
783       return iterationCount;
784     }
785
786     @Override
787     public void visitInstanceOfExpression(PsiInstanceOfExpression expression) {
788       if (LOG.isDebugEnabled()) {
789         LOG.debug("visitInstanceOfExpression " + expression);
790       }
791       PsiTypeElement checkType = expression.getCheckType();
792       if(checkType == null) {
793         throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText()));
794         return;
795       }
796       PsiType type = checkType.getType();
797       expression.getOperand().accept(this);
798 //    ClassObjectEvaluator typeEvaluator = new ClassObjectEvaluator(type.getCanonicalText());
799       Evaluator operandEvaluator = myResult;
800       myResult = new InstanceofEvaluator(operandEvaluator, new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(type)));
801     }
802
803     @Override
804     public void visitParenthesizedExpression(PsiParenthesizedExpression expression) {
805       if (LOG.isDebugEnabled()) {
806         LOG.debug("visitParenthesizedExpression " + expression);
807       }
808       PsiExpression expr = expression.getExpression();
809       if (expr != null){
810         expr.accept(this);
811       }
812     }
813
814     @Override
815     public void visitPostfixExpression(PsiPostfixExpression expression) {
816       if(expression.getType() == null) {
817         throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText()));
818       }
819
820       final PsiExpression operandExpression = expression.getOperand();
821       operandExpression.accept(this);
822
823       final Evaluator operandEvaluator = myResult;
824
825       final IElementType operation = expression.getOperationTokenType();
826       final PsiType operandType = operandExpression.getType();
827       @Nullable final PsiType unboxedOperandType = PsiPrimitiveType.getUnboxedType(operandType);
828
829       Evaluator incrementImpl = createBinaryEvaluator(
830         operandEvaluator, operandType,
831         new LiteralEvaluator(Integer.valueOf(1), "int"), PsiType.INT,
832         operation == JavaTokenType.PLUSPLUS ? JavaTokenType.PLUS : JavaTokenType.MINUS,
833         unboxedOperandType!= null? unboxedOperandType : operandType
834       );
835       if (unboxedOperandType != null) {
836         incrementImpl = new BoxingEvaluator(incrementImpl);
837       }
838       myResult = new PostfixOperationEvaluator(operandEvaluator, incrementImpl);
839     }
840
841     @Override
842     public void visitPrefixExpression(final PsiPrefixExpression expression) {
843       final PsiType expressionType = expression.getType();
844       if(expressionType == null) {
845         throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText()));
846       }
847
848       final PsiExpression operandExpression = expression.getOperand();
849       if (operandExpression == null) {
850         throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.operand", expression.getText()));
851       }
852
853       operandExpression.accept(this);
854       Evaluator operandEvaluator = myResult;
855
856       // handle unboxing issues
857       final PsiType operandType = operandExpression.getType();
858       @Nullable
859       final PsiType unboxedOperandType = PsiPrimitiveType.getUnboxedType(operandType);
860
861       final IElementType operation = expression.getOperationTokenType();
862
863       if(operation == JavaTokenType.PLUSPLUS || operation == JavaTokenType.MINUSMINUS) {
864         try {
865           final BinaryExpressionEvaluator rightEval = createBinaryEvaluator(
866             operandEvaluator, operandType,
867             new LiteralEvaluator(Integer.valueOf(1), "int"), PsiType.INT,
868             operation == JavaTokenType.PLUSPLUS ? JavaTokenType.PLUS : JavaTokenType.MINUS,
869             unboxedOperandType!= null? unboxedOperandType : operandType
870           );
871           myResult = new AssignmentEvaluator(operandEvaluator, unboxedOperandType != null? new BoxingEvaluator(rightEval) : rightEval);
872         }
873         catch (IncorrectOperationException e) {
874           LOG.error(e);
875         }
876       }
877       else {
878         if (JavaTokenType.PLUS.equals(operation) || JavaTokenType.MINUS.equals(operation)|| JavaTokenType.TILDE.equals(operation)) {
879           operandEvaluator = handleUnaryNumericPromotion(operandType, operandEvaluator);
880         }
881         else {
882           if (unboxedOperandType != null) {
883             operandEvaluator = new UnBoxingEvaluator(operandEvaluator);
884           }
885         }
886         myResult = new UnaryExpressionEvaluator(operation, expressionType.getCanonicalText(), operandEvaluator, expression.getOperationSign().getText());
887       }
888     }
889
890     @Override
891     public void visitMethodCallExpression(PsiMethodCallExpression expression) {
892       if (LOG.isDebugEnabled()) {
893         LOG.debug("visitMethodCallExpression " + expression);
894       }
895       final PsiExpressionList argumentList = expression.getArgumentList();
896       final PsiExpression[] argExpressions = argumentList.getExpressions();
897       Evaluator[] argumentEvaluators = new Evaluator[argExpressions.length];
898       // evaluate arguments
899       for (int idx = 0; idx < argExpressions.length; idx++) {
900         final PsiExpression psiExpression = argExpressions[idx];
901         psiExpression.accept(this);
902         if (myResult == null) {
903           // cannot build evaluator
904           throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", psiExpression.getText()));
905         }
906         argumentEvaluators[idx] = new DisableGC(myResult);
907       }
908       PsiReferenceExpression methodExpr = expression.getMethodExpression();
909
910       final JavaResolveResult resolveResult = methodExpr.advancedResolve(false);
911       final PsiMethod psiMethod = (PsiMethod)resolveResult.getElement();
912
913       PsiExpression qualifier = methodExpr.getQualifierExpression();
914       Evaluator objectEvaluator;
915       JVMName contextClass = null;
916
917       if(psiMethod != null) {
918         PsiClass methodPsiClass = psiMethod.getContainingClass();
919         contextClass =  JVMNameUtil.getJVMQualifiedName(methodPsiClass);
920         if (psiMethod.hasModifierProperty(PsiModifier.STATIC)) {
921           objectEvaluator = new TypeEvaluator(contextClass);
922         }
923         else if (qualifier != null ) {
924           qualifier.accept(this);
925           objectEvaluator = myResult;
926         }
927         else {
928           int iterationCount = 0;
929           final PsiElement currentFileResolveScope = resolveResult.getCurrentFileResolveScope();
930           if (currentFileResolveScope instanceof PsiClass) {
931             PsiClass aClass = getContextPsiClass();
932             while(aClass != null && !aClass.equals(currentFileResolveScope)) {
933               aClass = getOuterClass(aClass);
934               iterationCount++;
935             }
936           }
937           objectEvaluator = new ThisEvaluator(iterationCount);
938         }
939       }
940       else {
941         //trying to guess
942         if (qualifier != null) {
943           PsiType type = qualifier.getType();
944
945           if (type != null) {
946             contextClass = JVMNameUtil.getJVMQualifiedName(type);
947           }
948
949           if (qualifier instanceof PsiReferenceExpression && ((PsiReferenceExpression)qualifier).resolve() instanceof PsiClass) {
950             // this is a call to a 'static' method but class is not available, try to evaluate by qname
951             if (contextClass == null) {
952               contextClass = JVMNameUtil.getJVMRawText(((PsiReferenceExpression)qualifier).getQualifiedName());
953             }
954             objectEvaluator = new TypeEvaluator(contextClass);
955           }
956           else {
957             qualifier.accept(this);
958             objectEvaluator = myResult;
959           }
960         }
961         else {
962           objectEvaluator = new ThisEvaluator();
963           contextClass = JVMNameUtil.getContextClassJVMQualifiedName(myPosition);
964           if(contextClass == null && myContextPsiClass != null) {
965             contextClass = JVMNameUtil.getJVMQualifiedName(myContextPsiClass);
966           }
967           //else {
968           //  throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(
969           //    DebuggerBundle.message("evaluation.error.method.not.found", methodExpr.getReferenceName()))
970           //  );
971           //}
972         }
973       }
974
975       if (objectEvaluator == null) {
976         throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText()));
977       }
978
979       if (psiMethod != null && !psiMethod.isConstructor()) {
980         if (psiMethod.getReturnType() == null) {
981           throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.method.return.type", psiMethod.getText()));
982         }
983       }
984
985       boolean defaultInterfaceMethod = false;
986       boolean mustBeVararg = false;
987
988       if (psiMethod != null) {
989         processBoxingConversions(psiMethod.getParameterList().getParameters(), argExpressions, resolveResult.getSubstitutor(), argumentEvaluators);
990         argumentEvaluators = wrapVarargs(psiMethod.getParameterList().getParameters(), argExpressions, resolveResult.getSubstitutor(), argumentEvaluators);
991         defaultInterfaceMethod = psiMethod.hasModifierProperty(PsiModifier.DEFAULT);
992         mustBeVararg = psiMethod.isVarArgs();
993       }
994
995       myResult = new MethodEvaluator(objectEvaluator, contextClass, methodExpr.getReferenceName(),
996                                      psiMethod != null ? JVMNameUtil.getJVMSignature(psiMethod) : null, argumentEvaluators,
997                                      defaultInterfaceMethod, mustBeVararg);
998     }
999
1000     @Override
1001     public void visitLiteralExpression(PsiLiteralExpression expression) {
1002       final HighlightInfo parsingError = HighlightUtil.checkLiteralExpressionParsingError(expression, null, null);
1003       if (parsingError != null) {
1004         throwEvaluateException(parsingError.getDescription());
1005         return;
1006       }
1007
1008       final PsiType type = expression.getType();
1009       if (type == null) {
1010         throwEvaluateException(expression + ": null type");
1011         return;
1012       }
1013
1014       myResult = new LiteralEvaluator(expression.getValue(), type.getCanonicalText());
1015     }
1016
1017     @Override
1018     public void visitArrayAccessExpression(PsiArrayAccessExpression expression) {
1019       final PsiExpression indexExpression = expression.getIndexExpression();
1020       if(indexExpression == null) {
1021         throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
1022       }
1023       indexExpression.accept(this);
1024       final Evaluator indexEvaluator = handleUnaryNumericPromotion(indexExpression.getType(), myResult);
1025
1026       expression.getArrayExpression().accept(this);
1027       Evaluator arrayEvaluator = myResult;
1028       myResult = new ArrayAccessEvaluator(arrayEvaluator, indexEvaluator);
1029     }
1030
1031
1032     /**
1033      * Handles unboxing and numeric promotion issues for
1034      * - array dimension expressions
1035      * - array index expression
1036      * - unary +, -, and ~ operations
1037      * @param operandExpressionType
1038      * @param operandEvaluator  @return operandEvaluator possibly 'wrapped' with necessary unboxing and type-casting evaluators to make returning value
1039      * suitable for mentioned contexts
1040      */
1041     private static Evaluator handleUnaryNumericPromotion(final PsiType operandExpressionType, Evaluator operandEvaluator) {
1042       final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(operandExpressionType);
1043       if (unboxedType != null && !PsiType.BOOLEAN.equals(unboxedType)) {
1044         operandEvaluator = new UnBoxingEvaluator(operandEvaluator);
1045       }
1046
1047       // handle numeric promotion
1048       final PsiType _unboxedIndexType = unboxedType != null? unboxedType : operandExpressionType;
1049       if (_unboxedIndexType instanceof PsiPrimitiveType) {
1050         final PsiType promotionType = calcUnaryNumericPromotionType((PsiPrimitiveType)_unboxedIndexType);
1051         if (promotionType != null) {
1052           operandEvaluator = new TypeCastEvaluator(operandEvaluator, promotionType.getCanonicalText(), true);
1053         }
1054       }
1055       return operandEvaluator;
1056     }
1057
1058     @SuppressWarnings({"ConstantConditions"})
1059     @Override
1060     public void visitTypeCastExpression(PsiTypeCastExpression expression) {
1061       final PsiExpression operandExpr = expression.getOperand();
1062       operandExpr.accept(this);
1063       Evaluator operandEvaluator = myResult;
1064       final PsiType castType = expression.getCastType().getType();
1065       final PsiType operandType = operandExpr.getType();
1066
1067       // if operand type can not be resolved in current context - leave it for runtime checks
1068       if (castType != null && operandType != null && !TypeConversionUtil.areTypesConvertible(operandType, castType) && PsiUtil.resolveClassInType(operandType) != null) {
1069         throw new EvaluateRuntimeException(
1070           new EvaluateException(JavaErrorMessages.message("inconvertible.type.cast", JavaHighlightUtil.formatType(operandType), JavaHighlightUtil
1071             .formatType(castType)))
1072         );
1073       }
1074
1075       final boolean shouldPerformBoxingConversion = castType != null && operandType != null && TypeConversionUtil.boxingConversionApplicable(castType, operandType);
1076       final boolean castingToPrimitive = castType instanceof PsiPrimitiveType;
1077       if (shouldPerformBoxingConversion && castingToPrimitive) {
1078         operandEvaluator = new UnBoxingEvaluator(operandEvaluator);
1079       }
1080
1081       final boolean performCastToWrapperClass = shouldPerformBoxingConversion && !castingToPrimitive;
1082
1083       if (!(PsiUtil.resolveClassInClassTypeOnly(castType) instanceof PsiTypeParameter)) {
1084         String castTypeName = castType.getCanonicalText();
1085         if (performCastToWrapperClass) {
1086           final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(castType);
1087           if (unboxedType != null) {
1088             castTypeName = unboxedType.getCanonicalText();
1089           }
1090         }
1091
1092         myResult = new TypeCastEvaluator(operandEvaluator, castTypeName, castingToPrimitive);
1093       }
1094
1095       if (performCastToWrapperClass) {
1096         myResult = new BoxingEvaluator(myResult);
1097       }
1098     }
1099
1100     @Override
1101     public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) {
1102       PsiType type = expression.getOperand().getType();
1103
1104       if (type instanceof PsiPrimitiveType) {
1105         final JVMName typeName = JVMNameUtil.getJVMRawText(((PsiPrimitiveType)type).getBoxedTypeName());
1106         myResult = new FieldEvaluator(new TypeEvaluator(typeName), FieldEvaluator.TargetClassFilter.ALL, "TYPE");
1107       }
1108       else {
1109         myResult = new ClassObjectEvaluator(new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(type)));
1110       }
1111     }
1112
1113     @Override
1114     public void visitLambdaExpression(PsiLambdaExpression expression) {
1115       throw new EvaluateRuntimeException(new UnsupportedExpressionException(DebuggerBundle.message("evaluation.error.lambda.evaluation.not.supported")));
1116     }
1117
1118     @Override
1119     public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) {
1120       throw new EvaluateRuntimeException(new UnsupportedExpressionException(DebuggerBundle.message("evaluation.error.method.reference.evaluation.not.supported")));
1121     }
1122
1123     @Override
1124     public void visitNewExpression(final PsiNewExpression expression) {
1125       PsiType expressionPsiType = expression.getType();
1126       if (expressionPsiType instanceof PsiArrayType) {
1127         Evaluator dimensionEvaluator = null;
1128         PsiExpression[] dimensions = expression.getArrayDimensions();
1129         if (dimensions.length == 1){
1130           PsiExpression dimensionExpression = dimensions[0];
1131           dimensionExpression.accept(this);
1132           if (myResult != null) {
1133             dimensionEvaluator = handleUnaryNumericPromotion(dimensionExpression.getType(), myResult);
1134           }
1135           else {
1136             throwEvaluateException(
1137               DebuggerBundle.message("evaluation.error.invalid.array.dimension.expression", dimensionExpression.getText()));
1138           }
1139         }
1140         else if (dimensions.length > 1){
1141           throwEvaluateException(DebuggerBundle.message("evaluation.error.multi.dimensional.arrays.creation.not.supported"));
1142         }
1143
1144         Evaluator initializerEvaluator = null;
1145         PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer();
1146         if (arrayInitializer != null) {
1147           if (dimensionEvaluator != null) { // initializer already exists
1148             throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText()));
1149           }
1150           arrayInitializer.accept(this);
1151           if (myResult != null) {
1152             initializerEvaluator = handleUnaryNumericPromotion(arrayInitializer.getType(), myResult);
1153           }
1154           else {
1155             throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", arrayInitializer.getText()));
1156           }
1157           /*
1158           PsiExpression[] initializers = arrayInitializer.getInitializers();
1159           initializerEvaluators = new Evaluator[initializers.length];
1160           for (int idx = 0; idx < initializers.length; idx++) {
1161             PsiExpression initializer = initializers[idx];
1162             initializer.accept(this);
1163             if (myResult instanceof Evaluator) {
1164               initializerEvaluators[idx] = myResult;
1165             }
1166             else {
1167               throw new EvaluateException("Invalid expression for array initializer: " + initializer.getText(), true);
1168             }
1169           }
1170           */
1171         }
1172         if (dimensionEvaluator == null && initializerEvaluator == null) {
1173           throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText()));
1174         }
1175         myResult = new NewArrayInstanceEvaluator(
1176           new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(expressionPsiType)),
1177           dimensionEvaluator,
1178           initializerEvaluator
1179         );
1180       }
1181       else if (expressionPsiType instanceof PsiClassType){ // must be a class ref
1182         PsiClass aClass = ((PsiClassType)expressionPsiType).resolve();
1183         if(aClass instanceof PsiAnonymousClass) {
1184           throw new EvaluateRuntimeException(new UnsupportedExpressionException(DebuggerBundle.message("evaluation.error.anonymous.class.evaluation.not.supported")));
1185         }
1186         PsiExpressionList argumentList = expression.getArgumentList();
1187         if (argumentList == null) {
1188           throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
1189         }
1190         final PsiExpression[] argExpressions = argumentList.getExpressions();
1191         final JavaResolveResult constructorResolveResult = expression.resolveMethodGenerics();
1192         final PsiMethod constructor = (PsiMethod)constructorResolveResult.getElement();
1193         if (constructor == null && argExpressions.length > 0) {
1194           throw new EvaluateRuntimeException(new EvaluateException(
1195             DebuggerBundle.message("evaluation.error.cannot.resolve.constructor", expression.getText()), null));
1196         }
1197         Evaluator[] argumentEvaluators = new Evaluator[argExpressions.length];
1198         // evaluate arguments
1199         for (int idx = 0; idx < argExpressions.length; idx++) {
1200           PsiExpression argExpression = argExpressions[idx];
1201           argExpression.accept(this);
1202           if (myResult != null) {
1203             argumentEvaluators[idx] = new DisableGC(myResult);
1204           }
1205           else {
1206             throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", argExpression.getText()));
1207           }
1208         }
1209
1210         if (constructor != null) {
1211           processBoxingConversions(constructor.getParameterList().getParameters(), argExpressions, constructorResolveResult.getSubstitutor(), argumentEvaluators);
1212           argumentEvaluators = wrapVarargs(constructor.getParameterList().getParameters(), argExpressions, constructorResolveResult.getSubstitutor(), argumentEvaluators);
1213         }
1214
1215         if (aClass != null && aClass.getContainingClass() != null && !aClass.hasModifierProperty(PsiModifier.STATIC)) {
1216           argumentEvaluators = addThisEvaluator(argumentEvaluators, aClass.getContainingClass());
1217         }
1218
1219         JVMName signature = JVMNameUtil.getJVMConstructorSignature(constructor, aClass);
1220         myResult = new NewClassInstanceEvaluator(
1221           new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(expressionPsiType)),
1222           signature,
1223           argumentEvaluators
1224         );
1225       }
1226       else {
1227         if (expressionPsiType != null) {
1228           throwEvaluateException("Unsupported expression type: " + expressionPsiType.getPresentableText());
1229         }
1230         else {
1231           throwEvaluateException("Unknown type for expression: " + expression.getText());
1232         }
1233       }
1234     }
1235
1236     private Evaluator[] addThisEvaluator(Evaluator[] argumentEvaluators, PsiClass cls) {
1237       Evaluator[] res = new Evaluator[argumentEvaluators.length+1];
1238       int depth = calcIterationCount(cls, "this");
1239       res[0] = new ThisEvaluator(depth);
1240       System.arraycopy(argumentEvaluators, 0, res, 1, argumentEvaluators.length);
1241       return res;
1242     }
1243
1244     @Override
1245     public void visitArrayInitializerExpression(PsiArrayInitializerExpression expression) {
1246       PsiExpression[] initializers = expression.getInitializers();
1247       Evaluator[] evaluators = new Evaluator[initializers.length];
1248       final PsiType type = expression.getType();
1249       boolean primitive = type instanceof PsiArrayType && ((PsiArrayType)type).getComponentType() instanceof PsiPrimitiveType;
1250       for (int idx = 0; idx < initializers.length; idx++) {
1251         PsiExpression initializer = initializers[idx];
1252         initializer.accept(this);
1253         if (myResult != null) {
1254           final Evaluator coerced =
1255             primitive ? handleUnaryNumericPromotion(initializer.getType(), myResult) : new BoxingEvaluator(myResult);
1256           evaluators[idx] = new DisableGC(coerced);
1257         }
1258         else {
1259           throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", initializer.getText()));
1260         }
1261       }
1262       myResult = new ArrayInitializerEvaluator(evaluators);
1263       if (type != null && !(expression.getParent() instanceof PsiNewExpression)) {
1264         myResult = new NewArrayInstanceEvaluator(new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(type)),
1265                                                  null,
1266                                                  myResult);
1267       }
1268     }
1269
1270     @Nullable
1271     private static PsiClass getOuterClass(PsiClass aClass) {
1272       return aClass == null ? null : PsiTreeUtil.getContextOfType(aClass, PsiClass.class, true);
1273     }
1274
1275     private PsiClass getContainingClass(PsiVariable variable) {
1276       PsiElement element = PsiTreeUtil.getParentOfType(variable.getParent(), PsiClass.class, false);
1277       return element == null ? getContextPsiClass() : (PsiClass)element;
1278     }
1279
1280     public PsiClass getContextPsiClass() {
1281       return myContextPsiClass;
1282     }
1283
1284     protected ExpressionEvaluator buildElement(final PsiElement element) throws EvaluateException {
1285       LOG.assertTrue(element.isValid());
1286
1287       myContextPsiClass = PsiTreeUtil.getContextOfType(element, PsiClass.class, false);
1288       try {
1289         element.accept(this);
1290       }
1291       catch (EvaluateRuntimeException e) {
1292         throw e.getCause();
1293       }
1294       if (myResult == null) {
1295         throw EvaluateExceptionUtil
1296           .createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", element.toString()));
1297       }
1298       return new ExpressionEvaluatorImpl(myResult);
1299     }
1300   }
1301
1302   private static Evaluator[] wrapVarargs(final PsiParameter[] declaredParams,
1303                                          final PsiExpression[] actualArgumentExpressions,
1304                                          final PsiSubstitutor methodResolveSubstitutor,
1305                                          final Evaluator[] argumentEvaluators) {
1306     int lastParam = declaredParams.length - 1;
1307     if (lastParam >= 0 && declaredParams[lastParam].isVarArgs() && argumentEvaluators.length > lastParam) {
1308       // only wrap if the first varargs parameter is null for now
1309       if (!TypeConversionUtil.isNullType(actualArgumentExpressions[lastParam].getType())) {
1310         return argumentEvaluators;
1311       }
1312       // do not wrap arrays twice
1313       if (argumentEvaluators.length - lastParam == 1 && actualArgumentExpressions[lastParam].getType() instanceof PsiArrayType) {
1314         return argumentEvaluators;
1315       }
1316       PsiEllipsisType declaredParamType = (PsiEllipsisType)methodResolveSubstitutor.substitute(declaredParams[lastParam].getType());
1317       ArrayInitializerEvaluator varargArrayEvaluator =
1318         new ArrayInitializerEvaluator(Arrays.copyOfRange(argumentEvaluators, lastParam, argumentEvaluators.length));
1319       NewArrayInstanceEvaluator evaluator =
1320         new NewArrayInstanceEvaluator(new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(declaredParamType.toArrayType())), null,
1321                                       varargArrayEvaluator);
1322       Evaluator[] res = new Evaluator[declaredParams.length];
1323       System.arraycopy(argumentEvaluators, 0, res, 0, lastParam);
1324       res[lastParam] = new DisableGC(evaluator);
1325       return res;
1326     }
1327     return argumentEvaluators;
1328   }
1329
1330   private static void processBoxingConversions(final PsiParameter[] declaredParams,
1331                                                final PsiExpression[] actualArgumentExpressions,
1332                                                final PsiSubstitutor methodResolveSubstitutor,
1333                                                final Evaluator[] argumentEvaluators) {
1334     if (declaredParams.length > 0) {
1335       final int paramCount = Math.max(declaredParams.length, actualArgumentExpressions.length);
1336       PsiType varargType = null;
1337       for (int idx = 0; idx < paramCount; idx++) {
1338         if (idx >= actualArgumentExpressions.length) {
1339           break; // actual arguments count is less than number of declared params
1340         }
1341         PsiType declaredParamType;
1342         if (idx < declaredParams.length) {
1343           declaredParamType = methodResolveSubstitutor.substitute(declaredParams[idx].getType());
1344           if (declaredParamType instanceof PsiEllipsisType) {
1345             declaredParamType = varargType = ((PsiEllipsisType)declaredParamType).getComponentType();
1346           }
1347         }
1348         else if (varargType != null) {
1349           declaredParamType = varargType;
1350         }
1351         else {
1352           break;
1353         }
1354         final PsiType actualArgType = actualArgumentExpressions[idx].getType();
1355         if (TypeConversionUtil.boxingConversionApplicable(declaredParamType, actualArgType)) {
1356           final Evaluator argEval = argumentEvaluators[idx];
1357           argumentEvaluators[idx] = declaredParamType instanceof PsiPrimitiveType ? new UnBoxingEvaluator(argEval) : new BoxingEvaluator(argEval);
1358         }
1359       }
1360     }
1361   }
1362 }