| /* |
| * Copyright 2000-2012 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * Class EvaluatorBuilderImpl |
| * @author Jeka |
| */ |
| package com.intellij.debugger.engine.evaluation.expression; |
| |
| import com.intellij.codeInsight.daemon.JavaErrorMessages; |
| import com.intellij.codeInsight.daemon.impl.HighlightInfo; |
| import com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil; |
| import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil; |
| import com.intellij.debugger.DebuggerBundle; |
| import com.intellij.debugger.SourcePosition; |
| import com.intellij.debugger.engine.ContextUtil; |
| import com.intellij.debugger.engine.DebuggerUtils; |
| import com.intellij.debugger.engine.JVMName; |
| import com.intellij.debugger.engine.JVMNameUtil; |
| import com.intellij.debugger.engine.evaluation.*; |
| import com.intellij.debugger.ui.DebuggerEditorImpl; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.JavaConstantExpressionEvaluator; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.util.PsiTypesUtil; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.psi.util.TypeConversionUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.sun.jdi.Value; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.util.*; |
| |
| public class EvaluatorBuilderImpl implements EvaluatorBuilder { |
| private static final EvaluatorBuilderImpl ourInstance = new EvaluatorBuilderImpl(); |
| |
| private EvaluatorBuilderImpl() { |
| } |
| |
| public static EvaluatorBuilder getInstance() { |
| return ourInstance; |
| } |
| |
| public static ExpressionEvaluator build(final TextWithImports text, @Nullable PsiElement contextElement, final SourcePosition position) throws EvaluateException { |
| if (contextElement == null) { |
| throw EvaluateExceptionUtil.CANNOT_FIND_SOURCE_CLASS; |
| } |
| |
| final Project project = contextElement.getProject(); |
| |
| CodeFragmentFactory factory = DebuggerEditorImpl.findAppropriateFactory(text, contextElement); |
| PsiCodeFragment codeFragment = new CodeFragmentFactoryContextWrapper(factory).createCodeFragment(text, contextElement, project); |
| if (codeFragment == null) { |
| throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", text.getText())); |
| } |
| codeFragment.forceResolveScope(GlobalSearchScope.allScope(project)); |
| DebuggerUtils.checkSyntax(codeFragment); |
| |
| return factory.getEvaluatorBuilder().build(codeFragment, position); |
| } |
| |
| @Override |
| public ExpressionEvaluator build(final PsiElement codeFragment, final SourcePosition position) throws EvaluateException { |
| return new Builder(position).buildElement(codeFragment); |
| } |
| |
| private static class Builder extends JavaElementVisitor { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl"); |
| private Evaluator myResult = null; |
| private PsiClass myContextPsiClass; |
| private CodeFragmentEvaluator myCurrentFragmentEvaluator; |
| private final Set<JavaCodeFragment> myVisitedFragments = new HashSet<JavaCodeFragment>(); |
| @Nullable |
| private final SourcePosition myPosition; |
| |
| private Builder(@Nullable SourcePosition position) { |
| myPosition = position; |
| } |
| |
| @Override |
| public void visitCodeFragment(JavaCodeFragment codeFragment) { |
| myVisitedFragments.add(codeFragment); |
| ArrayList<Evaluator> evaluators = new ArrayList<Evaluator>(); |
| |
| CodeFragmentEvaluator oldFragmentEvaluator = myCurrentFragmentEvaluator; |
| myCurrentFragmentEvaluator = new CodeFragmentEvaluator(oldFragmentEvaluator); |
| |
| for (PsiElement child = codeFragment.getFirstChild(); child != null; child = child.getNextSibling()) { |
| child.accept(this); |
| if(myResult != null) { |
| evaluators.add(myResult); |
| } |
| myResult = null; |
| } |
| |
| myCurrentFragmentEvaluator.setStatements(evaluators.toArray(new Evaluator[evaluators.size()])); |
| myResult = myCurrentFragmentEvaluator; |
| |
| myCurrentFragmentEvaluator = oldFragmentEvaluator; |
| } |
| |
| @Override |
| public void visitErrorElement(PsiErrorElement element) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", element.getText())); |
| } |
| |
| @Override |
| public void visitAssignmentExpression(PsiAssignmentExpression expression) { |
| final PsiExpression rExpression = expression.getRExpression(); |
| if(rExpression == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return; |
| } |
| |
| rExpression.accept(this); |
| Evaluator rEvaluator = myResult; |
| |
| if(expression.getOperationTokenType() != JavaTokenType.EQ) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.operation.not.supported", expression.getOperationSign().getText())); |
| } |
| |
| final PsiExpression lExpression = expression.getLExpression(); |
| |
| final PsiType lType = lExpression.getType(); |
| if(lType == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", lExpression.getText())); |
| } |
| |
| if(!TypeConversionUtil.areTypesAssignmentCompatible(lType, rExpression) && rExpression.getType() != null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.incompatible.types", expression.getOperationSign().getText())); |
| } |
| lExpression.accept(this); |
| Evaluator lEvaluator = myResult; |
| |
| rEvaluator = handleAssignmentBoxingAndPrimitiveTypeConversions(lType, rExpression.getType(), rEvaluator); |
| |
| myResult = new AssignmentEvaluator(lEvaluator, rEvaluator); |
| } |
| |
| // returns rEvaluator possibly wrapped with boxing/unboxing and casting evaluators |
| private static Evaluator handleAssignmentBoxingAndPrimitiveTypeConversions(PsiType lType, PsiType rType, Evaluator rEvaluator) { |
| final PsiType unboxedLType = PsiPrimitiveType.getUnboxedType(lType); |
| |
| if (unboxedLType != null) { |
| if (rType instanceof PsiPrimitiveType && !PsiType.NULL.equals(rType)) { |
| if (!rType.equals(unboxedLType)) { |
| rEvaluator = new TypeCastEvaluator(rEvaluator, unboxedLType.getCanonicalText(), true); |
| } |
| rEvaluator = new BoxingEvaluator(rEvaluator); |
| } |
| } |
| else { |
| // either primitive type or not unboxable type |
| if (lType instanceof PsiPrimitiveType) { |
| if (rType instanceof PsiClassType) { |
| rEvaluator = new UnBoxingEvaluator(rEvaluator); |
| } |
| final PsiPrimitiveType unboxedRType = PsiPrimitiveType.getUnboxedType(rType); |
| final PsiType _rType = unboxedRType != null? unboxedRType : rType; |
| if (_rType instanceof PsiPrimitiveType && !PsiType.NULL.equals(_rType)) { |
| if (!lType.equals(_rType)) { |
| rEvaluator = new TypeCastEvaluator(rEvaluator, lType.getCanonicalText(), true); |
| } |
| } |
| } |
| } |
| return rEvaluator; |
| } |
| |
| @Override |
| public void visitStatement(PsiStatement statement) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.statement.not.supported", statement.getText())); |
| } |
| |
| @Override |
| public void visitBlockStatement(PsiBlockStatement statement) { |
| PsiStatement[] statements = statement.getCodeBlock().getStatements(); |
| Evaluator [] evaluators = new Evaluator[statements.length]; |
| for (int i = 0; i < statements.length; i++) { |
| PsiStatement psiStatement = statements[i]; |
| psiStatement.accept(this); |
| evaluators[i] = new DisableGC(myResult); |
| myResult = null; |
| } |
| myResult = new BlockStatementEvaluator(evaluators); |
| } |
| |
| @Override |
| public void visitWhileStatement(PsiWhileStatement statement) { |
| PsiStatement body = statement.getBody(); |
| if(body == null) return; |
| body.accept(this); |
| Evaluator bodyEvaluator = myResult; |
| |
| PsiExpression condition = statement.getCondition(); |
| if(condition == null) return; |
| condition.accept(this); |
| String label = null; |
| if(statement.getParent() instanceof PsiLabeledStatement) { |
| label = ((PsiLabeledStatement)statement.getParent()).getLabelIdentifier().getText(); |
| } |
| myResult = new WhileStatementEvaluator(myResult, bodyEvaluator, label); |
| } |
| |
| @Override |
| public void visitForStatement(PsiForStatement statement) { |
| PsiStatement initializer = statement.getInitialization(); |
| Evaluator initializerEvaluator = null; |
| if(initializer != null){ |
| initializer.accept(this); |
| initializerEvaluator = myResult; |
| } |
| |
| PsiExpression condition = statement.getCondition(); |
| Evaluator conditionEvaluator = null; |
| if(condition != null) { |
| condition.accept(this); |
| conditionEvaluator = myResult; |
| } |
| |
| PsiStatement update = statement.getUpdate(); |
| Evaluator updateEvaluator = null; |
| if(update != null){ |
| update.accept(this); |
| updateEvaluator = myResult; |
| } |
| |
| PsiStatement body = statement.getBody(); |
| if(body == null) return; |
| body.accept(this); |
| Evaluator bodyEvaluator = myResult; |
| |
| String label = null; |
| if(statement.getParent() instanceof PsiLabeledStatement) { |
| label = ((PsiLabeledStatement)statement.getParent()).getLabelIdentifier().getText(); |
| } |
| myResult = new ForStatementEvaluator(initializerEvaluator, conditionEvaluator, updateEvaluator, bodyEvaluator, label); |
| } |
| |
| @Override |
| public void visitIfStatement(PsiIfStatement statement) { |
| PsiStatement thenBranch = statement.getThenBranch(); |
| if(thenBranch == null) return; |
| thenBranch.accept(this); |
| Evaluator thenEvaluator = myResult; |
| |
| PsiStatement elseBranch = statement.getElseBranch(); |
| Evaluator elseEvaluator = null; |
| if(elseBranch != null){ |
| elseBranch.accept(this); |
| elseEvaluator = myResult; |
| } |
| |
| PsiExpression condition = statement.getCondition(); |
| if(condition == null) return; |
| condition.accept(this); |
| |
| myResult = new IfStatementEvaluator(myResult, thenEvaluator, elseEvaluator); |
| } |
| |
| @Override |
| public void visitBreakStatement(PsiBreakStatement statement) { |
| PsiIdentifier labelIdentifier = statement.getLabelIdentifier(); |
| myResult = BreakContinueStatementEvaluator.createBreakEvaluator(labelIdentifier != null ? labelIdentifier.getText() : null); |
| } |
| |
| @Override |
| public void visitContinueStatement(PsiContinueStatement statement) { |
| PsiIdentifier labelIdentifier = statement.getLabelIdentifier(); |
| myResult = BreakContinueStatementEvaluator.createContinueEvaluator(labelIdentifier != null ? labelIdentifier.getText() : null); |
| } |
| |
| @Override |
| public void visitExpressionStatement(PsiExpressionStatement statement) { |
| statement.getExpression().accept(this); |
| } |
| |
| @Override |
| public void visitExpression(PsiExpression expression) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("visitExpression " + expression); |
| } |
| } |
| |
| @Override |
| public void visitPolyadicExpression(PsiPolyadicExpression wideExpression) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("visitPolyadicExpression " + wideExpression); |
| } |
| PsiExpression[] operands = wideExpression.getOperands(); |
| operands[0].accept(this); |
| Evaluator result = myResult; |
| PsiType lType = operands[0].getType(); |
| for (int i = 1; i < operands.length; i++) { |
| PsiExpression expression = operands[i]; |
| if (expression == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", wideExpression.getText())); |
| return; |
| } |
| expression.accept(this); |
| Evaluator rResult = myResult; |
| IElementType opType = wideExpression.getOperationTokenType(); |
| PsiType rType = expression.getType(); |
| if (rType == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText())); |
| } |
| final PsiType typeForBinOp = TypeConversionUtil.calcTypeForBinaryExpression(lType, rType, opType, true); |
| if (typeForBinOp == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", wideExpression.getText())); |
| } |
| myResult = createBinaryEvaluator(result, lType, rResult, rType, opType, typeForBinOp); |
| lType = typeForBinOp; |
| result = myResult; |
| } |
| } |
| |
| // constructs binary evaluator handling unboxing and numeric promotion issues |
| private static BinaryExpressionEvaluator createBinaryEvaluator(Evaluator lResult, |
| PsiType lType, |
| Evaluator rResult, |
| @NotNull PsiType rType, |
| @NotNull IElementType operation, |
| @NotNull PsiType expressionExpectedType) { |
| // handle unboxing if necessary |
| if (isUnboxingInBinaryExpressionApplicable(lType, rType, operation)) { |
| if (rType instanceof PsiClassType && UnBoxingEvaluator.isTypeUnboxable(rType.getCanonicalText())) { |
| rResult = new UnBoxingEvaluator(rResult); |
| } |
| if (lType instanceof PsiClassType && UnBoxingEvaluator.isTypeUnboxable(lType.getCanonicalText())) { |
| lResult = new UnBoxingEvaluator(lResult); |
| } |
| } |
| if (isBinaryNumericPromotionApplicable(lType, rType, operation)) { |
| PsiType _lType = lType; |
| final PsiPrimitiveType unboxedLType = PsiPrimitiveType.getUnboxedType(lType); |
| if (unboxedLType != null) { |
| _lType = unboxedLType; |
| } |
| |
| PsiType _rType = rType; |
| final PsiPrimitiveType unboxedRType = PsiPrimitiveType.getUnboxedType(rType); |
| if (unboxedRType != null) { |
| _rType = unboxedRType; |
| } |
| |
| // handle numeric promotion |
| if (PsiType.DOUBLE.equals(_lType)) { |
| if (TypeConversionUtil.areTypesConvertible(_rType, PsiType.DOUBLE)) { |
| rResult = new TypeCastEvaluator(rResult, PsiType.DOUBLE.getCanonicalText(), true); |
| } |
| } |
| else if (PsiType.DOUBLE.equals(_rType)) { |
| if (TypeConversionUtil.areTypesConvertible(_lType, PsiType.DOUBLE)) { |
| lResult = new TypeCastEvaluator(lResult, PsiType.DOUBLE.getCanonicalText(), true); |
| } |
| } |
| else if (PsiType.FLOAT.equals(_lType)) { |
| if (TypeConversionUtil.areTypesConvertible(_rType, PsiType.FLOAT)) { |
| rResult = new TypeCastEvaluator(rResult, PsiType.FLOAT.getCanonicalText(), true); |
| } |
| } |
| else if (PsiType.FLOAT.equals(_rType)) { |
| if (TypeConversionUtil.areTypesConvertible(_lType, PsiType.FLOAT)) { |
| lResult = new TypeCastEvaluator(lResult, PsiType.FLOAT.getCanonicalText(), true); |
| } |
| } |
| else if (PsiType.LONG.equals(_lType)) { |
| if (TypeConversionUtil.areTypesConvertible(_rType, PsiType.LONG)) { |
| rResult = new TypeCastEvaluator(rResult, PsiType.LONG.getCanonicalText(), true); |
| } |
| } |
| else if (PsiType.LONG.equals(_rType)) { |
| if (TypeConversionUtil.areTypesConvertible(_lType, PsiType.LONG)) { |
| lResult = new TypeCastEvaluator(lResult, PsiType.LONG.getCanonicalText(), true); |
| } |
| } |
| else { |
| if (!PsiType.INT.equals(_lType) && TypeConversionUtil.areTypesConvertible(_lType, PsiType.INT)) { |
| lResult = new TypeCastEvaluator(lResult, PsiType.INT.getCanonicalText(), true); |
| } |
| if (!PsiType.INT.equals(_rType) && TypeConversionUtil.areTypesConvertible(_rType, PsiType.INT)) { |
| rResult = new TypeCastEvaluator(rResult, PsiType.INT.getCanonicalText(), true); |
| } |
| } |
| } |
| |
| return new BinaryExpressionEvaluator(lResult, rResult, operation, expressionExpectedType.getCanonicalText()); |
| } |
| |
| private static boolean isBinaryNumericPromotionApplicable(PsiType lType, PsiType rType, IElementType opType) { |
| if (lType == null || rType == null) { |
| return false; |
| } |
| if (!TypeConversionUtil.isNumericType(lType) || !TypeConversionUtil.isNumericType(rType)) { |
| return false; |
| } |
| if (opType == JavaTokenType.EQEQ || opType == JavaTokenType.NE) { |
| if (PsiType.NULL.equals(lType) || PsiType.NULL.equals(rType)) { |
| return false; |
| } |
| if (lType instanceof PsiClassType && rType instanceof PsiClassType) { |
| return false; |
| } |
| if (lType instanceof PsiClassType) { |
| return PsiPrimitiveType.getUnboxedType(lType) != null; // should be unboxable |
| } |
| if (rType instanceof PsiClassType) { |
| return PsiPrimitiveType.getUnboxedType(rType) != null; // should be unboxable |
| } |
| return true; |
| } |
| |
| return opType == JavaTokenType.ASTERISK || |
| opType == JavaTokenType.DIV || |
| opType == JavaTokenType.PERC || |
| opType == JavaTokenType.PLUS || |
| opType == JavaTokenType.MINUS || |
| opType == JavaTokenType.LT || |
| opType == JavaTokenType.LE || |
| opType == JavaTokenType.GT || |
| opType == JavaTokenType.GE || |
| opType == JavaTokenType.AND || |
| opType == JavaTokenType.XOR || |
| opType == JavaTokenType.OR; |
| |
| } |
| |
| private static boolean isUnboxingInBinaryExpressionApplicable(PsiType lType, PsiType rType, IElementType opCode) { |
| if (PsiType.NULL.equals(lType) || PsiType.NULL.equals(rType)) { |
| return false; |
| } |
| // handle '==' and '!=' separately |
| if (opCode == JavaTokenType.EQEQ || opCode == JavaTokenType.NE) { |
| return lType instanceof PsiPrimitiveType && rType instanceof PsiClassType || |
| lType instanceof PsiClassType && rType instanceof PsiPrimitiveType; |
| } |
| // all other operations at least one should be of class type |
| return lType instanceof PsiClassType || rType instanceof PsiClassType; |
| } |
| |
| /** |
| * @param type |
| * @return promotion type to cast to or null if no casting needed |
| */ |
| @Nullable |
| private static PsiType calcUnaryNumericPromotionType(PsiPrimitiveType type) { |
| if (PsiType.BYTE.equals(type) || PsiType.SHORT.equals(type) || PsiType.CHAR.equals(type) || PsiType.INT.equals(type)) { |
| return PsiType.INT; |
| } |
| return null; |
| } |
| |
| @Override |
| public void visitDeclarationStatement(PsiDeclarationStatement statement) { |
| List<Evaluator> evaluators = new ArrayList<Evaluator>(); |
| |
| PsiElement[] declaredElements = statement.getDeclaredElements(); |
| for (PsiElement declaredElement : declaredElements) { |
| if (declaredElement instanceof PsiLocalVariable) { |
| if (myCurrentFragmentEvaluator != null) { |
| final PsiLocalVariable localVariable = (PsiLocalVariable)declaredElement; |
| |
| final PsiType lType = localVariable.getType(); |
| |
| PsiElementFactory elementFactory = JavaPsiFacade.getInstance(localVariable.getProject()).getElementFactory(); |
| try { |
| PsiExpression initialValue = elementFactory.createExpressionFromText(PsiTypesUtil.getDefaultValueOfType(lType), null); |
| Object value = JavaConstantExpressionEvaluator.computeConstantExpression(initialValue, true); |
| myCurrentFragmentEvaluator.setInitialValue(localVariable.getName(), value); |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| catch (EvaluateException e) { |
| throw new EvaluateRuntimeException(e); |
| } |
| |
| PsiExpression initializer = localVariable.getInitializer(); |
| if (initializer != null) { |
| try { |
| if (!TypeConversionUtil.areTypesAssignmentCompatible(lType, initializer)) { |
| throwEvaluateException( |
| DebuggerBundle.message("evaluation.error.incompatible.variable.initializer.type", localVariable.getName())); |
| } |
| final PsiType rType = initializer.getType(); |
| initializer.accept(this); |
| Evaluator rEvaluator = myResult; |
| |
| PsiExpression localVarReference = elementFactory.createExpressionFromText(localVariable.getName(), initializer); |
| |
| localVarReference.accept(this); |
| Evaluator lEvaluator = myResult; |
| rEvaluator = handleAssignmentBoxingAndPrimitiveTypeConversions(localVarReference.getType(), rType, rEvaluator); |
| |
| Evaluator assignment = new AssignmentEvaluator(lEvaluator, rEvaluator); |
| evaluators.add(assignment); |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| } |
| } |
| else { |
| throw new EvaluateRuntimeException(new EvaluateException( |
| DebuggerBundle.message("evaluation.error.local.variable.declarations.not.supported"), null)); |
| } |
| } |
| else { |
| throw new EvaluateRuntimeException(new EvaluateException( |
| DebuggerBundle.message("evaluation.error.unsupported.declaration", declaredElement.getText()), null)); |
| } |
| } |
| |
| if(!evaluators.isEmpty()) { |
| CodeFragmentEvaluator codeFragmentEvaluator = new CodeFragmentEvaluator(myCurrentFragmentEvaluator); |
| codeFragmentEvaluator.setStatements(evaluators.toArray(new Evaluator[evaluators.size()])); |
| myResult = codeFragmentEvaluator; |
| } else { |
| myResult = null; |
| } |
| } |
| |
| @Override |
| public void visitConditionalExpression(PsiConditionalExpression expression) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("visitConditionalExpression " + expression); |
| } |
| final PsiExpression thenExpression = expression.getThenExpression(); |
| final PsiExpression elseExpression = expression.getElseExpression(); |
| if (thenExpression == null || elseExpression == null){ |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return; |
| } |
| PsiExpression condition = expression.getCondition(); |
| condition.accept(this); |
| if (myResult == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", condition.getText())); return; |
| } |
| Evaluator conditionEvaluator = myResult; |
| thenExpression.accept(this); |
| if (myResult == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", thenExpression.getText())); return; |
| } |
| Evaluator thenEvaluator = myResult; |
| elseExpression.accept(this); |
| if (myResult == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", elseExpression.getText())); return; |
| } |
| Evaluator elseEvaluator = myResult; |
| myResult = new ConditionalExpressionEvaluator(conditionEvaluator, thenEvaluator, elseEvaluator); |
| } |
| |
| @Override |
| public void visitReferenceExpression(PsiReferenceExpression expression) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("visitReferenceExpression " + expression); |
| } |
| PsiExpression qualifier = expression.getQualifierExpression(); |
| JavaResolveResult resolveResult = expression.advancedResolve(true); |
| PsiElement element = resolveResult.getElement(); |
| |
| if (element instanceof PsiLocalVariable || element instanceof PsiParameter) { |
| final Value labeledValue = element.getUserData(CodeFragmentFactoryContextWrapper.LABEL_VARIABLE_VALUE_KEY); |
| if (labeledValue != null) { |
| myResult = new IdentityEvaluator(labeledValue); |
| return; |
| } |
| //synthetic variable |
| final PsiFile containingFile = element.getContainingFile(); |
| if(containingFile instanceof PsiCodeFragment && myCurrentFragmentEvaluator != null && myVisitedFragments.contains(containingFile)) { |
| // psiVariable may live in PsiCodeFragment not only in debugger editors, for example Fabrique has such variables. |
| // So treat it as synthetic var only when this code fragment is located in DebuggerEditor, |
| // that's why we need to check that containing code fragment is the one we visited |
| myResult = new SyntheticVariableEvaluator(myCurrentFragmentEvaluator, ((PsiVariable)element).getName()); |
| return; |
| } |
| // local variable |
| final PsiVariable psiVar = (PsiVariable)element; |
| final String localName = psiVar.getName(); |
| PsiClass variableClass = getContainingClass(psiVar); |
| if (getContextPsiClass() == null || getContextPsiClass().equals(variableClass)) { |
| final LocalVariableEvaluator localVarEvaluator = new LocalVariableEvaluator(localName, ContextUtil.isJspImplicit(element)); |
| if (psiVar instanceof PsiParameter) { |
| final PsiParameter param = (PsiParameter)psiVar; |
| final PsiParameterList paramList = PsiTreeUtil.getParentOfType(param, PsiParameterList.class, true); |
| if (paramList != null) { |
| localVarEvaluator.setParameterIndex(paramList.getParameterIndex(param)); |
| } |
| } |
| myResult = localVarEvaluator; |
| return; |
| } |
| // the expression references final var outside the context's class (in some of the outer classes) |
| int iterationCount = 0; |
| PsiClass aClass = getOuterClass(getContextPsiClass()); |
| while (aClass != null && !aClass.equals(variableClass)) { |
| iterationCount++; |
| aClass = getOuterClass(aClass); |
| } |
| if (aClass != null) { |
| PsiExpression initializer = psiVar.getInitializer(); |
| if(initializer != null) { |
| Object value = JavaPsiFacade.getInstance(psiVar.getProject()).getConstantEvaluationHelper().computeConstantExpression(initializer); |
| if(value != null) { |
| PsiType type = resolveResult.getSubstitutor().substitute(psiVar.getType()); |
| myResult = new LiteralEvaluator(value, type.getCanonicalText()); |
| return; |
| } |
| } |
| Evaluator objectEvaluator = new ThisEvaluator(iterationCount); |
| //noinspection HardCodedStringLiteral |
| final PsiClass classAt = myPosition != null? JVMNameUtil.getClassAt(myPosition) : null; |
| FieldEvaluator.TargetClassFilter filter = FieldEvaluator.createClassFilter(classAt != null? classAt : getContextPsiClass()); |
| myResult = new FieldEvaluator(objectEvaluator, filter, "val$" + localName); |
| return; |
| } |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.local.variable.missing.from.class.closure", localName)); |
| } |
| else if (element instanceof PsiField) { |
| final PsiField psiField = (PsiField)element; |
| final PsiClass fieldClass = psiField.getContainingClass(); |
| if(fieldClass == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.cannot.resolve.field.class", psiField.getName())); return; |
| } |
| Evaluator objectEvaluator; |
| if (psiField.hasModifierProperty(PsiModifier.STATIC)) { |
| objectEvaluator = new TypeEvaluator(JVMNameUtil.getContextClassJVMQualifiedName(SourcePosition.createFromElement(psiField))); |
| } |
| else if(qualifier != null) { |
| qualifier.accept(this); |
| objectEvaluator = myResult; |
| } |
| else if (fieldClass.equals(getContextPsiClass()) || getContextPsiClass().isInheritor(fieldClass, true)) { |
| objectEvaluator = new ThisEvaluator(); |
| } |
| else { // myContextPsiClass != fieldClass && myContextPsiClass is not a subclass of fieldClass |
| int iterationCount = 0; |
| PsiClass aClass = getContextPsiClass(); |
| while (aClass != null && !(aClass.equals(fieldClass) || aClass.isInheritor(fieldClass, true))) { |
| iterationCount++; |
| aClass = getOuterClass(aClass); |
| } |
| if (aClass == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.cannot.sources.for.field.class", psiField.getName())); |
| } |
| objectEvaluator = new ThisEvaluator(iterationCount); |
| } |
| myResult = new FieldEvaluator(objectEvaluator, FieldEvaluator.createClassFilter(fieldClass), psiField.getName()); |
| } |
| else { |
| //let's guess what this could be |
| PsiElement nameElement = expression.getReferenceNameElement(); // get "b" part |
| String name; |
| if (nameElement instanceof PsiIdentifier) { |
| name = nameElement.getText(); |
| } |
| else { |
| //noinspection HardCodedStringLiteral |
| final String elementDisplayString = nameElement != null ? nameElement.getText() : "(null)"; |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.identifier.expected", elementDisplayString)); |
| return; |
| } |
| |
| if(qualifier != null) { |
| final PsiElement qualifierTarget = qualifier instanceof PsiReferenceExpression |
| ? ((PsiReferenceExpression)qualifier).resolve() : null; |
| if (qualifierTarget instanceof PsiClass) { |
| // this is a call to a 'static' field |
| PsiClass psiClass = (PsiClass)qualifierTarget; |
| final JVMName typeName = JVMNameUtil.getJVMQualifiedName(psiClass); |
| myResult = new FieldEvaluator(new TypeEvaluator(typeName), FieldEvaluator.createClassFilter(psiClass), name); |
| } |
| else { |
| PsiType type = qualifier.getType(); |
| if(type == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.qualifier.type.unknown", qualifier.getText())); |
| } |
| |
| qualifier.accept(this); |
| if (myResult == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.cannot.evaluate.qualifier", qualifier.getText())); |
| } |
| |
| myResult = new FieldEvaluator(myResult, FieldEvaluator.createClassFilter(type), name); |
| } |
| } |
| else { |
| myResult = new LocalVariableEvaluator(name, false); |
| } |
| } |
| } |
| |
| private static void throwEvaluateException(String message) throws EvaluateRuntimeException { |
| //noinspection ThrowableResultOfMethodCallIgnored |
| throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(message)); |
| } |
| |
| @Override |
| public void visitSuperExpression(PsiSuperExpression expression) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("visitSuperExpression " + expression); |
| } |
| final int iterationCount = calcIterationCount(expression.getQualifier()); |
| myResult = new SuperEvaluator(iterationCount); |
| } |
| |
| @Override |
| public void visitThisExpression(PsiThisExpression expression) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("visitThisExpression " + expression); |
| } |
| final int iterationCount = calcIterationCount(expression.getQualifier()); |
| myResult = new ThisEvaluator(iterationCount); |
| } |
| |
| private int calcIterationCount(final PsiJavaCodeReferenceElement qualifier) { |
| int iterationCount = 0; |
| if (qualifier != null) { |
| PsiElement targetClass = qualifier.resolve(); |
| if (targetClass == null || getContextPsiClass() == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", qualifier.getText())); |
| } |
| try { |
| PsiClass aClass = getContextPsiClass(); |
| while (aClass != null && !aClass.equals(targetClass)) { |
| iterationCount++; |
| aClass = getOuterClass(aClass); |
| } |
| } |
| catch (Exception e) { |
| //noinspection ThrowableResultOfMethodCallIgnored |
| throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException(e)); |
| } |
| } |
| return iterationCount; |
| } |
| |
| @Override |
| public void visitInstanceOfExpression(PsiInstanceOfExpression expression) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("visitInstanceOfExpression " + expression); |
| } |
| PsiTypeElement checkType = expression.getCheckType(); |
| if(checkType == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); |
| return; |
| } |
| PsiType type = checkType.getType(); |
| expression.getOperand().accept(this); |
| // ClassObjectEvaluator typeEvaluator = new ClassObjectEvaluator(type.getCanonicalText()); |
| Evaluator operandEvaluator = myResult; |
| myResult = new InstanceofEvaluator(operandEvaluator, new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(type))); |
| } |
| |
| @Override |
| public void visitParenthesizedExpression(PsiParenthesizedExpression expression) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("visitParenthesizedExpression " + expression); |
| } |
| PsiExpression expr = expression.getExpression(); |
| if (expr != null){ |
| expr.accept(this); |
| } |
| } |
| |
| @Override |
| public void visitPostfixExpression(PsiPostfixExpression expression) { |
| if(expression.getType() == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText())); |
| } |
| |
| final PsiExpression operandExpression = expression.getOperand(); |
| operandExpression.accept(this); |
| |
| final Evaluator operandEvaluator = myResult; |
| |
| final IElementType operation = expression.getOperationTokenType(); |
| final PsiType operandType = operandExpression.getType(); |
| @Nullable final PsiType unboxedOperandType = PsiPrimitiveType.getUnboxedType(operandType); |
| |
| Evaluator incrementImpl = createBinaryEvaluator( |
| operandEvaluator, operandType, |
| new LiteralEvaluator(Integer.valueOf(1), "int"), PsiType.INT, |
| operation == JavaTokenType.PLUSPLUS ? JavaTokenType.PLUS : JavaTokenType.MINUS, |
| unboxedOperandType!= null? unboxedOperandType : operandType |
| ); |
| if (unboxedOperandType != null) { |
| incrementImpl = new BoxingEvaluator(incrementImpl); |
| } |
| myResult = new PostfixOperationEvaluator(operandEvaluator, incrementImpl); |
| } |
| |
| @Override |
| public void visitPrefixExpression(final PsiPrefixExpression expression) { |
| final PsiType expressionType = expression.getType(); |
| if(expressionType == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.type", expression.getText())); |
| } |
| |
| final PsiExpression operandExpression = expression.getOperand(); |
| if (operandExpression == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.expression.operand", expression.getText())); |
| } |
| |
| operandExpression.accept(this); |
| Evaluator operandEvaluator = myResult; |
| |
| // handle unboxing issues |
| final PsiType operandType = operandExpression.getType(); |
| @Nullable |
| final PsiType unboxedOperandType = PsiPrimitiveType.getUnboxedType(operandType); |
| |
| final IElementType operation = expression.getOperationTokenType(); |
| |
| if(operation == JavaTokenType.PLUSPLUS || operation == JavaTokenType.MINUSMINUS) { |
| try { |
| final BinaryExpressionEvaluator rightEval = createBinaryEvaluator( |
| operandEvaluator, operandType, |
| new LiteralEvaluator(Integer.valueOf(1), "int"), PsiType.INT, |
| operation == JavaTokenType.PLUSPLUS ? JavaTokenType.PLUS : JavaTokenType.MINUS, |
| unboxedOperandType!= null? unboxedOperandType : operandType |
| ); |
| myResult = new AssignmentEvaluator(operandEvaluator, unboxedOperandType != null? new BoxingEvaluator(rightEval) : rightEval); |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| } |
| else { |
| if (JavaTokenType.PLUS.equals(operation) || JavaTokenType.MINUS.equals(operation)|| JavaTokenType.TILDE.equals(operation)) { |
| operandEvaluator = handleUnaryNumericPromotion(operandType, operandEvaluator); |
| } |
| else { |
| if (unboxedOperandType != null) { |
| operandEvaluator = new UnBoxingEvaluator(operandEvaluator); |
| } |
| } |
| myResult = new UnaryExpressionEvaluator(operation, expressionType.getCanonicalText(), operandEvaluator, expression.getOperationSign().getText()); |
| } |
| } |
| |
| @Override |
| public void visitMethodCallExpression(PsiMethodCallExpression expression) { |
| if (LOG.isDebugEnabled()) { |
| LOG.debug("visitMethodCallExpression " + expression); |
| } |
| final PsiExpressionList argumentList = expression.getArgumentList(); |
| final PsiExpression[] argExpressions = argumentList.getExpressions(); |
| Evaluator[] argumentEvaluators = new Evaluator[argExpressions.length]; |
| // evaluate arguments |
| for (int idx = 0; idx < argExpressions.length; idx++) { |
| final PsiExpression psiExpression = argExpressions[idx]; |
| psiExpression.accept(this); |
| if (myResult == null) { |
| // cannot build evaluator |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", psiExpression.getText())); |
| } |
| argumentEvaluators[idx] = new DisableGC(myResult); |
| } |
| PsiReferenceExpression methodExpr = expression.getMethodExpression(); |
| |
| final JavaResolveResult resolveResult = methodExpr.advancedResolve(false); |
| final PsiMethod psiMethod = (PsiMethod)resolveResult.getElement(); |
| |
| PsiExpression qualifier = methodExpr.getQualifierExpression(); |
| Evaluator objectEvaluator; |
| JVMName contextClass = null; |
| |
| if(psiMethod != null) { |
| PsiClass methodPsiClass = psiMethod.getContainingClass(); |
| contextClass = JVMNameUtil.getJVMQualifiedName(methodPsiClass); |
| if (psiMethod.hasModifierProperty(PsiModifier.STATIC)) { |
| objectEvaluator = new TypeEvaluator(contextClass); |
| } |
| else if (qualifier != null ) { |
| qualifier.accept(this); |
| objectEvaluator = myResult; |
| } |
| else { |
| int iterationCount = 0; |
| final PsiElement currentFileResolveScope = resolveResult.getCurrentFileResolveScope(); |
| if (currentFileResolveScope instanceof PsiClass) { |
| PsiClass aClass = getContextPsiClass(); |
| while(aClass != null && !aClass.equals(currentFileResolveScope)) { |
| aClass = getOuterClass(aClass); |
| iterationCount++; |
| } |
| } |
| objectEvaluator = new ThisEvaluator(iterationCount); |
| } |
| } |
| else { |
| //trying to guess |
| if (qualifier != null) { |
| PsiType type = qualifier.getType(); |
| |
| if (type != null) { |
| contextClass = JVMNameUtil.getJVMQualifiedName(type); |
| } |
| |
| if (qualifier instanceof PsiReferenceExpression && ((PsiReferenceExpression)qualifier).resolve() instanceof PsiClass) { |
| // this is a call to a 'static' method |
| if (contextClass == null && type == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.qualifier.type.unknown", qualifier.getText())); |
| } |
| assert contextClass != null; |
| objectEvaluator = new TypeEvaluator(contextClass); |
| } |
| else { |
| qualifier.accept(this); |
| objectEvaluator = myResult; |
| } |
| } |
| else { |
| objectEvaluator = new ThisEvaluator(); |
| contextClass = JVMNameUtil.getContextClassJVMQualifiedName(myPosition); |
| if(contextClass == null && myContextPsiClass != null) { |
| contextClass = JVMNameUtil.getJVMQualifiedName(myContextPsiClass); |
| } |
| //else { |
| // throw new EvaluateRuntimeException(EvaluateExceptionUtil.createEvaluateException( |
| // DebuggerBundle.message("evaluation.error.method.not.found", methodExpr.getReferenceName())) |
| // ); |
| //} |
| } |
| } |
| |
| if (objectEvaluator == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); |
| } |
| |
| if (psiMethod != null && !psiMethod.isConstructor()) { |
| if (psiMethod.getReturnType() == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.unknown.method.return.type", psiMethod.getText())); |
| } |
| } |
| |
| if (psiMethod != null) { |
| processBoxingConversions(psiMethod.getParameterList().getParameters(), argExpressions, resolveResult.getSubstitutor(), argumentEvaluators); |
| argumentEvaluators = wrapVarargs(psiMethod.getParameterList().getParameters(), argExpressions, resolveResult.getSubstitutor(), argumentEvaluators); |
| } |
| |
| myResult = new MethodEvaluator(objectEvaluator, contextClass, methodExpr.getReferenceName(), psiMethod != null ? JVMNameUtil.getJVMSignature(psiMethod) : null, argumentEvaluators); |
| } |
| |
| @Override |
| public void visitLiteralExpression(PsiLiteralExpression expression) { |
| final HighlightInfo parsingError = HighlightUtil.checkLiteralExpressionParsingError(expression, PsiUtil.getLanguageLevel(expression), |
| expression.getContainingFile()); |
| if (parsingError != null) { |
| throwEvaluateException(parsingError.getDescription()); |
| return; |
| } |
| |
| final PsiType type = expression.getType(); |
| if (type == null) { |
| throwEvaluateException(expression + ": null type"); |
| return; |
| } |
| |
| myResult = new LiteralEvaluator(expression.getValue(), type.getCanonicalText()); |
| } |
| |
| @Override |
| public void visitArrayAccessExpression(PsiArrayAccessExpression expression) { |
| final PsiExpression indexExpression = expression.getIndexExpression(); |
| if(indexExpression == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return; |
| } |
| indexExpression.accept(this); |
| final Evaluator indexEvaluator = handleUnaryNumericPromotion(indexExpression.getType(), myResult); |
| |
| expression.getArrayExpression().accept(this); |
| Evaluator arrayEvaluator = myResult; |
| myResult = new ArrayAccessEvaluator(arrayEvaluator, indexEvaluator); |
| } |
| |
| |
| /** |
| * Handles unboxing and numeric promotion issues for |
| * - array dimension expressions |
| * - array index expression |
| * - unary +, -, and ~ operations |
| * @param operandExpressionType |
| * @param operandEvaluator @return operandEvaluator possibly 'wrapped' with necessary unboxing and type-casting evaluators to make returning value |
| * suitable for mentioned contexts |
| */ |
| private static Evaluator handleUnaryNumericPromotion(final PsiType operandExpressionType, Evaluator operandEvaluator) { |
| final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(operandExpressionType); |
| if (unboxedType != null && !PsiType.BOOLEAN.equals(unboxedType)) { |
| operandEvaluator = new UnBoxingEvaluator(operandEvaluator); |
| } |
| |
| // handle numeric promotion |
| final PsiType _unboxedIndexType = unboxedType != null? unboxedType : operandExpressionType; |
| if (_unboxedIndexType instanceof PsiPrimitiveType) { |
| final PsiType promotionType = calcUnaryNumericPromotionType((PsiPrimitiveType)_unboxedIndexType); |
| if (promotionType != null) { |
| operandEvaluator = new TypeCastEvaluator(operandEvaluator, promotionType.getCanonicalText(), true); |
| } |
| } |
| return operandEvaluator; |
| } |
| |
| @SuppressWarnings({"ConstantConditions"}) |
| @Override |
| public void visitTypeCastExpression(PsiTypeCastExpression expression) { |
| final PsiExpression operandExpr = expression.getOperand(); |
| operandExpr.accept(this); |
| Evaluator operandEvaluator = myResult; |
| final PsiType castType = expression.getCastType().getType(); |
| final PsiType operandType = operandExpr.getType(); |
| |
| if (castType != null && operandType != null && !TypeConversionUtil.areTypesConvertible(operandType, castType)) { |
| throw new EvaluateRuntimeException( |
| new EvaluateException(JavaErrorMessages.message("inconvertible.type.cast", JavaHighlightUtil.formatType(operandType), JavaHighlightUtil |
| .formatType(castType))) |
| ); |
| } |
| |
| final boolean shouldPerformBoxingConversion = castType != null && operandType != null && TypeConversionUtil.boxingConversionApplicable(castType, operandType); |
| final boolean castingToPrimitive = castType instanceof PsiPrimitiveType; |
| if (shouldPerformBoxingConversion && castingToPrimitive) { |
| operandEvaluator = new UnBoxingEvaluator(operandEvaluator); |
| } |
| |
| final boolean performCastToWrapperClass = shouldPerformBoxingConversion && !castingToPrimitive; |
| |
| String castTypeName = castType.getCanonicalText(); |
| if (performCastToWrapperClass) { |
| final PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(castType); |
| if (unboxedType != null) { |
| castTypeName = unboxedType.getCanonicalText(); |
| } |
| } |
| |
| myResult = new TypeCastEvaluator(operandEvaluator, castTypeName, castingToPrimitive); |
| |
| if (performCastToWrapperClass) { |
| myResult = new BoxingEvaluator(myResult); |
| } |
| } |
| |
| @Override |
| public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) { |
| PsiType type = expression.getOperand().getType(); |
| |
| if (type instanceof PsiPrimitiveType) { |
| final JVMName typeName = JVMNameUtil.getJVMRawText(((PsiPrimitiveType)type).getBoxedTypeName()); |
| myResult = new FieldEvaluator(new TypeEvaluator(typeName), FieldEvaluator.TargetClassFilter.ALL, "TYPE"); |
| } |
| else { |
| myResult = new ClassObjectEvaluator(new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(type))); |
| } |
| } |
| |
| @Override |
| public void visitNewExpression(final PsiNewExpression expression) { |
| PsiType expressionPsiType = expression.getType(); |
| if (expressionPsiType instanceof PsiArrayType) { |
| Evaluator dimensionEvaluator = null; |
| PsiExpression[] dimensions = expression.getArrayDimensions(); |
| if (dimensions.length == 1){ |
| PsiExpression dimensionExpression = dimensions[0]; |
| dimensionExpression.accept(this); |
| if (myResult != null) { |
| dimensionEvaluator = handleUnaryNumericPromotion(dimensionExpression.getType(), myResult); |
| } |
| else { |
| throwEvaluateException( |
| DebuggerBundle.message("evaluation.error.invalid.array.dimension.expression", dimensionExpression.getText())); |
| } |
| } |
| else if (dimensions.length > 1){ |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.multi.dimensional.arrays.creation.not.supported")); |
| } |
| |
| Evaluator initializerEvaluator = null; |
| PsiArrayInitializerExpression arrayInitializer = expression.getArrayInitializer(); |
| if (arrayInitializer != null) { |
| if (dimensionEvaluator != null) { // initializer already exists |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); |
| } |
| arrayInitializer.accept(this); |
| if (myResult != null) { |
| initializerEvaluator = handleUnaryNumericPromotion(arrayInitializer.getType(), myResult); |
| } |
| else { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", arrayInitializer.getText())); |
| } |
| /* |
| PsiExpression[] initializers = arrayInitializer.getInitializers(); |
| initializerEvaluators = new Evaluator[initializers.length]; |
| for (int idx = 0; idx < initializers.length; idx++) { |
| PsiExpression initializer = initializers[idx]; |
| initializer.accept(this); |
| if (myResult instanceof Evaluator) { |
| initializerEvaluators[idx] = myResult; |
| } |
| else { |
| throw new EvaluateException("Invalid expression for array initializer: " + initializer.getText(), true); |
| } |
| } |
| */ |
| } |
| if (dimensionEvaluator == null && initializerEvaluator == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); |
| } |
| myResult = new NewArrayInstanceEvaluator( |
| new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(expressionPsiType)), |
| dimensionEvaluator, |
| initializerEvaluator |
| ); |
| } |
| else if (expressionPsiType instanceof PsiClassType){ // must be a class ref |
| PsiClass aClass = ((PsiClassType)expressionPsiType).resolve(); |
| if(aClass instanceof PsiAnonymousClass) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.anonymous.class.evaluation.not.supported")); |
| } |
| PsiExpressionList argumentList = expression.getArgumentList(); |
| if (argumentList == null) { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", expression.getText())); return; |
| } |
| final PsiExpression[] argExpressions = argumentList.getExpressions(); |
| final JavaResolveResult constructorResolveResult = expression.resolveMethodGenerics(); |
| final PsiMethod constructor = (PsiMethod)constructorResolveResult.getElement(); |
| if (constructor == null && argExpressions.length > 0) { |
| throw new EvaluateRuntimeException(new EvaluateException( |
| DebuggerBundle.message("evaluation.error.cannot.resolve.constructor", expression.getText()), null)); |
| } |
| Evaluator[] argumentEvaluators = new Evaluator[argExpressions.length]; |
| // evaluate arguments |
| for (int idx = 0; idx < argExpressions.length; idx++) { |
| PsiExpression argExpression = argExpressions[idx]; |
| argExpression.accept(this); |
| if (myResult != null) { |
| argumentEvaluators[idx] = new DisableGC(myResult); |
| } |
| else { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", argExpression.getText())); |
| } |
| } |
| |
| if (constructor != null) { |
| processBoxingConversions(constructor.getParameterList().getParameters(), argExpressions, constructorResolveResult.getSubstitutor(), argumentEvaluators); |
| argumentEvaluators = wrapVarargs(constructor.getParameterList().getParameters(), argExpressions, constructorResolveResult.getSubstitutor(), argumentEvaluators); |
| } |
| |
| //noinspection HardCodedStringLiteral |
| JVMName signature = constructor != null ? JVMNameUtil.getJVMSignature(constructor) : JVMNameUtil.getJVMRawText("()V"); |
| myResult = new NewClassInstanceEvaluator( |
| new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(expressionPsiType)), |
| signature, |
| argumentEvaluators |
| ); |
| } |
| else { |
| if (expressionPsiType != null) { |
| throwEvaluateException("Unsupported expression type: " + expressionPsiType.getPresentableText()); |
| } |
| else { |
| throwEvaluateException("Unknown type for expression: " + expression.getText()); |
| } |
| } |
| } |
| |
| @Override |
| public void visitArrayInitializerExpression(PsiArrayInitializerExpression expression) { |
| PsiExpression[] initializers = expression.getInitializers(); |
| Evaluator[] evaluators = new Evaluator[initializers.length]; |
| final PsiType type = expression.getType(); |
| boolean primitive = type instanceof PsiArrayType && ((PsiArrayType)type).getComponentType() instanceof PsiPrimitiveType; |
| for (int idx = 0; idx < initializers.length; idx++) { |
| PsiExpression initializer = initializers[idx]; |
| initializer.accept(this); |
| if (myResult != null) { |
| final Evaluator coerced = |
| primitive ? handleUnaryNumericPromotion(initializer.getType(), myResult) : new BoxingEvaluator(myResult); |
| evaluators[idx] = new DisableGC(coerced); |
| } |
| else { |
| throwEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", initializer.getText())); |
| } |
| } |
| myResult = new ArrayInitializerEvaluator(evaluators); |
| } |
| |
| @Nullable |
| private static PsiClass getOuterClass(PsiClass aClass) { |
| return aClass == null ? null : PsiTreeUtil.getContextOfType(aClass, PsiClass.class, true); |
| } |
| |
| private PsiClass getContainingClass(PsiVariable variable) { |
| PsiElement element = PsiTreeUtil.getParentOfType(variable.getParent(), PsiClass.class, false); |
| return element == null ? getContextPsiClass() : (PsiClass)element; |
| } |
| |
| public PsiClass getContextPsiClass() { |
| return myContextPsiClass; |
| } |
| |
| protected ExpressionEvaluator buildElement(final PsiElement element) throws EvaluateException { |
| LOG.assertTrue(element.isValid()); |
| |
| myContextPsiClass = PsiTreeUtil.getContextOfType(element, PsiClass.class, false); |
| try { |
| element.accept(this); |
| } |
| catch (EvaluateRuntimeException e) { |
| throw e.getCause(); |
| } |
| if (myResult == null) { |
| throw EvaluateExceptionUtil |
| .createEvaluateException(DebuggerBundle.message("evaluation.error.invalid.expression", element.toString())); |
| } |
| return new ExpressionEvaluatorImpl(myResult); |
| } |
| } |
| |
| private static Evaluator[] wrapVarargs(final PsiParameter[] declaredParams, |
| final PsiExpression[] actualArgumentExpressions, |
| final PsiSubstitutor methodResolveSubstitutor, |
| final Evaluator[] argumentEvaluators) { |
| int lastParam = declaredParams.length - 1; |
| if (lastParam >= 0 && declaredParams[lastParam].isVarArgs() && argumentEvaluators.length > lastParam) { |
| // only wrap if the first varargs parameter is null for now |
| if (!TypeConversionUtil.isNullType(actualArgumentExpressions[lastParam].getType())) { |
| return argumentEvaluators; |
| } |
| // do not wrap arrays twice |
| if (argumentEvaluators.length - lastParam == 1 && actualArgumentExpressions[lastParam].getType() instanceof PsiArrayType) { |
| return argumentEvaluators; |
| } |
| PsiEllipsisType declaredParamType = (PsiEllipsisType)methodResolveSubstitutor.substitute(declaredParams[lastParam].getType()); |
| ArrayInitializerEvaluator varargArrayEvaluator = |
| new ArrayInitializerEvaluator(Arrays.copyOfRange(argumentEvaluators, lastParam, argumentEvaluators.length)); |
| NewArrayInstanceEvaluator evaluator = |
| new NewArrayInstanceEvaluator(new TypeEvaluator(JVMNameUtil.getJVMQualifiedName(declaredParamType.toArrayType())), null, |
| varargArrayEvaluator); |
| Evaluator[] res = new Evaluator[declaredParams.length]; |
| System.arraycopy(argumentEvaluators, 0, res, 0, lastParam); |
| res[lastParam] = new DisableGC(evaluator); |
| return res; |
| } |
| return argumentEvaluators; |
| } |
| |
| private static void processBoxingConversions(final PsiParameter[] declaredParams, |
| final PsiExpression[] actualArgumentExpressions, |
| final PsiSubstitutor methodResolveSubstitutor, |
| final Evaluator[] argumentEvaluators) { |
| if (declaredParams.length > 0) { |
| final int paramCount = Math.max(declaredParams.length, actualArgumentExpressions.length); |
| PsiType varargType = null; |
| for (int idx = 0; idx < paramCount; idx++) { |
| if (idx >= actualArgumentExpressions.length) { |
| break; // actual arguments count is less than number of declared params |
| } |
| PsiType declaredParamType; |
| if (idx < declaredParams.length) { |
| declaredParamType = methodResolveSubstitutor.substitute(declaredParams[idx].getType()); |
| if (declaredParamType instanceof PsiEllipsisType) { |
| declaredParamType = varargType = ((PsiEllipsisType)declaredParamType).getComponentType(); |
| } |
| } |
| else if (varargType != null) { |
| declaredParamType = varargType; |
| } |
| else { |
| break; |
| } |
| final PsiType actualArgType = actualArgumentExpressions[idx].getType(); |
| if (TypeConversionUtil.boxingConversionApplicable(declaredParamType, actualArgType)) { |
| final Evaluator argEval = argumentEvaluators[idx]; |
| argumentEvaluators[idx] = declaredParamType instanceof PsiPrimitiveType ? new UnBoxingEvaluator(argEval) : new BoxingEvaluator(argEval); |
| } |
| } |
| } |
| } |
| } |