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.
18 * Class EvaluatorBuilderImpl
21 package com.intellij.debugger.engine.evaluation.expression;
23 import com.intellij.codeInsight.daemon.JavaErrorMessages;
24 import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil;
25 import com.intellij.debugger.DebuggerBundle;
26 import com.intellij.debugger.SourcePosition;
27 import com.intellij.debugger.engine.ContextUtil;
28 import com.intellij.debugger.engine.DebuggerUtils;
29 import com.intellij.debugger.engine.JVMName;
30 import com.intellij.debugger.engine.JVMNameUtil;
31 import com.intellij.debugger.engine.evaluation.*;
32 import com.intellij.openapi.diagnostic.Logger;
33 import com.intellij.openapi.project.Project;
34 import com.intellij.psi.*;
35 import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
36 import com.intellij.psi.search.GlobalSearchScope;
37 import com.intellij.psi.tree.IElementType;
38 import com.intellij.psi.util.PsiTreeUtil;
39 import com.intellij.psi.util.PsiTypesUtil;
40 import com.intellij.psi.util.TypeConversionUtil;
41 import com.intellij.util.IncorrectOperationException;
42 import com.sun.jdi.Value;
43 import org.jetbrains.annotations.Nullable;
45 import java.util.ArrayList;
46 import java.util.HashSet;
47 import java.util.List;
50 public class EvaluatorBuilderImpl implements EvaluatorBuilder {
51 private static final EvaluatorBuilderImpl ourInstance = new EvaluatorBuilderImpl();
52 private final CodeFragmentFactory myCodeFactory;
54 private EvaluatorBuilderImpl() {
55 myCodeFactory = new CodeFragmentFactoryContextWrapper(DefaultCodeFragmentFactory.getInstance());
58 public static EvaluatorBuilder getInstance() {
62 public ExpressionEvaluator build(final TextWithImports text, final PsiElement contextElement, final SourcePosition position) throws EvaluateException {
63 if (contextElement == null) {
64 throw EvaluateExceptionUtil.CANNOT_FIND_SOURCE_CLASS;
67 final Project project = contextElement.getProject();
69 PsiCodeFragment codeFragment = myCodeFactory.createCodeFragment(text, contextElement, project);
70 if(codeFragment == null) {
71 throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", text.getText()));
73 codeFragment.forceResolveScope(GlobalSearchScope.allScope(project));
74 DebuggerUtils.checkSyntax(codeFragment);
76 return build(codeFragment, position);
79 public ExpressionEvaluator build(final PsiElement codeFragment, final SourcePosition position) throws EvaluateException {
80 return new Builder(position).buildElement(codeFragment);
83 private static class Builder extends JavaElementVisitor {
84 private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl");
85 private Evaluator myResult = null;
86 private PsiClass myContextPsiClass;
87 private CodeFragmentEvaluator myCurrentFragmentEvaluator;
88 private final Set<JavaCodeFragment> myVisitedFragments = new HashSet<JavaCodeFragment>();
90 private final SourcePosition myPosition;
92 private Builder(@Nullable SourcePosition position) {
93 myPosition = position;
97 public void visitCodeFragment(JavaCodeFragment codeFragment) {
98 myVisitedFragments.add(codeFragment);
99 ArrayList<Evaluator> evaluators = new ArrayList<Evaluator>();
101 CodeFragmentEvaluator oldFragmentEvaluator = myCurrentFragmentEvaluator;
102 myCurrentFragmentEvaluator = new CodeFragmentEvaluator(oldFragmentEvaluator);
104 for (PsiElement child = codeFragment.getFirstChild(); child != null; child = child.getNextSibling()) {
106 if(myResult != null) {
107 evaluators.add(myResult);
112 myCurrentFragmentEvaluator.setStatements(evaluators.toArray(new Evaluator[evaluators.size()]));
113 myResult = myCurrentFragmentEvaluator;
115 myCurrentFragmentEvaluator = oldFragmentEvaluator;
119 public void visitErrorElement(PsiErrorElement element) {
120 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", element.getText()));
124 public void visitAssignmentExpression(PsiAssignmentExpression expression) {
125 final PsiExpression rExpression = expression.getRExpression();
126 if(rExpression == null) {
127 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
130 rExpression.accept(this);
131 Evaluator rEvaluator = myResult;
133 if(expression.getOperationSign().getTokenType() != JavaTokenType.EQ) {
134 throwEvaluateException(DebuggerBundle.message("evaluation.error.operation.not.supported", expression.getOperationSign().getText()));
137 final PsiExpression lExpression = expression.getLExpression();
139 final PsiType lType = lExpression.getType();
141 throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", lExpression.getText()));
144 if(!TypeConversionUtil.areTypesAssignmentCompatible(lType, rExpression)) {
145 throwEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", expression.getOperationSign().getText()));
147 lExpression.accept(this);
148 Evaluator lEvaluator = myResult;
150 rEvaluator = handleAssignmentBoxingAndPrimitiveTypeConversions(lType, rExpression.getType(), rEvaluator);
152 myResult = new AssignmentEvaluator(lEvaluator, rEvaluator);
155 // returns rEvaluator possibly wrapped with boxing/unboxing and casting evaluators
156 private static Evaluator handleAssignmentBoxingAndPrimitiveTypeConversions(PsiType lType, PsiType rType, Evaluator rEvaluator) {
157 final PsiType unboxedLType = PsiPrimitiveType.getUnboxedType(lType);
159 if (unboxedLType != null) {
160 if (rType instanceof PsiPrimitiveType && !PsiPrimitiveType.NULL.equals(rType)) {
161 if (!rType.equals(unboxedLType)) {
162 rEvaluator = new TypeCastEvaluator(rEvaluator, unboxedLType.getCanonicalText(), true);
164 rEvaluator = new BoxingEvaluator(rEvaluator);
168 // either primitive type or not unboxable type
169 if (lType instanceof PsiPrimitiveType) {
170 if (rType instanceof PsiClassType) {
171 rEvaluator = new UnBoxingEvaluator(rEvaluator);
173 final PsiPrimitiveType unboxedRType = PsiPrimitiveType.getUnboxedType(rType);
174 final PsiType _rType = unboxedRType != null? unboxedRType : rType;
175 if (_rType instanceof PsiPrimitiveType && !PsiPrimitiveType.NULL.equals(_rType)) {
176 if (!lType.equals(_rType)) {
177 rEvaluator = new TypeCastEvaluator(rEvaluator, lType.getCanonicalText(), true);
186 public void visitStatement(PsiStatement statement) {
187 throwEvaluateException(DebuggerBundle.message("evaluation.error.statement.not.supported", statement.getText()));
191 public void visitBlockStatement(PsiBlockStatement statement) {
192 PsiStatement[] statements = statement.getCodeBlock().getStatements();
193 Evaluator [] evaluators = new Evaluator[statements.length];
194 for (int i = 0; i < statements.length; i++) {
195 PsiStatement psiStatement = statements[i];
196 psiStatement.accept(this);
197 evaluators[i] = myResult;
200 myResult = new BlockStatementEvaluator(evaluators);
204 public void visitWhileStatement(PsiWhileStatement statement) {
205 PsiStatement body = statement.getBody();
206 if(body == null) return;
208 Evaluator bodyEvaluator = myResult;
210 PsiExpression condition = statement.getCondition();
211 if(condition == null) return;
212 condition.accept(this);
214 if(statement.getParent() instanceof PsiLabeledStatement) {
215 label = ((PsiLabeledStatement)statement.getParent()).getLabelIdentifier().getText();
217 myResult = new WhileStatementEvaluator(myResult, bodyEvaluator, label);
221 public void visitForStatement(PsiForStatement statement) {
222 PsiStatement initializer = statement.getInitialization();
223 Evaluator initializerEvaluator = null;
224 if(initializer != null){
225 initializer.accept(this);
226 initializerEvaluator = myResult;
229 PsiExpression condition = statement.getCondition();
230 Evaluator conditionEvaluator = null;
231 if(condition != null) {
232 condition.accept(this);
233 conditionEvaluator = myResult;
236 PsiStatement update = statement.getUpdate();
237 Evaluator updateEvaluator = null;
240 updateEvaluator = myResult;
243 PsiStatement body = statement.getBody();
244 if(body == null) return;
246 Evaluator bodyEvaluator = myResult;
249 if(statement.getParent() instanceof PsiLabeledStatement) {
250 label = ((PsiLabeledStatement)statement.getParent()).getLabelIdentifier().getText();
252 myResult = new ForStatementEvaluator(initializerEvaluator, conditionEvaluator, updateEvaluator, bodyEvaluator, label);
256 public void visitIfStatement(PsiIfStatement statement) {
257 PsiStatement thenBranch = statement.getThenBranch();
258 if(thenBranch == null) return;
259 thenBranch.accept(this);
260 Evaluator thenEvaluator = myResult;
262 PsiStatement elseBranch = statement.getElseBranch();
263 Evaluator elseEvaluator = null;
264 if(elseBranch != null){
265 elseBranch.accept(this);
266 elseEvaluator = myResult;
269 PsiExpression condition = statement.getCondition();
270 if(condition == null) return;
271 condition.accept(this);
273 myResult = new IfStatementEvaluator(myResult, thenEvaluator, elseEvaluator);
277 public void visitBreakStatement(PsiBreakStatement statement) {
278 PsiIdentifier labelIdentifier = statement.getLabelIdentifier();
279 myResult = BreakContinueStatementEvaluator.createBreakEvaluator(labelIdentifier != null ? labelIdentifier.getText() : null);
283 public void visitContinueStatement(PsiContinueStatement statement) {
284 PsiIdentifier labelIdentifier = statement.getLabelIdentifier();
285 myResult = BreakContinueStatementEvaluator.createContinueEvaluator(labelIdentifier != null ? labelIdentifier.getText() : null);
289 public void visitExpressionStatement(PsiExpressionStatement statement) {
290 statement.getExpression().accept(this);
294 public void visitExpression(PsiExpression expression) {
295 if (LOG.isDebugEnabled()) {
296 LOG.debug("visitExpression " + expression);
301 public void visitBinaryExpression(PsiBinaryExpression expression) {
302 if (LOG.isDebugEnabled()) {
303 LOG.debug("visitBinaryExpression " + expression);
305 final PsiExpression lOperand = expression.getLOperand();
306 lOperand.accept(this);
307 Evaluator lResult = myResult;
308 final PsiExpression rOperand = expression.getROperand();
309 if(rOperand == null) {
310 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
312 rOperand.accept(this);
313 Evaluator rResult = myResult;
314 IElementType opType = expression.getOperationSign().getTokenType();
315 PsiType expressionExpectedType = expression.getType();
316 if (expressionExpectedType == null) {
317 throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText()));
319 myResult = createBinaryEvaluator(lResult, lOperand.getType(), rResult, rOperand.getType(), opType, expressionExpectedType);
323 // constructs binary evaluator handling unboxing and numeric promotion issues
324 private static BinaryExpressionEvaluator createBinaryEvaluator(
325 Evaluator lResult, final PsiType lType, Evaluator rResult, final PsiType rType, final IElementType operation, final PsiType expressionExpectedType) {
326 // handle unboxing if neccesary
327 if (isUnboxingInBinaryExpressionApplicable(lType, rType, operation)) {
328 if (rType instanceof PsiClassType && UnBoxingEvaluator.isTypeUnboxable(rType.getCanonicalText())) {
329 rResult = new UnBoxingEvaluator(rResult);
331 if (lType instanceof PsiClassType && UnBoxingEvaluator.isTypeUnboxable(lType.getCanonicalText())) {
332 lResult = new UnBoxingEvaluator(lResult);
335 if (isBinaryNumericPromotionApplicable(lType, rType, operation)) {
336 PsiType _lType = lType;
337 final PsiPrimitiveType unboxedLType = PsiPrimitiveType.getUnboxedType(lType);
338 if (unboxedLType != null) {
339 _lType = unboxedLType;
342 PsiType _rType = rType;
343 final PsiPrimitiveType unboxedRType = PsiPrimitiveType.getUnboxedType(rType);
344 if (unboxedRType != null) {
345 _rType = unboxedRType;
348 // handle numeric promotion
349 if (PsiType.DOUBLE.equals(_lType)) {
350 if (TypeConversionUtil.areTypesConvertible(_rType, PsiType.DOUBLE)) {
351 rResult = new TypeCastEvaluator(rResult, PsiType.DOUBLE.getCanonicalText(), true);
354 else if (PsiType.DOUBLE.equals(_rType)) {
355 if (TypeConversionUtil.areTypesConvertible(_lType, PsiType.DOUBLE)) {
356 lResult = new TypeCastEvaluator(lResult, PsiType.DOUBLE.getCanonicalText(), true);
359 else if (PsiType.FLOAT.equals(_lType)) {
360 if (TypeConversionUtil.areTypesConvertible(_rType, PsiType.FLOAT)) {
361 rResult = new TypeCastEvaluator(rResult, PsiType.FLOAT.getCanonicalText(), true);
364 else if (PsiType.FLOAT.equals(_rType)) {
365 if (TypeConversionUtil.areTypesConvertible(_lType, PsiType.FLOAT)) {
366 lResult = new TypeCastEvaluator(lResult, PsiType.FLOAT.getCanonicalText(), true);
369 else if (PsiType.LONG.equals(_lType)) {
370 if (TypeConversionUtil.areTypesConvertible(_rType, PsiType.LONG)) {
371 rResult = new TypeCastEvaluator(rResult, PsiType.LONG.getCanonicalText(), true);
374 else if (PsiType.LONG.equals(_rType)) {
375 if (TypeConversionUtil.areTypesConvertible(_lType, PsiType.LONG)) {
376 lResult = new TypeCastEvaluator(lResult, PsiType.LONG.getCanonicalText(), true);
380 if (!PsiType.INT.equals(_lType) && TypeConversionUtil.areTypesConvertible(_lType, PsiType.INT)) {
381 lResult = new TypeCastEvaluator(lResult, PsiType.INT.getCanonicalText(), true);
383 if (!PsiType.INT.equals(_rType) && TypeConversionUtil.areTypesConvertible(_rType, PsiType.INT)) {
384 rResult = new TypeCastEvaluator(rResult, PsiType.INT.getCanonicalText(), true);
389 return new BinaryExpressionEvaluator(lResult, rResult, operation, expressionExpectedType.getCanonicalText());
392 private static boolean isBinaryNumericPromotionApplicable(PsiType lType, PsiType rType, IElementType opType) {
393 if (lType == null || rType == null) {
396 if (opType == JavaTokenType.EQEQ || opType == JavaTokenType.NE) {
397 if (PsiType.NULL.equals(lType) || PsiType.NULL.equals(rType)) {
400 if (lType instanceof PsiClassType && rType instanceof PsiClassType) {
403 if (lType instanceof PsiClassType) {
404 return PsiPrimitiveType.getUnboxedType(lType) != null; // should be unboxable
406 if (rType instanceof PsiClassType) {
407 return PsiPrimitiveType.getUnboxedType(rType) != null; // should be unboxable
412 return opType == JavaTokenType.ASTERISK ||
413 opType == JavaTokenType.DIV ||
414 opType == JavaTokenType.PERC ||
415 opType == JavaTokenType.PLUS ||
416 opType == JavaTokenType.MINUS ||
417 opType == JavaTokenType.LT ||
418 opType == JavaTokenType.LE ||
419 opType == JavaTokenType.GT ||
420 opType == JavaTokenType.GE ||
421 opType == JavaTokenType.AND ||
422 opType == JavaTokenType.XOR ||
423 opType == JavaTokenType.OR;
427 private static boolean isUnboxingInBinaryExpressionApplicable(PsiType lType, PsiType rType, IElementType opCode) {
428 if (PsiType.NULL.equals(lType) || PsiType.NULL.equals(rType)) {
431 // handle '==' and '!=' separately
432 if (opCode == JavaTokenType.EQEQ || opCode == JavaTokenType.NE) {
433 return lType instanceof PsiPrimitiveType && rType instanceof PsiClassType ||
434 lType instanceof PsiClassType && rType instanceof PsiPrimitiveType;
436 // all other operations at least one should be of class type
437 return lType instanceof PsiClassType || rType instanceof PsiClassType;
442 * @return promotion type to cast to or null if no casting needed
445 private static PsiType calcUnaryNumericPromotionType(PsiPrimitiveType type) {
446 if (PsiType.BYTE.equals(type) || PsiType.SHORT.equals(type) || PsiType.CHAR.equals(type) || PsiType.INT.equals(type)) {
453 public void visitDeclarationStatement(PsiDeclarationStatement statement) {
454 List<Evaluator> evaluators = new ArrayList<Evaluator>();
456 PsiElement[] declaredElements = statement.getDeclaredElements();
457 for (PsiElement declaredElement : declaredElements) {
458 if (declaredElement instanceof PsiLocalVariable) {
459 if (myCurrentFragmentEvaluator != null) {
460 final PsiLocalVariable localVariable = (PsiLocalVariable)declaredElement;
462 final PsiType lType = localVariable.getType();
464 PsiElementFactory elementFactory = JavaPsiFacade.getInstance(localVariable.getProject()).getElementFactory();
466 PsiExpression initialValue = elementFactory.createExpressionFromText(PsiTypesUtil.getDefaultValueOfType(lType), null);
467 Object value = JavaConstantExpressionEvaluator.computeConstantExpression(initialValue, true);
468 myCurrentFragmentEvaluator.setInitialValue(localVariable.getName(), value);
470 catch (IncorrectOperationException e) {
473 catch (EvaluateException e) {
474 throw new EvaluateRuntimeException(e);
477 PsiExpression initializer = localVariable.getInitializer();
478 if (initializer != null) {
480 if (!TypeConversionUtil.areTypesAssignmentCompatible(lType, initializer)) {
481 throwEvaluateException(
482 DebuggerBundle.message("evaluation.error.incompatible.variable.initializer.type", localVariable.getName()));
484 final PsiType rType = initializer.getType();
485 initializer.accept(this);
486 Evaluator rEvaluator = myResult;
488 PsiExpression localVarReference = elementFactory.createExpressionFromText(localVariable.getName(), initializer);
490 localVarReference.accept(this);
491 Evaluator lEvaluator = myResult;
492 rEvaluator = handleAssignmentBoxingAndPrimitiveTypeConversions(localVarReference.getType(), rType, rEvaluator);
494 Evaluator assignment = new AssignmentEvaluator(lEvaluator, rEvaluator);
495 evaluators.add(assignment);
497 catch (IncorrectOperationException e) {
503 throw new EvaluateRuntimeException(new EvaluateException(
504 DebuggerBundle.message("evaluation.error.local.variable.declarations.not.supported"), null));
508 throw new EvaluateRuntimeException(new EvaluateException(
509 DebuggerBundle.message("evaluation.error.unsupported.declaration", declaredElement.getText()), null));
513 if(!evaluators.isEmpty()) {
514 CodeFragmentEvaluator codeFragmentEvaluator = new CodeFragmentEvaluator(myCurrentFragmentEvaluator);
515 codeFragmentEvaluator.setStatements(evaluators.toArray(new Evaluator[evaluators.size()]));
516 myResult = codeFragmentEvaluator;
523 public void visitConditionalExpression(PsiConditionalExpression expression) {
524 if (LOG.isDebugEnabled()) {
525 LOG.debug("visitConditionalExpression " + expression);
527 final PsiExpression thenExpression = expression.getThenExpression();
528 final PsiExpression elseExpression = expression.getElseExpression();
529 if (thenExpression == null || elseExpression == null){
530 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
532 PsiExpression condition = expression.getCondition();
533 condition.accept(this);
534 if (myResult == null) {
535 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", condition.getText())); return;
537 Evaluator conditionEvaluator = myResult;
538 thenExpression.accept(this);
539 if (myResult == null) {
540 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", thenExpression.getText())); return;
542 Evaluator thenEvaluator = myResult;
543 elseExpression.accept(this);
544 if (myResult == null) {
545 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", elseExpression.getText())); return;
547 Evaluator elseEvaluator = myResult;
548 myResult = new ConditionalExpressionEvaluator(conditionEvaluator, thenEvaluator, elseEvaluator);
552 public void visitReferenceExpression(PsiReferenceExpression expression) {
553 if (LOG.isDebugEnabled()) {
554 LOG.debug("visitReferenceExpression " + expression);
556 PsiExpression qualifier = expression.getQualifierExpression();
557 JavaResolveResult resolveResult = expression.advancedResolve(true);
558 PsiElement element = resolveResult.getElement();
560 if (element instanceof PsiLocalVariable || element instanceof PsiParameter) {
561 final Value labeledValue = element.getUserData(CodeFragmentFactoryContextWrapper.LABEL_VARIABLE_VALUE_KEY);
562 if (labeledValue != null) {
563 myResult = new IdentityEvaluator(labeledValue);
567 final PsiFile containingFile = element.getContainingFile();
568 if(containingFile instanceof PsiCodeFragment && myCurrentFragmentEvaluator != null && myVisitedFragments.contains(containingFile)) {
569 // psiVariable may live in PsiCodeFragment not only in debugger editors, for example Fabrique has such variables.
570 // So treat it as synthetic var only when this code fragment is located in DebuggerEditor,
571 // that's why we need to check that containing code fragment is the one we visited
572 myResult = new SyntheticVariableEvaluator(myCurrentFragmentEvaluator, ((PsiVariable)element).getName());
576 final PsiVariable psiVar = (PsiVariable)element;
577 final String localName = psiVar.getName();
578 PsiClass variableClass = getContainingClass(psiVar);
579 if (getContextPsiClass() == null || getContextPsiClass().equals(variableClass)) {
580 final LocalVariableEvaluator localVarEvaluator = new LocalVariableEvaluator(localName, ContextUtil.isJspImplicit(element));
581 if (psiVar instanceof PsiParameter) {
582 final PsiParameter param = (PsiParameter)psiVar;
583 final PsiParameterList paramList = PsiTreeUtil.getParentOfType(param, PsiParameterList.class, true);
584 if (paramList != null) {
585 localVarEvaluator.setParameterIndex(paramList.getParameterIndex(param));
588 myResult = localVarEvaluator;
591 // the expression references final var outside the context's class (in some of the outer classes)
592 int iterationCount = 0;
593 PsiClass aClass = getOuterClass(getContextPsiClass());
594 while (aClass != null && !aClass.equals(variableClass)) {
596 aClass = getOuterClass(aClass);
598 if (aClass != null) {
599 PsiExpression initializer = psiVar.getInitializer();
600 if(initializer != null) {
601 Object value = JavaPsiFacade.getInstance(psiVar.getProject()).getConstantEvaluationHelper().computeConstantExpression(initializer);
603 PsiType type = resolveResult.getSubstitutor().substitute(psiVar.getType());
604 myResult = new LiteralEvaluator(value, type.getCanonicalText());
608 Evaluator objectEvaluator = new ThisEvaluator(iterationCount);
609 //noinspection HardCodedStringLiteral
610 final PsiClass classAt = myPosition != null? JVMNameUtil.getClassAt(myPosition) : null;
611 FieldEvaluator.TargetClassFilter filter = FieldEvaluator.createClassFilter(classAt != null? classAt : getContextPsiClass());
612 myResult = new FieldEvaluator(objectEvaluator, filter, "val$" + localName);
615 throwEvaluateException(DebuggerBundle.message("evaluation.error.local.variable.missing.from.class.closure", localName));
617 else if (element instanceof PsiField) {
618 final PsiField psiField = (PsiField)element;
619 final PsiClass fieldClass = psiField.getContainingClass();
620 if(fieldClass == null) {
621 throwEvaluateException(DebuggerBundle.message("evaluation.error.cannot.resolve.field.class", psiField.getName())); return;
623 Evaluator objectEvaluator;
624 if (psiField.hasModifierProperty(PsiModifier.STATIC)) {
625 objectEvaluator = new TypeEvaluator(JVMNameUtil.getContextClassJVMQualifiedName(SourcePosition.createFromElement(psiField)));
627 else if(qualifier != null) {
628 qualifier.accept(this);
629 objectEvaluator = myResult;
631 else if (fieldClass.equals(getContextPsiClass()) || getContextPsiClass().isInheritor(fieldClass, true)) {
632 objectEvaluator = new ThisEvaluator();
634 else { // myContextPsiClass != fieldClass && myContextPsiClass is not a subclass of fieldClass
635 int iterationCount = 0;
636 PsiClass aClass = getContextPsiClass();
637 while (aClass != null && !(aClass.equals(fieldClass) || aClass.isInheritor(fieldClass, true))) {
639 aClass = getOuterClass(aClass);
641 if (aClass == null) {
642 throwEvaluateException(DebuggerBundle.message("evaluation.error.cannot.sources.for.field.class", psiField.getName()));
644 objectEvaluator = new ThisEvaluator(iterationCount);
646 myResult = new FieldEvaluator(objectEvaluator, FieldEvaluator.createClassFilter(fieldClass), psiField.getName());
649 //let's guess what this could be
650 PsiElement nameElement = expression.getReferenceNameElement(); // get "b" part
652 if (nameElement instanceof PsiIdentifier) {
653 name = nameElement.getText();
656 //noinspection HardCodedStringLiteral
657 final String elementDisplayString = nameElement != null ? nameElement.getText() : "(null)";
658 throwEvaluateException(DebuggerBundle.message("evaluation.error.identifier.expected", elementDisplayString));
662 if(qualifier != null) {
663 final PsiElement qualifierTarget = qualifier instanceof PsiReferenceExpression
664 ? ((PsiReferenceExpression)qualifier).resolve() : null;
665 if (qualifierTarget instanceof PsiClass) {
666 // this is a call to a 'static' field
667 PsiClass psiClass = (PsiClass)qualifierTarget;
668 final JVMName typeName = JVMNameUtil.getJVMQualifiedName(psiClass);
669 myResult = new FieldEvaluator(new TypeEvaluator(typeName), FieldEvaluator.createClassFilter(psiClass), name);
672 PsiType type = qualifier.getType();
674 throwEvaluateException(DebuggerBundle.message("evaluation.error.qualifier.type.unknown", qualifier.getText()));
677 qualifier.accept(this);
678 if (myResult == null) {
679 throwEvaluateException(DebuggerBundle.message("evaluation.error.cannot.evaluate.qualifier", qualifier.getText()));
682 myResult = new FieldEvaluator(myResult, FieldEvaluator.createClassFilter(type), name);
686 myResult = new LocalVariableEvaluator(name, false);
691 private static void throwEvaluateException(String message) throws EvaluateRuntimeException {
692 //noinspection ThrowableResultOfMethodCallIgnored
693 throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(message));
697 public void visitSuperExpression(PsiSuperExpression expression) {
698 if (LOG.isDebugEnabled()) {
699 LOG.debug("visitSuperExpression " + expression);
701 final int iterationCount = calcIterationCount(expression.getQualifier());
702 myResult = new SuperEvaluator(iterationCount);
706 public void visitThisExpression(PsiThisExpression expression) {
707 if (LOG.isDebugEnabled()) {
708 LOG.debug("visitThisExpression " + expression);
710 final int iterationCount = calcIterationCount(expression.getQualifier());
711 myResult = new ThisEvaluator(iterationCount);
714 private int calcIterationCount(final PsiJavaCodeReferenceElement qualifier) {
715 int iterationCount = 0;
716 if (qualifier != null) {
717 PsiElement targetClass = qualifier.resolve();
718 if (targetClass == null || getContextPsiClass() == null) {
719 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", qualifier.getText()));
722 PsiClass aClass = getContextPsiClass();
723 while (aClass != null && !aClass.equals(targetClass)) {
725 aClass = getOuterClass(aClass);
728 catch (Exception e) {
729 //noinspection ThrowableResultOfMethodCallIgnored
730 throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(e));
733 return iterationCount;
737 public void visitInstanceOfExpression(PsiInstanceOfExpression expression) {
738 if (LOG.isDebugEnabled()) {
739 LOG.debug("visitInstanceOfExpression " + expression);
741 PsiTypeElement checkType = expression.getCheckType();
742 if(checkType == null) {
743 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText()));
746 PsiType type = checkType.getType();
747 expression.getOperand().accept(this);
748 // ClassObjectEvaluator typeEvaluator = new ClassObjectEvaluator(type.getCanonicalText());
749 Evaluator operandEvaluator = myResult;
750 myResult = new InstanceofEvaluator(operandEvaluator, new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(type)));
754 public void visitParenthesizedExpression(PsiParenthesizedExpression expression) {
755 if (LOG.isDebugEnabled()) {
756 LOG.debug("visitParenthesizedExpression " + expression);
758 PsiExpression expr = expression.getExpression();
765 public void visitPostfixExpression(PsiPostfixExpression expression) {
766 if(expression.getType() == null) {
767 throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText()));
770 final PsiExpression operandExpression = expression.getOperand();
771 operandExpression.accept(this);
773 final Evaluator operandEvaluator = myResult;
775 final IElementType operation = expression.getOperationSign().getTokenType();
776 final PsiType operandType = operandExpression.getType();
777 @Nullable final PsiType unboxedOperandType = PsiPrimitiveType.getUnboxedType(operandType);
779 Evaluator incrementImpl = createBinaryEvaluator(
780 operandEvaluator, operandType,
781 new LiteralEvaluator(Integer.valueOf(1), "int"), PsiType.INT,
782 operation == JavaTokenType.PLUSPLUS ? JavaTokenType.PLUS : JavaTokenType.MINUS,
783 unboxedOperandType!= null? unboxedOperandType : operandType
785 if (unboxedOperandType != null) {
786 incrementImpl = new BoxingEvaluator(incrementImpl);
788 myResult = new PostfixOperationEvaluator(operandEvaluator, incrementImpl);
792 public void visitPrefixExpression(final PsiPrefixExpression expression) {
793 final PsiType expressionType = expression.getType();
794 if(expressionType == null) {
795 throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText()));
798 final PsiExpression operandExpression = expression.getOperand();
799 if (operandExpression == null) {
800 throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.operand", expression.getText()));
803 operandExpression.accept(this);
804 Evaluator operandEvaluator = myResult;
806 // handle unboxing issues
807 final PsiType operandType = operandExpression.getType();
809 final PsiType unboxedOperandType = PsiPrimitiveType.getUnboxedType(operandType);
811 final IElementType operation = expression.getOperationSign().getTokenType();
813 if(operation == JavaTokenType.PLUSPLUS || operation == JavaTokenType.MINUSMINUS) {
815 final BinaryExpressionEvaluator rightEval = createBinaryEvaluator(
816 operandEvaluator, operandType,
817 new LiteralEvaluator(Integer.valueOf(1), "int"), PsiType.INT,
818 operation == JavaTokenType.PLUSPLUS ? JavaTokenType.PLUS : JavaTokenType.MINUS,
819 unboxedOperandType!= null? unboxedOperandType : operandType
821 myResult = new AssignmentEvaluator(operandEvaluator, unboxedOperandType != null? new BoxingEvaluator(rightEval) : rightEval);
823 catch (IncorrectOperationException e) {
828 if (JavaTokenType.PLUS.equals(operation) || JavaTokenType.MINUS.equals(operation)|| JavaTokenType.TILDE.equals(operation)) {
829 operandEvaluator = handleUnaryNumericPromotion(operandType, operandEvaluator);
832 if (unboxedOperandType != null) {
833 operandEvaluator = new UnBoxingEvaluator(operandEvaluator);
836 myResult = new UnaryExpressionEvaluator(operation, expressionType.getCanonicalText(), operandEvaluator, expression.getOperationSign().getText());
841 public void visitMethodCallExpression(PsiMethodCallExpression expression) {
842 if (LOG.isDebugEnabled()) {
843 LOG.debug("visitMethodCallExpression " + expression);
845 final PsiExpressionList argumentList = expression.getArgumentList();
846 final PsiExpression[] argExpressions = argumentList.getExpressions();
847 List<Evaluator> argumentEvaluators = new ArrayList<Evaluator>(argExpressions.length);
848 // evaluate arguments
849 for (PsiExpression psiExpression : argExpressions) {
850 psiExpression.accept(this);
851 if (myResult == null) {
852 // cannot build evaluator
853 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", psiExpression.getText()));
855 argumentEvaluators.add(myResult);
857 PsiReferenceExpression methodExpr = expression.getMethodExpression();
859 final JavaResolveResult resolveResult = methodExpr.advancedResolve(false);
860 final PsiMethod psiMethod = (PsiMethod)resolveResult.getElement();
862 PsiExpression qualifier = methodExpr.getQualifierExpression();
863 Evaluator objectEvaluator;
864 JVMName contextClass = null;
866 if(psiMethod != null) {
867 PsiClass methodPsiClass = psiMethod.getContainingClass();
868 contextClass = JVMNameUtil.getJVMQualifiedName(methodPsiClass);
869 if (psiMethod.hasModifierProperty(PsiModifier.STATIC)) {
870 objectEvaluator = new TypeEvaluator(contextClass);
872 else if (qualifier != null ) {
873 qualifier.accept(this);
874 objectEvaluator = myResult;
877 int iterationCount = 0;
878 final PsiElement currentFileResolveScope = resolveResult.getCurrentFileResolveScope();
879 if (currentFileResolveScope instanceof PsiClass) {
880 PsiClass aClass = getContextPsiClass();
881 while(aClass != null && !aClass.equals(currentFileResolveScope)) {
882 aClass = getOuterClass(aClass);
886 objectEvaluator = new ThisEvaluator(iterationCount);
891 if (qualifier != null) {
892 PsiType type = qualifier.getType();
895 contextClass = JVMNameUtil.getJVMQualifiedName(type);
898 if (qualifier instanceof PsiReferenceExpression && ((PsiReferenceExpression)qualifier).resolve() instanceof PsiClass) {
899 // this is a call to a 'static' method
900 if (contextClass == null && type == null) {
901 throwEvaluateException(DebuggerBundle.message("evaluation.error.qualifier.type.unknown", qualifier.getText()));
903 assert contextClass != null;
904 objectEvaluator = new TypeEvaluator(contextClass);
907 qualifier.accept(this);
908 objectEvaluator = myResult;
912 objectEvaluator = new ThisEvaluator();
913 contextClass = JVMNameUtil.getContextClassJVMQualifiedName(myPosition);
914 if(contextClass == null && myContextPsiClass != null) {
915 contextClass = JVMNameUtil.getJVMQualifiedName(myContextPsiClass);
918 // throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(
919 // DebuggerBundle.message("evaluation.error.method.not.found", methodExpr.getReferenceName()))
925 if (objectEvaluator == null) {
926 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText()));
929 if (psiMethod != null && !psiMethod.isConstructor()) {
930 if (psiMethod.getReturnType() == null) {
931 throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.method.return.type", psiMethod.getText()));
935 if (psiMethod != null) {
937 final PsiParameter[] declaredParams = psiMethod.getParameterList().getParameters();
938 for (int i = 0, parametersLength = declaredParams.length; i < parametersLength; i++) {
939 if (i >= argExpressions.length) {
940 break; // actual arguments count is less than number of declared params
942 final PsiType declaredParamType = declaredParams[i].getType();
943 PsiType substituted = resolveResult.getSubstitutor().substitute(declaredParamType);
944 final PsiType actualArgType = argExpressions[i].getType();
945 if (TypeConversionUtil.boxingConversionApplicable(substituted, actualArgType)) {
946 final Evaluator argEval = argumentEvaluators.get(i);
947 argumentEvaluators.set(i, substituted instanceof PsiPrimitiveType
948 ? new UnBoxingEvaluator(argEval) : new BoxingEvaluator(argEval));
953 myResult = new MethodEvaluator(objectEvaluator, contextClass, methodExpr.getReferenceName(), psiMethod != null ? JVMNameUtil.getJVMSignature(psiMethod) : null, argumentEvaluators);
957 public void visitLiteralExpression(PsiLiteralExpression expression) {
958 Object value = expression.getValue();
959 if(expression.getParsingError() != null) {
960 throwEvaluateException(expression.getParsingError());
962 myResult = new LiteralEvaluator(value, expression.getType().getCanonicalText());
966 public void visitArrayAccessExpression(PsiArrayAccessExpression expression) {
967 final PsiExpression indexExpression = expression.getIndexExpression();
968 if(indexExpression == null) {
969 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
971 indexExpression.accept(this);
972 final Evaluator indexEvaluator = handleUnaryNumericPromotion(indexExpression.getType(), myResult);
974 expression.getArrayExpression().accept(this);
975 Evaluator arrayEvaluator = myResult;
976 myResult = new ArrayAccessEvaluator(arrayEvaluator, indexEvaluator);
981 * Handles unboxing and numeric promotion issues for
982 * - array diumention expressions
983 * - array index expression
984 * - unary +, -, and ~ operations
985 * @param operandExpressionType
986 * @param operandEvaluator @return operandEvaluator possibly 'wrapped' with neccesary unboxing and type-casting evaluators to make returning value
987 * sutable for mentioned contexts
989 private static Evaluator handleUnaryNumericPromotion(final PsiType operandExpressionType, Evaluator operandEvaluator) {
990 final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(operandExpressionType);
991 if (unboxedType != null && !PsiType.BOOLEAN.equals(unboxedType)) {
992 operandEvaluator = new UnBoxingEvaluator(operandEvaluator);
995 // handle numeric promotion
996 final PsiType _unboxedIndexType = unboxedType != null? unboxedType : operandExpressionType;
997 if (_unboxedIndexType instanceof PsiPrimitiveType) {
998 final PsiType promotionType = calcUnaryNumericPromotionType((PsiPrimitiveType)_unboxedIndexType);
999 if (promotionType != null) {
1000 operandEvaluator = new TypeCastEvaluator(operandEvaluator, promotionType.getCanonicalText(), true);
1003 return operandEvaluator;
1006 @SuppressWarnings({"ConstantConditions"})
1008 public void visitTypeCastExpression(PsiTypeCastExpression expression) {
1009 final PsiExpression operandExpr = expression.getOperand();
1010 operandExpr.accept(this);
1011 Evaluator operandEvaluator = myResult;
1012 final PsiType castType = expression.getCastType().getType();
1013 final PsiType operandType = operandExpr.getType();
1015 if (castType != null && operandType != null && !TypeConversionUtil.areTypesConvertible(operandType, castType)) {
1016 throw new EvaluateRuntimeException(
1017 new EvaluateException(JavaErrorMessages.message("inconvertible.type.cast", HighlightUtil.formatType(operandType), HighlightUtil.formatType(castType)))
1021 final boolean shouldPerformBoxingConversion = castType != null && operandType != null && TypeConversionUtil.boxingConversionApplicable(castType, operandType);
1022 final boolean castingToPrimitive = castType instanceof PsiPrimitiveType;
1023 if (shouldPerformBoxingConversion && castingToPrimitive) {
1024 operandEvaluator = new UnBoxingEvaluator(operandEvaluator);
1027 final boolean performCastToWrapperClass = shouldPerformBoxingConversion && !castingToPrimitive;
1029 String castTypeName = castType.getCanonicalText();
1030 if (performCastToWrapperClass) {
1031 final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(castType);
1032 if (unboxedType != null) {
1033 castTypeName = unboxedType.getCanonicalText();
1037 myResult = new TypeCastEvaluator(operandEvaluator, castTypeName, castingToPrimitive);
1039 if (performCastToWrapperClass) {
1040 myResult = new BoxingEvaluator(myResult);
1045 public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) {
1046 PsiType type = expression.getOperand().getType();
1048 if (type instanceof PsiPrimitiveType) {
1049 final JVMName typeName = JVMNameUtil.getJVMRawText(((PsiPrimitiveType)type).getBoxedTypeName());
1050 myResult = new FieldEvaluator(new TypeEvaluator(typeName), FieldEvaluator.TargetClassFilter.ALL, "TYPE");
1053 myResult = new ClassObjectEvaluator(new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(type)));
1058 public void visitNewExpression(PsiNewExpression expression) {
1059 PsiType expressionPsiType = expression.getType();
1060 if (expressionPsiType instanceof PsiArrayType) {
1061 Evaluator dimensionEvaluator = null;
1062 PsiExpression[] dimensions = expression.getArrayDimensions();
1063 if (dimensions.length == 1){
1064 PsiExpression dimensionExpression = dimensions[0];
1065 dimensionExpression.accept(this);
1066 if (myResult != null) {
1067 dimensionEvaluator = handleUnaryNumericPromotion(dimensionExpression.getType(), myResult);
1070 throwEvaluateException(
1071 DebuggerBundle.message("evaluation.error.invalid.array.dimension.expression", dimensionExpression.getText()));
1074 else if (dimensions.length > 1){
1075 throwEvaluateException(DebuggerBundle.message("evaluation.error.multi.dimensional.arrays.creation.not.supported"));
1078 Evaluator initializerEvaluator = null;
1079 PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer();
1080 if (arrayInitializer != null) {
1081 if (dimensionEvaluator != null) { // initializer already exists
1082 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText()));
1084 arrayInitializer.accept(this);
1085 if (myResult != null) {
1086 initializerEvaluator = handleUnaryNumericPromotion(arrayInitializer.getType(), myResult);
1089 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", arrayInitializer.getText()));
1092 PsiExpression[] initializers = arrayInitializer.getInitializers();
1093 initializerEvaluators = new Evaluator[initializers.length];
1094 for (int idx = 0; idx < initializers.length; idx++) {
1095 PsiExpression initializer = initializers[idx];
1096 initializer.accept(this);
1097 if (myResult instanceof Evaluator) {
1098 initializerEvaluators[idx] = myResult;
1101 throw new EvaluateException("Invalid expression for array initializer: " + initializer.getText(), true);
1106 if (dimensionEvaluator == null && initializerEvaluator == null) {
1107 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText()));
1109 myResult = new NewArrayInstanceEvaluator(
1110 new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(expressionPsiType)),
1112 initializerEvaluator
1115 else { // must be a class ref
1116 LOG.assertTrue(expressionPsiType instanceof PsiClassType);
1117 PsiClass aClass = ((PsiClassType)expressionPsiType).resolve();
1118 if(aClass instanceof PsiAnonymousClass) {
1119 throwEvaluateException(DebuggerBundle.message("evaluation.error.anonymous.class.evaluation.not.supported"));
1121 PsiExpressionList argumentList = expression.getArgumentList();
1122 if (argumentList == null) {
1123 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return;
1125 PsiExpression[] argExpressions = argumentList.getExpressions();
1126 PsiMethod constructor = expression.resolveConstructor();
1127 if (constructor == null && argExpressions.length > 0) {
1128 throw new EvaluateRuntimeException(new EvaluateException(
1129 DebuggerBundle.message("evaluation.error.cannot.resolve.constructor", expression.getText()), null));
1131 Evaluator[] argumentEvaluators = new Evaluator[argExpressions.length];
1132 // evaluate arguments
1133 for (int idx = 0; idx < argExpressions.length; idx++) {
1134 PsiExpression argExpression = argExpressions[idx];
1135 argExpression.accept(this);
1136 if (myResult != null) {
1137 argumentEvaluators[idx] = myResult;
1140 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", argExpression.getText()));
1143 //noinspection HardCodedStringLiteral
1144 JVMName signature = constructor != null ? JVMNameUtil.getJVMSignature(constructor) : JVMNameUtil.getJVMRawText("()V");
1145 myResult = new NewClassInstanceEvaluator(
1146 new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(expressionPsiType)),
1154 public void visitArrayInitializerExpression(PsiArrayInitializerExpression expression) {
1155 PsiExpression[] initializers = expression.getInitializers();
1156 Evaluator[] evaluators = new Evaluator[initializers.length];
1157 for (int idx = 0; idx < initializers.length; idx++) {
1158 PsiExpression initializer = initializers[idx];
1159 initializer.accept(this);
1160 if (myResult != null) {
1161 evaluators[idx] = handleUnaryNumericPromotion(initializer.getType(), myResult);
1164 throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", initializer.getText()));
1167 myResult = new ArrayInitializerEvaluator(evaluators);
1170 private static PsiClass getOuterClass(PsiClass aClass) {
1171 if(aClass == null) return null;
1172 return PsiTreeUtil.getContextOfType(aClass, PsiClass.class, true);
1175 private PsiClass getContainingClass(PsiVariable variable) {
1176 PsiElement element = PsiTreeUtil.getParentOfType(variable.getParent(), PsiClass.class, false);
1177 return element == null ? getContextPsiClass() : (PsiClass)element;
1180 public PsiClass getContextPsiClass() {
1181 return myContextPsiClass;
1184 protected ExpressionEvaluator buildElement(final PsiElement element) throws EvaluateException {
1185 LOG.assertTrue(element.isValid());
1187 myContextPsiClass = PsiTreeUtil.getContextOfType(element, PsiClass.class, false);
1189 element.accept(this);
1191 catch (EvaluateRuntimeException e) {
1194 if (myResult == null) {
1195 throw EvaluateExceptionUtil
1196 .createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", element.toString()));
1198 return new ExpressionEvaluatorImpl(myResult);