/******************************************************************************* * Copyright (c) 2000, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Jesper Steen Moller - Bug 427089 *******************************************************************************/ package org.eclipse.jdt.internal.debug.eval.ast.engine; import java.util.ArrayList; import java.util.EmptyStackException; import java.util.Iterator; import java.util.List; import java.util.Stack; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.dom.*; import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin; import org.eclipse.jdt.internal.debug.eval.ast.instructions.AndAssignmentOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.AndOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.ArrayAllocation; import org.eclipse.jdt.internal.debug.eval.ast.instructions.ArrayInitializerInstruction; import org.eclipse.jdt.internal.debug.eval.ast.instructions.AssignmentOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.Cast; import org.eclipse.jdt.internal.debug.eval.ast.instructions.CompoundInstruction; import org.eclipse.jdt.internal.debug.eval.ast.instructions.ConditionalJump; import org.eclipse.jdt.internal.debug.eval.ast.instructions.Constructor; import org.eclipse.jdt.internal.debug.eval.ast.instructions.DivideAssignmentOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.DivideOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.Dup; import org.eclipse.jdt.internal.debug.eval.ast.instructions.DupX1; import org.eclipse.jdt.internal.debug.eval.ast.instructions.EqualEqualOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.GreaterEqualOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.GreaterOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.InstanceOfOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.Instruction; import org.eclipse.jdt.internal.debug.eval.ast.instructions.InstructionSequence; import org.eclipse.jdt.internal.debug.eval.ast.instructions.Jump; import org.eclipse.jdt.internal.debug.eval.ast.instructions.LeftShiftAssignmentOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.LeftShiftOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.LessEqualOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.LessOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.LocalVariableCreation; import org.eclipse.jdt.internal.debug.eval.ast.instructions.MinusAssignmentOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.MinusOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.MultiplyAssignmentOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.MultiplyOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.NoOp; import org.eclipse.jdt.internal.debug.eval.ast.instructions.NotOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.OrAssignmentOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.OrOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PlusAssignmentOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PlusOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.Pop; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PostfixMinusMinusOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PostfixPlusPlusOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PrefixMinusMinusOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PrefixPlusPlusOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushArrayLength; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushArrayType; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushBoolean; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushChar; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushClassLiteralValue; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushDouble; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushFieldVariable; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushFloat; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushInt; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushLocalVariable; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushLong; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushNull; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushPrimitiveType; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushStaticFieldVariable; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushString; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushThis; import org.eclipse.jdt.internal.debug.eval.ast.instructions.PushType; import org.eclipse.jdt.internal.debug.eval.ast.instructions.RemainderAssignmentOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.RemainderOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.ReturnInstruction; import org.eclipse.jdt.internal.debug.eval.ast.instructions.RightShiftAssignmentOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.RightShiftOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.SendMessage; import org.eclipse.jdt.internal.debug.eval.ast.instructions.SendStaticMessage; import org.eclipse.jdt.internal.debug.eval.ast.instructions.ThrowInstruction; import org.eclipse.jdt.internal.debug.eval.ast.instructions.TwiddleOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.UnaryMinusOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.UnaryPlusOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.UnsignedRightShiftAssignmentOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.UnsignedRightShiftOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.Value; import org.eclipse.jdt.internal.debug.eval.ast.instructions.XorAssignmentOperator; import org.eclipse.jdt.internal.debug.eval.ast.instructions.XorOperator; import org.eclipse.osgi.util.NLS; import com.ibm.icu.text.MessageFormat; /** * The AST instruction compiler generates a sequence of instructions * (InstructionSequence) from a DOM AST. */ public class ASTInstructionCompiler extends ASTVisitor { /** * Represent a break or a continue instruction. These instructions needs are * stored and managed later by their related statement. */ class CompleteInstruction { Jump fInstruction; String fLabel; boolean fIsBreak; public CompleteInstruction(Jump instruction, String label, boolean isBreak) { fInstruction = instruction; fLabel = label; fIsBreak = isBreak; } } /** * Whether to print debug messages to the console */ private static boolean VERBOSE = false; private InstructionSequence fInstructions; /** * The list of pending break and continue instruction. */ private List<CompleteInstruction> fCompleteInstructions; private int fStartPosition; private boolean fActive; private boolean fHasErrors; private Stack<Instruction> fStack; private int fCounter; // internal index used to create unique variable name private int fUniqueIdIndex = 0; /** * Create a new AST instruction compiler */ public ASTInstructionCompiler(int startPosition, String snippet) { fStartPosition = startPosition; fInstructions = new InstructionSequence(snippet); fStack = new Stack<>(); fCompleteInstructions = new ArrayList<>(); } /** * Returns the instruction sequence generated by this AST instruction * compiler */ public InstructionSequence getInstructions() { return fInstructions; } /** * Returns whether the generated instruction sequence has errors. Errors * include: * <ol> * <li>AST contains unimplemented operations (features which will be * supported, but aren't yet)</li> * <li>AST contains unsupported operations (features which are not yet * implemented and are likely NOT to be implemented)</li> * </ol> */ public boolean hasErrors() { return fHasErrors; } private void setHasError(boolean value) { fHasErrors = value; } private void addErrorMessage(String message) { fInstructions.addError(message); } private boolean isActive() { return fActive; } private void setActive(boolean active) { fActive = active; } private void push(Instruction i) { fStack.push(i); } private void storeInstruction() { Instruction instruction = null; try { instruction = fStack.pop(); } catch(EmptyStackException ese) { JDIDebugPlugin.log(new Status( IStatus.WARNING, JDIDebugPlugin.getUniqueIdentifier(), NLS.bind(EvaluationEngineMessages.ASTInstructionCompiler_4, fCounter), ese)); } if(instruction != null) { fCounter++; if (instruction instanceof CompoundInstruction) { ((CompoundInstruction) instruction).setEnd(fCounter); } fInstructions.add(instruction); verbose("Add " + instruction.toString()); //$NON-NLS-1$ } } /** * Prints the given message to the console if verbose mode is on. * * @param message * the message to display */ private void verbose(String message) { if (VERBOSE) { System.out.println(message); } } private String getTypeName(ITypeBinding typeBinding) { if (typeBinding.isRawType()) { typeBinding = typeBinding.getErasure(); } if (typeBinding.isTypeVariable()) { ITypeBinding[] typeBounds = typeBinding.getTypeBounds(); if (typeBounds.length > 0) { String name = getTypeName(typeBounds[0]); if (typeBounds.length > 1 && "java.lang.Object".equals(name)) { //$NON-NLS-1$ return getTypeName(typeBounds[1]); } return name; } return "java.lang.Object"; //$NON-NLS-1$ } StringBuffer name; if (typeBinding.isArray()) { name = new StringBuffer(getTypeName(typeBinding.getElementType())); int dimensions = typeBinding.getDimensions(); for (int i = 0; i < dimensions; i++) { name.append("[]"); //$NON-NLS-1$ } return name.toString(); } //try it the old way name = new StringBuffer(Signature.getTypeErasure(typeBinding.getName())); IPackageBinding packageBinding = typeBinding.getPackage(); typeBinding = typeBinding.getDeclaringClass(); while (typeBinding != null) { name.insert(0, '$').insert(0, Signature.getTypeErasure(typeBinding.getName())); typeBinding = typeBinding.getDeclaringClass(); } if (packageBinding != null && !packageBinding.isUnnamed()) { name.insert(0, '.').insert(0, packageBinding.getName()); } return name.toString(); } private String getTypeSignature(ITypeBinding typeBinding) { return Signature.createTypeSignature(getTypeName(typeBinding), true) .replace('.', '/'); } private boolean isALocalType(ITypeBinding typeBinding) { while (typeBinding != null) { if (typeBinding.isLocal()) { return true; } typeBinding = typeBinding.getDeclaringClass(); } return false; } private boolean containsALocalType(IMethodBinding methodBinding) { ITypeBinding[] typeBindings = methodBinding.getParameterTypes(); for (ITypeBinding typeBinding : typeBindings) { if (isALocalType(typeBinding)) { return true; } } return false; } private int getEnclosingLevel(ASTNode node, ITypeBinding referenceTypeBinding) { ASTNode parent = node; ITypeBinding refbinding = referenceTypeBinding.isParameterizedType() ? referenceTypeBinding .getTypeDeclaration() : referenceTypeBinding; do { parent = parent.getParent(); } while (parent != null && !(parent instanceof AbstractTypeDeclaration || parent instanceof AnonymousClassDeclaration)); if (parent == null) { return 0; } ITypeBinding parentBinding = null; if (parent instanceof AbstractTypeDeclaration) { parentBinding = ((AbstractTypeDeclaration) parent).resolveBinding(); } else if (parent instanceof AnonymousClassDeclaration) { parentBinding = ((AnonymousClassDeclaration) parent) .resolveBinding(); } if (parentBinding != null && (parentBinding.isEqualTo(refbinding) || parentBinding .isCastCompatible(refbinding))) { return 0; } return getEnclosingLevel(parent, referenceTypeBinding) + 1; } private int getSuperLevel(ITypeBinding current, ITypeBinding reference) { if (current.equals(reference)) { return 0; } return getSuperLevel(current.getSuperclass(), reference); } /** * Return the label associated with the given statement. * * @param statement * the statement. * @return the associated label, or <code>null</code> if there is none. */ private String getLabel(Statement statement) { ASTNode parent = statement.getParent(); if (parent instanceof LabeledStatement) { return ((LabeledStatement) parent).getLabel().getIdentifier(); } return null; } /** * Append a pop instruction in the instruction list if needed. A pop * instruction is added when the expression has a return value, i.e. all * expressions expect method invocation expressions which have * <code>void</code> as return type and variable declaration expression. * * @param expression * the expression to test. */ private void addPopInstructionIfNeeded(Expression expression) { boolean pop = true; if (expression instanceof MethodInvocation) { IMethodBinding methodBinding = (IMethodBinding) ((MethodInvocation) expression) .getName().resolveBinding(); if (methodBinding != null && "void".equals(methodBinding.getReturnType().getName())) { //$NON-NLS-1$ pop = false; } } else if (expression instanceof SuperMethodInvocation) { IMethodBinding methodBinding = (IMethodBinding) ((SuperMethodInvocation) expression) .getName().resolveBinding(); if (methodBinding != null && "void".equals(methodBinding.getReturnType().getName())) { //$NON-NLS-1$ pop = false; } } else if (expression instanceof VariableDeclarationExpression) { pop = false; } if (pop) { addPopInstruction(); } } /** * */ private void addPopInstruction() { Instruction lastInstruction = fInstructions .getInstruction(fInstructions.getEnd()); push(new Pop(lastInstruction.getSize() + 1)); storeInstruction(); } /** * Check the current type of a value and the requested type to decide if * boxing/un-boxing is required. If needed, the correct instruction is added * to the stack Returns true if a storeInstruction() is needed after * visiting the expression */ private boolean checkAutoBoxing(ITypeBinding valueBinding, ITypeBinding requestedBinding) { if (valueBinding == null) { return false; // unresolved } if (valueBinding.isPrimitive() == requestedBinding.isPrimitive()) { return false; } if (requestedBinding.isPrimitive()) { unBoxing(valueBinding); } else { boxing(requestedBinding, valueBinding); } return true; } /** * Add to the stack the instruction to box a primitive value. */ private void boxing(ITypeBinding requestedBinding, ITypeBinding valueBinding) { String requestedTypeName = requestedBinding.getQualifiedName(); if ("java.lang.Object".equals(requestedTypeName)) { //$NON-NLS-1$ switch (valueBinding.getBinaryName().charAt(0)) { case 'I': push(new SendStaticMessage( "java.lang.Integer", "valueOf", "(I)Ljava/lang/Integer;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ break; case 'C': push(new SendStaticMessage( "java.lang.Character", "valueOf", "(C)Ljava/lang/Character;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ break; case 'B': push(new SendStaticMessage( "java.lang.Byte", "valueOf", "(B)Ljava/lang/Byte;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ break; case 'S': push(new SendStaticMessage( "java.lang.Short", "valueOf", "(S)Ljava/lang/Short;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ break; case 'J': push(new SendStaticMessage( "java.lang.Long", "valueOf", "(J)Ljava/lang/Long;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ break; case 'F': push(new SendStaticMessage( "java.lang.Float", "valueOf", "(F)Ljava/lang/Float;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ break; case 'D': push(new SendStaticMessage( "java.lang.Double", "valueOf", "(D)Ljava/lang/Double;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ break; case 'Z': push(new SendStaticMessage( "java.lang.Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ break; } } else if ("java.lang.Integer".equals(requestedTypeName)) { //$NON-NLS-1$ push(new SendStaticMessage(requestedTypeName, "valueOf", "(I)Ljava/lang/Integer;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Character".equals(requestedTypeName)) { //$NON-NLS-1$ push(new SendStaticMessage(requestedTypeName, "valueOf", "(C)Ljava/lang/Character;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Byte".equals(requestedTypeName)) { //$NON-NLS-1$ push(new SendStaticMessage(requestedTypeName, "valueOf", "(B)Ljava/lang/Byte;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Short".equals(requestedTypeName)) { //$NON-NLS-1$ push(new SendStaticMessage(requestedTypeName, "valueOf", "(S)Ljava/lang/Short;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Long".equals(requestedTypeName)) { //$NON-NLS-1$ push(new SendStaticMessage(requestedTypeName, "valueOf", "(J)Ljava/lang/Long;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Float".equals(requestedTypeName)) { //$NON-NLS-1$ push(new SendStaticMessage(requestedTypeName, "valueOf", "(F)Ljava/lang/Float;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Double".equals(requestedTypeName)) { //$NON-NLS-1$ push(new SendStaticMessage(requestedTypeName, "valueOf", "(D)Ljava/lang/Double;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Boolean".equals(requestedTypeName)) { //$NON-NLS-1$ push(new SendStaticMessage(requestedTypeName, "valueOf", "(Z)Ljava/lang/Boolean;", 1, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } } /** * Add the instruction to un-box a non-primitive value if needed. Returns * true if a storeInstruction() is needed after visiting the expression */ private boolean unBoxing(ITypeBinding valueBinding) { String valueTypeName = valueBinding.getQualifiedName(); if ("java.lang.Integer".equals(valueTypeName)) { //$NON-NLS-1$ push(new SendMessage("intValue", "()I", 0, null, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Character".equals(valueTypeName)) { //$NON-NLS-1$ push(new SendMessage("charValue", "()C", 0, null, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Byte".equals(valueTypeName)) { //$NON-NLS-1$ push(new SendMessage("byteValue", "()B", 0, null, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Short".equals(valueTypeName)) { //$NON-NLS-1$ push(new SendMessage("shortValue", "()S", 0, null, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Long".equals(valueTypeName)) { //$NON-NLS-1$ push(new SendMessage("longValue", "()J", 0, null, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Float".equals(valueTypeName)) { //$NON-NLS-1$ push(new SendMessage("floatValue", "()F", 0, null, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Double".equals(valueTypeName)) { //$NON-NLS-1$ push(new SendMessage("doubleValue", "()D", 0, null, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else if ("java.lang.Boolean".equals(valueTypeName)) { //$NON-NLS-1$ push(new SendMessage("booleanValue", "()Z", 0, null, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else { return false; } return true; } /** * End visit methods * * There are two paths to ending a visit to a node: * <ol> * <li>For control statements, the necessary control instructions (jump, * conditional jump) are inserted into the instruction sequence</li> * <li>For other cases, we simply remove the node's instruction from the * stack and add it to the instruction sequence.</li> * </ol> */ /** * @see ASTVisitor#endVisit(AnonymousClassDeclaration) */ @Override public void endVisit(AnonymousClassDeclaration node) { } /** * @see ASTVisitor#endVisit(ArrayAccess) */ @Override public void endVisit(ArrayAccess node) { if (!isActive() || hasErrors()) return; ITypeBinding typeBinding = node.getIndex().resolveTypeBinding(); if (typeBinding != null && unBoxing(typeBinding)) { // un-box the index, if required storeInstruction(); } storeInstruction(); } /** * @see ASTVisitor#endVisit(ArrayCreation) */ @Override public void endVisit(ArrayCreation node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(ArrayInitializer) */ @Override public void endVisit(ArrayInitializer node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(ArrayType) */ @Override public void endVisit(ArrayType node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(AssertStatement) */ @Override public void endVisit(AssertStatement node) { } /** * @see ASTVisitor#endVisit(Assignment) */ @Override public void endVisit(Assignment node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(Block) */ @Override public void endVisit(Block node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(BooleanLiteral) */ @Override public void endVisit(BooleanLiteral node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(BreakStatement) */ @Override public void endVisit(BreakStatement node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(CastExpression) */ @Override public void endVisit(CastExpression node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(CatchClause) */ @Override public void endVisit(CatchClause node) { } /** * @see ASTVisitor#endVisit(CharacterLiteral) */ @Override public void endVisit(CharacterLiteral node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(ClassInstanceCreation) */ @Override public void endVisit(ClassInstanceCreation node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(CompilationUnit) */ @Override public void endVisit(CompilationUnit node) { while(!fStack.isEmpty()) { storeInstruction(); } } /** * @see ASTVisitor#endVisit(ConditionalExpression) */ @Override public void endVisit(ConditionalExpression node) { if (!isActive() || hasErrors()) return; // Get the instructions int ifFalseAddress = fInstructions.getEnd(); Instruction ifFalse = fInstructions.get(ifFalseAddress); int ifTrueAddress = ifFalseAddress - ifFalse.getSize(); Instruction ifTrue = fInstructions.get(ifTrueAddress); int conditionalAddress = ifTrueAddress - ifTrue.getSize(); // Insert the conditional jump ConditionalJump conditionalJump = new ConditionalJump(false); fInstructions.insert(conditionalJump, conditionalAddress + 1); // Insert the jump int jumpAddress = ifTrueAddress + 2; Jump jump = new Jump(); fInstructions.insert(jump, jumpAddress); // Set the jump offsets conditionalJump.setOffset(ifTrue.getSize() + 1); jump.setOffset(ifFalse.getSize() + 1); fCounter += 2; storeInstruction(); } /** * @see ASTVisitor#endVisit(ConstructorInvocation) */ @Override public void endVisit(ConstructorInvocation node) { } /** * @see ASTVisitor#endVisit(ContinueStatement) */ @Override public void endVisit(ContinueStatement node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(DoStatement) */ @Override public void endVisit(DoStatement node) { if (!isActive() || hasErrors()) return; /* * The structure of generated instructions is : * * -- | body -- -- |condition -- - jump to the first instruction of the * body if the condition is true. */ String label = getLabel(node); // get address of each part int conditionAddress = fInstructions.getEnd(); Instruction condition = fInstructions.getInstruction(conditionAddress); int bodyAddress = conditionAddress - condition.getSize(); Instruction body = fInstructions.getInstruction(bodyAddress); int bodyStartAddress = bodyAddress - body.getSize(); // add the conditionnalJump ConditionalJump conditionalJump = new ConditionalJump(true); fInstructions.add(conditionalJump); fCounter++; // set jump offsets conditionalJump.setOffset(-(condition.getSize() + body.getSize() + 1)); // for each pending break or continue instruction which are related to // this loop, set the offset of the corresponding jump. for (Iterator<CompleteInstruction> iter = fCompleteInstructions.iterator(); iter.hasNext();) { CompleteInstruction instruction = iter.next(); Jump jumpInstruction = instruction.fInstruction; int instructionAddress = fInstructions.indexOf(jumpInstruction); if (instructionAddress > bodyStartAddress && (instruction.fLabel == null || instruction.fLabel .equals(label))) { iter.remove(); if (instruction.fIsBreak) { // jump to the instruction after the last jump jumpInstruction .setOffset((conditionAddress - instructionAddress) + 1); } else { // jump to the first instruction of the condition jumpInstruction.setOffset(bodyAddress - instructionAddress); } } } storeInstruction(); } /** * @see ASTVisitor#endVisit(EmptyStatement) */ @Override public void endVisit(EmptyStatement node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /* * (non-Javadoc) * * @see * org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom * .EnhancedForStatement) */ @Override public void endVisit(EnhancedForStatement node) { if (!isActive() || hasErrors()) return; /* * The structure of generated instructions is : * * For an array: -- | <ParameterType>[] a= Expression | int i= 0 | * <ParameterType> <ParameterName> -- -- | i < a.length - jump to the * instruction after the last jump if the condition is false. -- -- | s= * a[i] | Body -- -- - jump to the first instruction of the condition. * * For an Iterable: -- | Iterator i= Expression.iterator() | * <ParameterType> <ParameterName> -- -- | i.hasNext() - jump to the * instruction after the last jump if the condition is false. -- -- | s= * i.next() | Body -- -- - jump to the first instruction of the * condition. */ int bodyAddress = fInstructions.getEnd(); Instruction body = fInstructions.getInstruction(bodyAddress); int conditionAddress = bodyAddress - body.getSize(); Instruction condition = fInstructions.getInstruction(conditionAddress); int initAddress = conditionAddress - condition.getSize(); // add conditional jump ConditionalJump condJump = new ConditionalJump(false); fInstructions.insert(condJump, conditionAddress + 1); bodyAddress++; fCounter++; condJump.setOffset(body.getSize() + 1); // add jump Jump jump = new Jump(); fInstructions.add(jump); fCounter++; jump.setOffset(initAddress - (bodyAddress + 1)); // for each pending break or continue instruction which are related to // this loop, set the offset of the corresponding jump. String label = getLabel(node); for (Iterator<CompleteInstruction> iter = fCompleteInstructions.iterator(); iter.hasNext();) { CompleteInstruction instruction = iter.next(); Jump jumpInstruction = instruction.fInstruction; int instructionAddress = fInstructions.indexOf(jumpInstruction); if (instructionAddress > conditionAddress && (instruction.fLabel == null || instruction.fLabel .equals(label))) { iter.remove(); if (instruction.fIsBreak) { // jump to the instruction after the last jump jumpInstruction .setOffset((bodyAddress - instructionAddress) + 1); } else { // jump to the first instruction of the condition jumpInstruction.setOffset(initAddress - instructionAddress); } } } storeInstruction(); } /** * @see ASTVisitor#endVisit(ExpressionStatement) */ @Override public void endVisit(ExpressionStatement node) { if (!isActive() || hasErrors()) return; addPopInstructionIfNeeded(node.getExpression()); } /** * @see ASTVisitor#endVisit(FieldAccess) */ @Override public void endVisit(FieldAccess node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(FieldDeclaration) */ @Override public void endVisit(FieldDeclaration node) { } /** * @see ASTVisitor#endVisit(ForStatement) */ @Override public void endVisit(ForStatement node) { if (!isActive() || hasErrors()) return; /* * The structure of generated instructions is : * * -- |initialization -- -- |condition -- - jump to the instruction * after the last jump if the condition is false. -- | body -- -- | * updaters -- - jump to the first instruction of the condition. */ String label = getLabel(node); boolean hasCondition = node.getExpression() != null; // get address of each part int updatersAddress = fInstructions.getEnd(); Instruction updaters = fInstructions.getInstruction(updatersAddress); int bodyAddress = updatersAddress - updaters.getSize(); Instruction body = fInstructions.getInstruction(bodyAddress); int bodyStartAddress = bodyAddress - body.getSize(); int conditionAddress; Instruction condition; if (hasCondition) { conditionAddress = bodyStartAddress; condition = fInstructions.getInstruction(conditionAddress); } else { conditionAddress = 0; condition = null; } // add jump Jump jump = new Jump(); fInstructions.add(jump); fCounter++; if (hasCondition) { // add conditional jump ConditionalJump condJump = new ConditionalJump(false); fInstructions.insert(condJump, conditionAddress + 1); bodyAddress++; bodyStartAddress++; updatersAddress++; fCounter++; // set conditional jump offset condJump.setOffset(body.getSize() + updaters.getSize() + 1); } // set jump offset jump.setOffset(-((hasCondition && (condition != null) ? condition .getSize() : 0) + body.getSize() + updaters.getSize() + 2)); // for each pending break or continue instruction which are related to // this loop, set the offset of the corresponding jump. for (Iterator<CompleteInstruction> iter = fCompleteInstructions.iterator(); iter.hasNext();) { CompleteInstruction instruction = iter.next(); Jump jumpInstruction = instruction.fInstruction; int instructionAddress = fInstructions.indexOf(jumpInstruction); if (instructionAddress > bodyStartAddress && (instruction.fLabel == null || instruction.fLabel .equals(label))) { iter.remove(); if (instruction.fIsBreak) { // jump to the instruction after the last jump jumpInstruction .setOffset((updatersAddress - instructionAddress) + 1); } else { // jump to the first instruction of the condition jumpInstruction.setOffset(bodyAddress - instructionAddress); } } } storeInstruction(); } /** * @see ASTVisitor#endVisit(IfStatement) */ @Override public void endVisit(IfStatement node) { if (!isActive() || hasErrors()) return; boolean hasElseStatement = node.getElseStatement() != null; // Get the instructions int ifFalseAddress = 0; Instruction ifFalse = null; int ifTrueAddress = 0; Instruction ifTrue = null; if (hasElseStatement) { ifFalseAddress = fInstructions.getEnd(); ifFalse = fInstructions.get(ifFalseAddress); ifTrueAddress = ifFalseAddress - ifFalse.getSize(); ifTrue = fInstructions.get(ifTrueAddress); } else { ifTrueAddress = fInstructions.getEnd(); ifTrue = fInstructions.get(ifTrueAddress); } int conditionalAddress = ifTrueAddress - ifTrue.getSize(); // Insert the conditional jump ConditionalJump conditionalJump = new ConditionalJump(false); fInstructions.insert(conditionalJump, conditionalAddress + 1); // Set the jump offset conditionalJump.setOffset(ifTrue.getSize() + ((hasElseStatement) ? 1 : 0)); fCounter++; if (hasElseStatement) { // Insert the jump int jumpAddress = ifTrueAddress + 2; Jump jump = new Jump(); fInstructions.insert(jump, jumpAddress); // Set the jump offset jump.setOffset(ifFalse.getSize() + 1); fCounter++; } storeInstruction(); } /** * @see ASTVisitor#endVisit(ImportDeclaration) */ @Override public void endVisit(ImportDeclaration node) { } /** * @see ASTVisitor#endVisit(InfixExpression) */ @Override public void endVisit(InfixExpression node) { } /** * @see ASTVisitor#endVisit(Initializer) */ @Override public void endVisit(Initializer node) { } /** * @see ASTVisitor#endVisit(InstanceofExpression) */ @Override public void endVisit(InstanceofExpression node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(Javadoc) */ @Override public void endVisit(Javadoc node) { } /** * @see ASTVisitor#endVisit(LabeledStatement) */ @Override public void endVisit(LabeledStatement node) { if (!isActive() || hasErrors()) return; String label = node.getLabel().getIdentifier(); // for each pending continue instruction which are related to // this statement, set the offset of the corresponding jump. for (Iterator<CompleteInstruction> iter = fCompleteInstructions.iterator(); iter.hasNext();) { CompleteInstruction instruction = iter.next(); if (instruction.fLabel != null && instruction.fLabel.equals(label)) { iter.remove(); Jump jumpInstruction = instruction.fInstruction; int instructionAddress = fInstructions.indexOf(jumpInstruction); if (instruction.fIsBreak) { // jump to the instruction after the statement jumpInstruction.setOffset(fInstructions.getEnd() - instructionAddress); } } } } /** * @see ASTVisitor#endVisit(MethodDeclaration) */ @Override public void endVisit(MethodDeclaration node) { setActive(false); } /** * @see ASTVisitor#endVisit(MethodInvocation) */ @Override public void endVisit(MethodInvocation node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(NullLiteral) */ @Override public void endVisit(NullLiteral node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(NumberLiteral) */ @Override public void endVisit(NumberLiteral node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(PackageDeclaration) */ @Override public void endVisit(PackageDeclaration node) { } /** * @see ASTVisitor#endVisit(SimpleType) */ @Override public void endVisit(ParameterizedType node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(ParenthesizedExpression) */ @Override public void endVisit(ParenthesizedExpression node) { } /** * @see ASTVisitor#endVisit(PostfixExpression) */ @Override public void endVisit(PostfixExpression node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(PrefixExpression) */ @Override public void endVisit(PrefixExpression node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(PrimitiveType) */ @Override public void endVisit(PrimitiveType node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(QualifiedName) */ @Override public void endVisit(QualifiedName node) { } /** * @see ASTVisitor#endVisit(SimpleType) */ @Override public void endVisit(QualifiedType node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(ReturnStatement) */ @Override public void endVisit(ReturnStatement node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(SimpleName) */ @Override public void endVisit(SimpleName node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(SimpleType) */ @Override public void endVisit(SimpleType node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(SingleVariableDeclaration) */ @Override public void endVisit(SingleVariableDeclaration node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(StringLiteral) */ @Override public void endVisit(StringLiteral node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(SuperConstructorInvocation) */ @Override public void endVisit(SuperConstructorInvocation node) { } /** * @see ASTVisitor#endVisit(SuperFieldAccess) */ @Override public void endVisit(SuperFieldAccess node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(SuperMethodInvocation) */ @Override public void endVisit(SuperMethodInvocation node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(SwitchCase) */ @Override public void endVisit(SwitchCase node) { // never called } /** * @see ASTVisitor#endVisit(SwitchStatement) */ @Override public void endVisit(SwitchStatement node) { // nothing to do } /** * @see ASTVisitor#endVisit(SynchronizedStatement) */ @Override public void endVisit(SynchronizedStatement node) { } /** * @see ASTVisitor#endVisit(ThisExpression) */ @Override public void endVisit(ThisExpression node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(ThrowStatement) */ @Override public void endVisit(ThrowStatement node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(TryStatement) */ @Override public void endVisit(TryStatement node) { } /** * @see ASTVisitor#endVisit(TypeDeclaration) */ @Override public void endVisit(TypeDeclaration node) { } /** * @see ASTVisitor#endVisit(TypeDeclarationStatement) */ @Override public void endVisit(TypeDeclarationStatement node) { } /** * @see ASTVisitor#endVisit(TypeLiteral) */ @Override public void endVisit(TypeLiteral node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(VariableDeclarationExpression) */ @Override public void endVisit(VariableDeclarationExpression node) { } /** * @see ASTVisitor#endVisit(VariableDeclarationFragment) */ @Override public void endVisit(VariableDeclarationFragment node) { if (!isActive() || hasErrors()) return; storeInstruction(); } /** * @see ASTVisitor#endVisit(VariableDeclarationStatement) */ @Override public void endVisit(VariableDeclarationStatement node) { } /** * @see ASTVisitor#endVisit(WhileStatement) */ @Override public void endVisit(WhileStatement node) { if (!isActive() || hasErrors()) return; /* * The structure of generated instructions is : * * -- |condition -- - jump to the instruction after the last jump if the * condition is false. -- | body -- - jump to the first instruction of * the condition. */ String label = getLabel(node); // get address of each part int bodyAddress = fInstructions.getEnd(); Instruction body = fInstructions.getInstruction(bodyAddress); int conditionAddress = bodyAddress - body.getSize(); Instruction condition = fInstructions.getInstruction(conditionAddress); // add the conditionnalJump ConditionalJump conditionalJump = new ConditionalJump(false); fInstructions.insert(conditionalJump, conditionAddress + 1); // add the jump Jump jump = new Jump(); fInstructions.add(jump); // set jump offsets conditionalJump.setOffset(body.getSize() + 1); jump.setOffset(-(condition.getSize() + body.getSize() + 2)); // for each pending break or continue instruction which are related to // this loop, set the offset of the corresponding jump. for (Iterator<CompleteInstruction> iter = fCompleteInstructions.iterator(); iter.hasNext();) { CompleteInstruction instruction = iter.next(); Jump jumpInstruction = instruction.fInstruction; int instructionAddress = fInstructions.indexOf(jumpInstruction); if (instructionAddress > conditionAddress && (instruction.fLabel == null || instruction.fLabel .equals(label))) { iter.remove(); if (instruction.fIsBreak) { // jump to the instruction after the last jump jumpInstruction .setOffset((bodyAddress - instructionAddress) + 2); } else { // jump to the first instruction of the condition jumpInstruction.setOffset((conditionAddress - condition .getSize()) - instructionAddress); } } } fCounter += 2; storeInstruction(); } /* * Visit methods * * There are two variations of node visiting: <ol> <li>Push the instruction * corresponding to the node onto the stack and return <code>true</code> to * visit the children of the node.</li> <li>Push the instruction * corresponding to the node onto the stack and visit the children of the * node manually (return <code>false</code> to avoid the default child * visiting implementation).</li> </ol> */ /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * AnnotationTypeDeclaration) */ @Override public boolean visit(AnnotationTypeDeclaration node) { return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * AnnotationTypeMemberDeclaration) */ @Override public boolean visit(AnnotationTypeMemberDeclaration node) { return false; } /** * @see ASTVisitor#visit(AnonymousClassDeclaration) */ @Override public boolean visit(AnonymousClassDeclaration node) { if (!isActive()) { return true; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Anonymous_type_declaration_cannot_be_used_in_an_evaluation_expression_2); return false; } /** * @see ASTVisitor#visit(ArrayAccess) */ @Override public boolean visit(ArrayAccess node) { if (!isActive()) { return false; } push(new org.eclipse.jdt.internal.debug.eval.ast.instructions.ArrayAccess( fCounter)); return true; } /** * @see ASTVisitor#visit(ArrayCreation) */ @Override public boolean visit(ArrayCreation node) { if (!isActive()) { return false; } ArrayType arrayType = node.getType(); ITypeBinding binding = resolveTypeBinding(arrayType); if (binding != null && isALocalType(binding.getElementType())) { addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Local_type_array_instance_creation_cannot_be_used_in_an_evaluation_expression_29); setHasError(true); return false; } push(new ArrayAllocation(arrayType.getDimensions(), node.dimensions() .size(), node.getInitializer() != null, fCounter)); return true; } /** * @see ASTVisitor#visit(ArrayInitializer) */ @Override public boolean visit(ArrayInitializer node) { if (!isActive()) { return false; } ITypeBinding typeBinding = resolveTypeBinding(node); if (typeBinding != null) { int dimension = typeBinding.getDimensions(); String signature = getTypeSignature(typeBinding.getElementType()); push(new ArrayInitializerInstruction(signature, node.expressions() .size(), dimension, fCounter)); } return true; } /** * @see ASTVisitor#visit(ArrayType) */ @Override public boolean visit(ArrayType node) { if (!isActive()) { return false; } ITypeBinding arrayTypeBinding = resolveTypeBinding(node); if (arrayTypeBinding != null) { int dimension = arrayTypeBinding.getDimensions(); String signature = getTypeSignature(arrayTypeBinding .getElementType()); push(new PushArrayType(signature, dimension, fCounter)); } return false; } /** * @see ASTVisitor#visit(AssertStatement) */ @Override public boolean visit(AssertStatement node) { if (!isActive()) { return false; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Assert_statement_cannot_be_used_in_an_evaluation_expression_3); return false; } /** * @see ASTVisitor#visit(Assignment) */ @Override public boolean visit(Assignment node) { if (!isActive()) { return false; } Expression leftHandSide = node.getLeftHandSide(); Expression rightHandSide = node.getRightHandSide(); int variableTypeId = getTypeId(leftHandSide); int valueTypeId = getTypeId(rightHandSide); String opToken = node.getOperator().toString(); int opTokenLength = opToken.length(); char char0 = opToken.charAt(0); char char2 = '\0'; if (opTokenLength > 2) { char2 = opToken.charAt(2); } ITypeBinding rightBinding = resolveTypeBinding(rightHandSide); if (rightBinding == null) { return false; } ITypeBinding leftBinding = resolveTypeBinding(leftHandSide); if (leftBinding == null) { return false; } if (variableTypeId == Instruction.T_Object) { // If the variable is an object, the value may need to be boxed for // the simple assignment. // For the compound assignment operators, the value of the variable // have to be un-boxed before the operation is done, then re-boxed // to // to be stored in the variable. int unboxedVariableTypeId = getUnBoxedTypeId(leftHandSide); int unboxedValueTypeId = getUnBoxedTypeId(rightHandSide); int unboxedResultTypeId = Instruction.getBinaryPromotionType( unboxedVariableTypeId, unboxedValueTypeId); push(new AssignmentOperator(variableTypeId, variableTypeId, fCounter)); leftHandSide.accept(this); if (char0 == '=') { boolean storeRequired = false; if (rightBinding.isPrimitive()) { boxing(leftBinding, rightBinding); storeRequired = true; } rightHandSide.accept(this); if (storeRequired) { storeInstruction(); // boxing } } else { boolean unrecognized = false; boxing(leftBinding, rightBinding); switch (char0) { case '=': // equal break; case '+': // plus equal push(new PlusOperator(unboxedVariableTypeId, unboxedValueTypeId, unboxedResultTypeId, fCounter)); break; case '-': // minus equal push(new MinusOperator(unboxedVariableTypeId, unboxedValueTypeId, unboxedResultTypeId, fCounter)); break; case '*': // multiply equal push(new MultiplyOperator(unboxedVariableTypeId, unboxedValueTypeId, unboxedResultTypeId, fCounter)); break; case '/': // divide equal push(new DivideOperator(unboxedVariableTypeId, unboxedValueTypeId, unboxedResultTypeId, fCounter)); break; case '%': // remainder equal push(new RemainderOperator(unboxedVariableTypeId, unboxedValueTypeId, unboxedResultTypeId, fCounter)); break; case '^': // XOr equal push(new XorOperator(unboxedVariableTypeId, unboxedValueTypeId, unboxedResultTypeId, fCounter)); break; case '|': // or equal push(new OrOperator(unboxedVariableTypeId, unboxedValueTypeId, unboxedResultTypeId, fCounter)); break; case '&': // and equal push(new AndOperator(unboxedVariableTypeId, unboxedValueTypeId, unboxedResultTypeId, fCounter)); break; case '<': // left shift equal push(new LeftShiftOperator(unboxedVariableTypeId, unboxedValueTypeId, unboxedResultTypeId, fCounter)); break; case '>': // right shift equal or unsigned right shift equal switch (char2) { case '=': // right shift equal push(new RightShiftOperator(unboxedVariableTypeId, unboxedValueTypeId, unboxedResultTypeId, fCounter)); break; case '>': // unsigned right shift equal push(new UnsignedRightShiftOperator( unboxedVariableTypeId, unboxedValueTypeId, unboxedResultTypeId, fCounter)); break; default: unrecognized = true; break; } break; default: unrecognized = true; break; } if (unrecognized) { setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Unrecognized_assignment_operator____4 + opToken); return false; } unBoxing(leftBinding); push(new Dup()); storeInstruction(); // dupe storeInstruction(); // un-boxing boolean storeRequired = unBoxing(rightBinding); rightHandSide.accept(this); if (storeRequired) { storeInstruction(); // un-boxing } storeInstruction(); // operation storeInstruction(); // boxing } } else { boolean unrecognized = false; switch (char0) { case '=': // equal push(new AssignmentOperator(variableTypeId, valueTypeId, fCounter)); break; case '+': // plus equal push(new PlusAssignmentOperator(variableTypeId, valueTypeId, fCounter)); break; case '-': // minus equal push(new MinusAssignmentOperator(variableTypeId, valueTypeId, fCounter)); break; case '*': // multiply equal push(new MultiplyAssignmentOperator(variableTypeId, valueTypeId, fCounter)); break; case '/': // divide equal push(new DivideAssignmentOperator(variableTypeId, valueTypeId, fCounter)); break; case '%': // remainder equal push(new RemainderAssignmentOperator(variableTypeId, valueTypeId, fCounter)); break; case '^': // XOr equal push(new XorAssignmentOperator(variableTypeId, valueTypeId, fCounter)); break; case '|': // or equal push(new OrAssignmentOperator(variableTypeId, valueTypeId, fCounter)); break; case '&': // and equal push(new AndAssignmentOperator(variableTypeId, valueTypeId, fCounter)); break; case '<': // left shift equal push(new LeftShiftAssignmentOperator(variableTypeId, valueTypeId, fCounter)); break; case '>': // right shift equal or unsigned right shift equal switch (char2) { case '=': // right shift equal push(new RightShiftAssignmentOperator(variableTypeId, valueTypeId, fCounter)); break; case '>': // unsigned right shift equal push(new UnsignedRightShiftAssignmentOperator( variableTypeId, valueTypeId, fCounter)); break; default: unrecognized = true; break; } break; default: unrecognized = true; break; } if (unrecognized) { setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Unrecognized_assignment_operator____4 + opToken); return false; } leftHandSide.accept(this); boolean storeRequired = unBoxing(rightBinding); rightHandSide.accept(this); if (storeRequired) { storeInstruction(); } } return false; } /** * @see ASTVisitor#visit(Block) */ @Override public boolean visit(Block node) { int start = node.getStartPosition(); if (start == fStartPosition || start == (fStartPosition + 1)) { setActive(true); } if (!isActive()) { return true; } push(new NoOp(fCounter)); return true; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * BlockComment) */ @Override public boolean visit(BlockComment node) { return false; } /** * @see ASTVisitor#visit(BooleanLiteral) */ @Override public boolean visit(BooleanLiteral node) { if (!isActive()) { return false; } push(new PushBoolean(node.booleanValue())); return true; } /** * @see ASTVisitor#visit(BreakStatement) */ @Override public boolean visit(BreakStatement node) { if (!isActive()) { return false; } // create the equivalent jump instruction in the instruction // and add an element in the list of pending break and continue // instructions Jump instruction = new Jump(); SimpleName labelName = node.getLabel(); String label = null; if (labelName != null) { label = labelName.getIdentifier(); } push(instruction); fCompleteInstructions.add(new CompleteInstruction(instruction, label, true)); return false; } /** * @see ASTVisitor#visit(CastExpression) */ @Override public boolean visit(CastExpression node) { if (!isActive()) { return false; } Type type = node.getType(); int typeId = getTypeId(type); ITypeBinding typeBinding = resolveTypeBinding(type); if (typeBinding != null) { String baseTypeSignature; int dimension = typeBinding.getDimensions(); if (typeBinding.isArray()) { typeBinding = typeBinding.getElementType(); } baseTypeSignature = getTypeName(typeBinding); push(new Cast(typeId, baseTypeSignature, dimension, fCounter)); node.getExpression().accept(this); } return false; } /** * @see ASTVisitor#visit(CatchClause) */ @Override public boolean visit(CatchClause node) { if (!isActive()) { return false; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Catch_clause_cannot_be_used_in_an_evaluation_expression_6); return false; } /** * @see ASTVisitor#visit(CharacterLiteral) */ @Override public boolean visit(CharacterLiteral node) { if (!isActive()) { return false; } push(new PushChar(node.charValue())); return true; } /** * return false, visit expression, type name & arguments, don't visit body * declaration * * @see ASTVisitor#visit(ClassInstanceCreation) */ @Override public boolean visit(ClassInstanceCreation node) { if (!isActive()) { return true; } if (node.getAnonymousClassDeclaration() != null) { setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Anonymous_type_declaration_cannot_be_used_in_an_evaluation_expression_7); } IMethodBinding methodBinding = node.resolveConstructorBinding(); if (methodBinding == null) { setHasError(true); addErrorMessage(MessageFormat.format( EvaluationEngineMessages.ASTInstructionCompiler_1, new Object[] { node.toString() })); return false; } ITypeBinding typeBinding = methodBinding.getDeclaringClass(); boolean isInstanceMemberType = typeBinding.isMember() && !Modifier.isStatic(typeBinding.getModifiers()); if (isALocalType(typeBinding)) { setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Constructor_of_a_local_type_cannot_be_used_in_an_evaluation_expression_8); } if (containsALocalType(methodBinding)) { setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Constructor_which_contains_a_local_type_as_parameter_cannot_be_used_in_an_evaluation_expression_30); } if (hasErrors()) { return false; } int paramCount = methodBinding.getParameterTypes().length; String enclosingTypeSignature = null; ITypeBinding enclosingTypeBinding = null; if (isInstanceMemberType) { enclosingTypeBinding = typeBinding.getDeclaringClass(); if (enclosingTypeBinding == null) { setHasError(true); addErrorMessage(MessageFormat.format( EvaluationEngineMessages.ASTInstructionCompiler_2, new Object[] { typeBinding.getQualifiedName() })); return false; } enclosingTypeSignature = getTypeSignature(enclosingTypeBinding); paramCount++; } String signature = getMethodSignature(methodBinding, enclosingTypeSignature).replace('.', '/'); push(new Constructor(signature, paramCount, fCounter)); push(new PushType(getTypeName(typeBinding))); storeInstruction(); if (isInstanceMemberType) { Expression optionalExpression = node.getExpression(); if (optionalExpression != null) { optionalExpression.accept(this); } else { // for a non-static inner class, check if we are not in a static // context (method) ASTNode parent = node; do { parent = parent.getParent(); } while (!(parent instanceof MethodDeclaration)); if (Modifier.isStatic(((MethodDeclaration) parent) .getModifiers())) { setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Must_explicitly_qualify_the_allocation_with_an_instance_of_the_enclosing_type_33); return false; } push(new PushThis(getEnclosingLevel(node, enclosingTypeBinding))); storeInstruction(); } } List<Expression> arguments = node.arguments(); pushMethodArguments(methodBinding, arguments); return false; } /** * @see ASTVisitor#visit(CompilationUnit) */ @Override public boolean visit(CompilationUnit node) { return true; } /** * @see ASTVisitor#visit(ConditionalExpression) */ @Override public boolean visit(ConditionalExpression node) { if (!isActive()) { return true; } push(new NoOp(fCounter)); return true; } /** * @see ASTVisitor#visit(ConstructorInvocation) */ @Override public boolean visit(ConstructorInvocation node) { if (!isActive()) { return false; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_this_constructor_invocation_cannot_be_used_in_an_evaluation_expression_9); return false; } /** * @see ASTVisitor#visit(ContinueStatement) */ @Override public boolean visit(ContinueStatement node) { if (!isActive()) { return false; } // create the equivalent jump instruction in the instruction // and add an element in the list of pending break and continue // instructions Jump instruction = new Jump(); SimpleName labelName = node.getLabel(); String label = null; if (labelName != null) { label = labelName.getIdentifier(); } push(instruction); fCompleteInstructions.add(new CompleteInstruction(instruction, label, false)); return false; } /** * @see ASTVisitor#visit(CreationReference) */ @Override public boolean visit(CreationReference node) { if (!isActive()) { return true; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Reference_expressions_cannot_be_used_in_an_evaluation_expression); return false; } /** * @see ASTVisitor#visit(DoStatement) */ @Override public boolean visit(DoStatement node) { if (!isActive()) { return false; } push(new NoOp(fCounter)); return true; } /** * @see ASTVisitor#visit(EmptyStatement) */ @Override public boolean visit(EmptyStatement node) { if (!isActive()) { return false; } push(new NoOp(fCounter)); return true; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * EnhancedForStatement) */ @Override public boolean visit(EnhancedForStatement node) { if (!isActive()) { return false; } push(new NoOp(fCounter)); ITypeBinding typeBinding = resolveTypeBinding(node.getExpression()); if (typeBinding == null) { return false; } Type paramType = node.getParameter().getType(); ITypeBinding paramBinding = resolveTypeBinding(paramType); if (paramBinding == null) { return false; } String typeSignature = getTypeSignature(paramBinding); int paramTypeId = getTypeId(paramType); boolean isParamPrimitiveType = paramTypeId != Instruction.T_Object && paramTypeId != Instruction.T_String; String paramIdentifier = node.getParameter().getName().getIdentifier(); if (typeBinding.isArray()) { // the expression returns an array int idIndex = fUniqueIdIndex++; String arrayIdentifier = "#a" + idIndex; //$NON-NLS-1$ String varIdentifier = "#i" + idIndex; //$NON-NLS-1$ push(new LocalVariableCreation(arrayIdentifier, typeSignature, 1, isParamPrimitiveType, true, fCounter)); node.getExpression().accept(this); storeInstruction(); push(new LocalVariableCreation(varIdentifier, "I", 0, true, true, fCounter)); //$NON-NLS-1$ push(new PushInt(0)); storeInstruction(); storeInstruction(); push(new LocalVariableCreation(paramIdentifier, typeSignature, 0, isParamPrimitiveType, false, fCounter)); storeInstruction(); push(new LessOperator(Instruction.T_int, Instruction.T_int, fCounter)); push(new PushLocalVariable(varIdentifier)); storeInstruction(); push(new PushArrayLength(fCounter)); push(new PushLocalVariable(arrayIdentifier)); storeInstruction(); storeInstruction(); storeInstruction(); // conditional jump will be added here push(new NoOp(fCounter)); push(new AssignmentOperator(paramTypeId, paramTypeId, fCounter)); push(new PushLocalVariable(paramIdentifier)); storeInstruction(); push(new org.eclipse.jdt.internal.debug.eval.ast.instructions.ArrayAccess( fCounter)); push(new PushLocalVariable(arrayIdentifier)); storeInstruction(); push(new PostfixPlusPlusOperator(Instruction.T_int, fCounter)); push(new PushLocalVariable(varIdentifier)); storeInstruction(); storeInstruction(); storeInstruction(); if (checkAutoBoxing(typeBinding.getElementType(), paramBinding)) { storeInstruction(); } storeInstruction(); addPopInstruction(); node.getBody().accept(this); storeInstruction(); // jump will be added here } else { // the expression returns a collection String iteratorIdentifier = "#i" + fUniqueIdIndex++; //$NON-NLS-1$ push(new LocalVariableCreation(iteratorIdentifier, "Ljava/util/Iterator;", 0, false, true, fCounter)); //$NON-NLS-1$ push(new SendMessage( "iterator", "()Ljava/util/Iterator;", 0, null, fCounter)); //$NON-NLS-1$//$NON-NLS-2$ node.getExpression().accept(this); storeInstruction(); storeInstruction(); push(new LocalVariableCreation(paramIdentifier, typeSignature, 0, isParamPrimitiveType, false, fCounter)); storeInstruction(); push(new SendMessage("hasNext", "()Z", 0, null, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ push(new PushLocalVariable(iteratorIdentifier)); storeInstruction(); storeInstruction(); // conditional jump will be added here push(new NoOp(fCounter)); push(new AssignmentOperator(paramTypeId, paramTypeId, fCounter)); push(new PushLocalVariable(paramIdentifier)); storeInstruction(); push(new SendMessage( "next", "()Ljava/lang/Object;", 0, null, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ push(new PushLocalVariable(iteratorIdentifier)); storeInstruction(); storeInstruction(); ITypeBinding[] typeArguments = typeBinding.getTypeArguments(); if (typeArguments != null && typeArguments.length > 0) { if (checkAutoBoxing(typeArguments[0], paramBinding)) { storeInstruction(); } } storeInstruction(); addPopInstruction(); node.getBody().accept(this); storeInstruction(); // jump will be added here } return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * EnumConstantDeclaration) */ @Override public boolean visit(EnumConstantDeclaration node) { if (!isActive()) { return true; } // nothing to do, we shouldn't hit this node return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * EnumDeclaration) */ @Override public boolean visit(EnumDeclaration node) { if (!isActive()) { return true; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_0); return false; } /** * @see ASTVisitor#visit(ExpressionMethodReference) */ @Override public boolean visit(ExpressionMethodReference node) { if (!isActive()) { return true; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Reference_expressions_cannot_be_used_in_an_evaluation_expression); return false; } /** * @see ASTVisitor#visit(ExpressionStatement) */ @Override public boolean visit(ExpressionStatement node) { return true; } /** * return false, visit expression, don't visit name * * @see ASTVisitor#visit(FieldAccess) */ @Override public boolean visit(FieldAccess node) { if (!isActive()) { return false; } SimpleName fieldName = node.getName(); IVariableBinding fieldBinding = (IVariableBinding) fieldName .resolveBinding(); if (fieldBinding != null) { ITypeBinding declaringTypeBinding = fieldBinding .getDeclaringClass(); Expression expression = node.getExpression(); String fieldId = fieldName.getIdentifier(); if (Modifier.isStatic(fieldBinding.getModifiers())) { push(new PushStaticFieldVariable(fieldId, getTypeName(declaringTypeBinding), fCounter)); expression.accept(this); addPopInstruction(); } else { if (declaringTypeBinding == null) { // it is a field without // declaring type => it is // the special length array // field push(new PushArrayLength(fCounter)); } else { if (isALocalType(declaringTypeBinding)) { setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Qualified_local_type_field_access_cannot_be_used_in_an_evaluation_expression_31); return false; } push(new PushFieldVariable(fieldId, getTypeSignature(declaringTypeBinding), fCounter)); } expression.accept(this); } } return false; } /** * @see ASTVisitor#visit(FieldDeclaration) */ @Override public boolean visit(FieldDeclaration node) { return true; } /** * @see ASTVisitor#visit(ForStatement) return <code>false</code>, don't use * the standard accept order. order used for visiting children : * initializers, condition, body, updaters */ @Override public boolean visit(ForStatement node) { if (!isActive()) { return false; } push(new NoOp(fCounter)); push(new NoOp(fCounter)); for (Iterator<Expression> iter = node.initializers().iterator(); iter.hasNext();) { Expression expr = iter.next(); expr.accept(this); addPopInstructionIfNeeded(expr); } storeInstruction(); Expression condition = node.getExpression(); if (condition != null) { condition.accept(this); } node.getBody().accept(this); push(new NoOp(fCounter)); for (Iterator<Expression> iter = node.updaters().iterator(); iter.hasNext();) { Expression expr = iter.next(); expr.accept(this); addPopInstructionIfNeeded(expr); } storeInstruction(); return false; } /** * @see ASTVisitor#visit(IfStatement) */ @Override public boolean visit(IfStatement node) { if (!isActive()) { return false; } push(new NoOp(fCounter)); return true; } /** * @see ASTVisitor#visit(ImportDeclaration) */ @Override public boolean visit(ImportDeclaration node) { return false; } /** * return <code>false</code>, don't use the standard accept order. * * @see ASTVisitor#visit(InfixExpression) */ @Override public boolean visit(InfixExpression node) { if (!isActive()) { return false; } String opToken = node.getOperator().toString(); int opTokenLength = opToken.length(); char char0 = opToken.charAt(0); char char1 = '\0'; char char2 = '\0'; if (opTokenLength > 1) { char1 = opToken.charAt(1); if (opTokenLength > 2) { char2 = opToken.charAt(2); } } List<Expression> extendedOperands = node.extendedOperands(); int operatorNumber = extendedOperands.size() + 1; int[][] types = new int[operatorNumber][3]; Iterator<Expression> iterator = extendedOperands.iterator(); Expression leftOperand = node.getLeftOperand(); Expression rightOperand = node.getRightOperand(); int leftTypeId; int rightTypeId; boolean unbox = false; // for == and != un-box when at least operand is primitive (otherwise // compare the objects) ITypeBinding leftBinding = resolveTypeBinding(leftOperand); if (leftBinding == null) { return false; } ITypeBinding rightBinding = resolveTypeBinding(rightOperand); if (rightBinding == null) { return false; } if ((char0 == '=' || char0 == '!') && char1 == '=') { unbox = leftBinding.isPrimitive() || rightBinding.isPrimitive(); } else { unbox = true; } if (unbox) { leftTypeId = getUnBoxedTypeId(leftOperand); rightTypeId = getUnBoxedTypeId(rightOperand); } else { leftTypeId = getTypeId(leftOperand); rightTypeId = getTypeId(rightOperand); } int resultTypeId = Instruction.getBinaryPromotionType(leftTypeId, rightTypeId); types[0][0] = resultTypeId; types[0][1] = leftTypeId; types[0][2] = rightTypeId; for (int i = 1; i < operatorNumber; i++) { Expression operand = iterator.next(); leftTypeId = resultTypeId; rightTypeId = getUnBoxedTypeId(operand); resultTypeId = Instruction.getBinaryPromotionType(leftTypeId, rightTypeId); types[i][0] = resultTypeId; types[i][1] = leftTypeId; types[i][2] = rightTypeId; } boolean unrecognized = false; switch (char0) { case '*': // multiply for (int i = operatorNumber - 1; i >= 0; i--) { push(new MultiplyOperator(types[i][0], types[i][1], types[i][2], fCounter)); } break; case '/': // divide for (int i = operatorNumber - 1; i >= 0; i--) { push(new DivideOperator(types[i][0], types[i][1], types[i][2], fCounter)); } break; case '%': // remainder for (int i = operatorNumber - 1; i >= 0; i--) { push(new RemainderOperator(types[i][0], types[i][1], types[i][2], fCounter)); } break; case '+': // plus for (int i = operatorNumber - 1; i >= 0; i--) { push(new PlusOperator(types[i][0], types[i][1], types[i][2], fCounter)); } break; case '-': // minus for (int i = operatorNumber - 1; i >= 0; i--) { push(new MinusOperator(types[i][0], types[i][1], types[i][2], fCounter)); } break; case '<': // left shift or less or less equal switch (char1) { case '\0': // less for (int i = operatorNumber - 1; i >= 0; i--) { push(new LessOperator(types[i][1], types[i][2], fCounter)); } break; case '<': // left shift for (int i = operatorNumber - 1; i >= 0; i--) { push(new LeftShiftOperator( Instruction.getUnaryPromotionType(types[i][1]), types[i][1], types[i][2], fCounter)); } break; case '=': // less equal for (int i = operatorNumber - 1; i >= 0; i--) { push(new LessEqualOperator(types[i][1], types[i][2], fCounter)); } break; default: unrecognized = true; break; } break; case '>': // right shift or unsigned right shift or greater or greater // equal switch (char1) { case '\0': // greater for (int i = operatorNumber - 1; i >= 0; i--) { push(new GreaterOperator(types[i][1], types[i][2], fCounter)); } break; case '>': // right shift or unsigned right shift switch (char2) { case '\0': // right shift for (int i = operatorNumber - 1; i >= 0; i--) { push(new RightShiftOperator( Instruction.getUnaryPromotionType(types[i][1]), types[i][1], types[i][2], fCounter)); } break; case '>': // unsigned right shift for (int i = operatorNumber - 1; i >= 0; i--) { push(new UnsignedRightShiftOperator( Instruction.getUnaryPromotionType(types[i][1]), types[i][1], types[i][2], fCounter)); } break; } break; case '=': // greater equal for (int i = operatorNumber - 1; i >= 0; i--) { push(new GreaterEqualOperator(types[i][1], types[i][2], fCounter)); } break; default: unrecognized = true; break; } break; case '=': // equal equal for (int i = operatorNumber - 1; i >= 0; i--) { push(new EqualEqualOperator(types[i][1], types[i][2], true, fCounter)); } break; case '!': // not equal for (int i = operatorNumber - 1; i >= 0; i--) { push(new EqualEqualOperator(types[i][1], types[i][2], false, fCounter)); } break; case '^': // XOr for (int i = operatorNumber - 1; i >= 0; i--) { push(new XorOperator(types[i][0], types[i][1], types[i][2], fCounter)); } break; case '|': // or or or or switch (char1) { case '\0': // or for (int i = operatorNumber - 1; i >= 0; i--) { push(new OrOperator(types[i][0], types[i][1], types[i][2], fCounter)); } break; case '|': // or or for (int i = operatorNumber - 1; i >= 0; i--) { push(new NoOp(fCounter)); } break; default: unrecognized = true; break; } break; case '&': // and or and and switch (char1) { case '\0': // and for (int i = operatorNumber - 1; i >= 0; i--) { push(new AndOperator(types[i][0], types[i][1], types[i][2], fCounter)); } break; case '&': // and and for (int i = operatorNumber - 1; i >= 0; i--) { push(new NoOp(fCounter)); } break; default: unrecognized = true; break; } break; default: unrecognized = true; break; } if (unrecognized) { setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Unrecognized_infix_operator____13 + opToken); } if (hasErrors()) { return false; } iterator = extendedOperands.iterator(); if ((char0 == '&' && char1 == '&') || (char0 == '|' && char1 == '|')) { boolean isOrOr = char0 == '|'; ConditionalJump[] conditionalJumps = new ConditionalJump[operatorNumber]; int[] conditionalJumpAddresses = new int[operatorNumber]; boolean storeRequired = unBoxing(leftBinding); leftOperand.accept(this); if (storeRequired) { storeInstruction(); } ConditionalJump conditionalJump = new ConditionalJump(isOrOr); conditionalJumps[0] = conditionalJump; conditionalJumpAddresses[0] = fCounter; push(conditionalJump); storeInstruction(); storeRequired = unBoxing(rightBinding); rightOperand.accept(this); if (storeRequired) { storeInstruction(); } for (int i = 1; i < operatorNumber; i++) { conditionalJump = new ConditionalJump(isOrOr); conditionalJumps[i] = conditionalJump; conditionalJumpAddresses[i] = fCounter; push(conditionalJump); storeInstruction(); Expression operand = iterator.next(); ITypeBinding typeBinding = resolveTypeBinding(operand); if (typeBinding == null) { return false; } storeRequired = unBoxing(typeBinding); operand.accept(this); if (storeRequired) { storeInstruction(); } } Jump jump = new Jump(); jump.setOffset(1); push(jump); storeInstruction(); for (int i = 0; i < operatorNumber; i++) { conditionalJumps[i].setOffset(fCounter - conditionalJumpAddresses[i] - 1); } push(new PushBoolean(isOrOr)); storeInstruction(); // store the no-op storeInstruction(); } else { // other operators boolean storeRequired = false; if (unbox) { storeRequired = unBoxing(leftBinding); } leftOperand.accept(this); if (storeRequired) { storeInstruction(); } if (unbox) { storeRequired = unBoxing(rightBinding); } rightOperand.accept(this); if (storeRequired) { storeInstruction(); } storeInstruction(); for (int i = 1; i < operatorNumber; i++) { Expression operand = iterator.next(); if (unbox) { ITypeBinding typeBinding = resolveTypeBinding(operand); if (typeBinding == null) { return false; } storeRequired = unBoxing(typeBinding); } operand.accept(this); if (storeRequired) { storeInstruction(); } storeInstruction(); } } return false; } /** * @see ASTVisitor#visit(Initializer) */ @Override public boolean visit(Initializer node) { return true; } /** * @see ASTVisitor#visit(InstanceofExpression) */ @Override public boolean visit(InstanceofExpression node) { if (!isActive()) { return false; } push(new InstanceOfOperator(fCounter)); return true; } /** * @see ASTVisitor#visit(Javadoc) */ @Override public boolean visit(Javadoc node) { return false; } /** * @see ASTVisitor#visit(LabeledStatement) return <code>false</code>, don't * use the standard accept order. */ @Override public boolean visit(LabeledStatement node) { node.getBody().accept(this); return false; } /** * @see ASTVisitor#visit(LambdaExpression) */ @Override public boolean visit(LambdaExpression node) { if (!isActive()) { return true; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Lambda_expressions_cannot_be_used_in_an_evaluation_expression); return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * LineComment) */ @Override public boolean visit(LineComment node) { return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * MarkerAnnotation) */ @Override public boolean visit(MarkerAnnotation node) { return false; } /* * (non-Javadoc) * * @see * org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MemberRef * ) */ @Override public boolean visit(MemberRef node) { return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * MemberValuePair) */ @Override public boolean visit(MemberValuePair node) { return false; } /** * @see ASTVisitor#visit(MethodDeclaration) */ @Override public boolean visit(MethodDeclaration node) { int start = node.getStartPosition(); int end = start + node.getLength(); if (start < fStartPosition && end > fStartPosition) { return true; } return false; } /** * return false, don't visit name, visit expression & arguments * * @see ASTVisitor#visit(MethodInvocation) */ @Override public boolean visit(MethodInvocation node) { if (!isActive()) { return false; } IMethodBinding methodBinding = (IMethodBinding) node.getName().resolveBinding(); if (methodBinding == null) { // could be the receiver is not visible - for example a private // field access from super class ASTNode root = node.getRoot(); if (root instanceof CompilationUnit) { CompilationUnit cu = (CompilationUnit) root; IProblem[] problems = cu.getProblems(); for (IProblem problem : problems) { setHasError(true); addErrorMessage(problem.getMessage()); } } } if (hasErrors()) { return false; } if (containsALocalType(methodBinding)) { setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Method_which_contains_a_local_type_as_parameter_cannot_be_used_in_an_evaluation_expression_32); return false; } int paramCount = methodBinding.getParameterTypes().length; String selector = methodBinding.getName(); String signature = getMethodSignature(methodBinding, null).replace('.', '/'); boolean isStatic = Flags.isStatic(methodBinding.getModifiers()); Expression expression = node.getExpression(); if (isStatic) { String typeName = getTypeName(methodBinding.getDeclaringClass()); push(new SendStaticMessage(typeName, selector, signature, paramCount, fCounter)); if (expression != null) { node.getExpression().accept(this); addPopInstruction(); } } else { push(new SendMessage(selector, signature, paramCount, null, fCounter)); if (expression == null) { push(new PushThis(getEnclosingLevel(node, methodBinding.getDeclaringClass()))); storeInstruction(); } else { node.getExpression().accept(this); } } List<Expression> arguments = node.arguments(); pushMethodArguments(methodBinding, arguments); return false; } /** * Pushes method arguments onto the stack for a method or constructor * invocation taking variable arguments and auto-boxing into consideration. * * @param methodBinding * method or constructor being called * @param arguments * argument list */ private void pushMethodArguments(IMethodBinding methodBinding, List<Expression> arguments) { int argCount = arguments.size(); ITypeBinding[] parameterTypes = methodBinding.getParameterTypes(); int paramCount = parameterTypes.length; ITypeBinding lastArgBinding = null; if (methodBinding.isVarargs()) { Expression lastArg = arguments.get(argCount - 1); lastArgBinding = resolveTypeBinding(lastArg); if (lastArgBinding == null) { return; } } if (methodBinding.isVarargs() && !(paramCount == argCount && parameterTypes[paramCount - 1].getDimensions() == lastArgBinding.getDimensions())) { // if this method is a varargs, and if the method is invoked using // the varargs syntax // (multiple arguments) and not an array Iterator<Expression> iterator = arguments.iterator(); // process the first arguments (no part of the variable argument) for (int i = 0; i < paramCount - 1; i++) { Expression argument = iterator.next(); boolean storeRequired = checkAutoBoxing( argument.resolveTypeBinding(), parameterTypes[i]); argument.accept(this); if (storeRequired) { storeInstruction(); } } // create a array of the remainder arguments ITypeBinding varargsParameterType = parameterTypes[paramCount - 1]; ITypeBinding varargsElementType = varargsParameterType .getElementType(); push(new ArrayInitializerInstruction( getTypeSignature(varargsElementType), argCount - paramCount + 1, varargsParameterType.getDimensions(), fCounter)); while (iterator.hasNext()) { Expression argument = iterator.next(); boolean storeRequired = checkAutoBoxing( argument.resolveTypeBinding(), varargsElementType); argument.accept(this); if (storeRequired) { storeInstruction(); } } storeInstruction(); } else { Iterator<Expression> iterator = arguments.iterator(); int i = 0; while (iterator.hasNext()) { Expression argument = iterator.next(); boolean storeRequired = checkAutoBoxing( argument.resolveTypeBinding(), parameterTypes[i++]); argument.accept(this); if (storeRequired) { storeInstruction(); } } } } /* * (non-Javadoc) * * @see * org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodRef * ) */ @Override public boolean visit(MethodRef node) { return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * MethodRefParameter) */ @Override public boolean visit(MethodRefParameter node) { return false; } /* * (non-Javadoc) * * @see * org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.Modifier * ) */ @Override public boolean visit(Modifier node) { return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * NormalAnnotation) */ @Override public boolean visit(NormalAnnotation node) { return false; } /** * @see ASTVisitor#visit(NullLiteral) */ @Override public boolean visit(NullLiteral node) { if (!isActive()) { return false; } push(new PushNull()); return true; } /** * @see ASTVisitor#visit(NumberLiteral) */ @Override public boolean visit(NumberLiteral node) { if (!isActive()) { return false; } int literalType = getTypeId(node); String token = node.getToken(); int tokenLastCharOffset = token.length() - 1; char lastChar = token.charAt(tokenLastCharOffset); String subToken = token.substring(0, tokenLastCharOffset); switch (literalType) { case Instruction.T_byte: push(new PushInt(parseByteValue(token))); break; case Instruction.T_short: push(new PushInt(parseShortValue(token))); break; case Instruction.T_int: push(new PushInt(parseIntValue(token))); break; case Instruction.T_long: push(new PushLong(parseLongValue(subToken))); break; case Instruction.T_float: push(new PushFloat( Float.parseFloat(removePrefixZerosAndUnderscores(subToken, false)))); break; case Instruction.T_double: if (lastChar == 'D' || lastChar == 'd') { push(new PushDouble( Double.parseDouble(removePrefixZerosAndUnderscores( subToken, false)))); } else { push(new PushDouble( Double.parseDouble(removePrefixZerosAndUnderscores( token, false)))); } break; } return true; } /** * Removes all preamble typing and underscores and returns the base integer * value * * @param token * the token to parse * @return the int value of the token */ public static int parseIntValue(String token) { token = removePrefixZerosAndUnderscores(token, false); switch (getBase(token)) { case 8: return Integer.valueOf(token.substring(1), 8).intValue(); case 16: return Integer.valueOf(token.substring(2), 16).intValue(); case 2: return Integer.valueOf(token.substring(2), 2).intValue(); default: return Integer.valueOf(token, 10).intValue(); } } /** * Removes all preamble typing and underscores and returns the base short * value * * @param token * the token to parse * @return the short value of the token */ public static short parseShortValue(String token) { token = removePrefixZerosAndUnderscores(token, false); switch (getBase(token)) { case 8: return Short.valueOf(token.substring(1), 8).shortValue(); case 16: return Short.valueOf(token.substring(2), 16).shortValue(); case 2: return Short.valueOf(token.substring(2), 2).shortValue(); default: return Short.valueOf(token, 10).shortValue(); } } /** * Removes all preamble typing and underscores and returns the base byte * value * * @param token * the token to parse * @return the byte value of the token */ public static byte parseByteValue(String token) { token = removePrefixZerosAndUnderscores(token, false); switch (getBase(token)) { case 8: return Byte.valueOf(token.substring(1), 8).byteValue(); case 16: return Byte.valueOf(token.substring(2), 16).byteValue(); case 2: return Byte.valueOf(token.substring(2), 2).byteValue(); default: return Byte.valueOf(token, 10).byteValue(); } } /** * Removes all preamble typing and underscores and returns the base long * value * * @param token * the token to parse * @return the long value of the token */ public static long parseLongValue(String token) { token = removePrefixZerosAndUnderscores(token, true); switch (getBase(token)) { case 8: return Long.valueOf(token.substring(1), 8).longValue(); case 16: return Long.valueOf(token.substring(2), 16).longValue(); case 2: return Long.valueOf(token.substring(2), 2).longValue(); default: return Long.valueOf(token, 10).longValue(); } } /** * Returns the numeric base for the given token according to the Java * specification. Returns 8, 10, or 16. * * @param token * the token to get the base from * @return the numeric base for the given token */ public static int getBase(String token) { if (token.charAt(0) == '0' && (token.length() > 1)) { switch (token.charAt(1)) { case 'x': case 'X': // "0x" prefix: Hexadecimal return 16; case 'b': case 'B': // "0b" prefix: binary return 2; default: // "0" prefix: Octal return 8; } } return 10; // No prefix: Decimal } /** * @see ASTVisitor#visit(PackageDeclaration) */ @Override public boolean visit(PackageDeclaration node) { return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * ParameterizedType) */ @Override public boolean visit(ParameterizedType node) { if (!isActive()) { return false; } ITypeBinding typeBinding = resolveTypeBinding(node); if (typeBinding != null) { push(new PushType(getTypeName(typeBinding))); } return false; } /** * @see ASTVisitor#visit(ParenthesizedExpression) */ @Override public boolean visit(ParenthesizedExpression node) { if (!isActive()) { return false; } return true; } /** * @see ASTVisitor#visit(PostfixExpression) */ @Override public boolean visit(PostfixExpression node) { if (!isActive()) { return false; } Expression operand = node.getOperand(); int expressionTypeId = getTypeId(operand); String opToken = node.getOperator().toString(); char char0 = opToken.charAt(0); if (expressionTypeId == Instruction.T_Object) { int expressionUnBoxedTypeId = getUnBoxedTypeId(operand); AssignmentOperator assignmentInstruction = new AssignmentOperator( Instruction.T_Object, Instruction.T_Object, fCounter); push(assignmentInstruction); operand.accept(this); switch (char0) { case '+': // plus plus push(new PlusOperator(expressionUnBoxedTypeId, expressionUnBoxedTypeId, expressionUnBoxedTypeId, fCounter)); break; case '-': // minus minus push(new MinusOperator(expressionUnBoxedTypeId, expressionUnBoxedTypeId, expressionUnBoxedTypeId, fCounter)); break; default: setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_unrecognized_postfix_operator____15 + opToken); return false; } push(new Value(fCounter)); push(new Dup()); storeInstruction(); // dupe storeInstruction(); // value push(new DupX1()); storeInstruction(); // dup_x1 ITypeBinding typeBinding = resolveTypeBinding(operand); if (typeBinding == null) { return false; } unBoxing(typeBinding); storeInstruction(); // un-boxing push(new PushInt(1)); storeInstruction(); // push 1 storeInstruction(); // operator boxing(typeBinding, null); storeInstruction(); // boxing storeInstruction(); // assignment push(new Pop(assignmentInstruction.getSize() + 1)); return false; } switch (char0) { case '+': // plus plus push(new PostfixPlusPlusOperator(expressionTypeId, fCounter)); break; case '-': // minus minus push(new PostfixMinusMinusOperator(expressionTypeId, fCounter)); break; default: setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_unrecognized_postfix_operator____15 + opToken); return false; } return true; } /** * @see ASTVisitor#visit(PrefixExpression) */ @Override public boolean visit(PrefixExpression node) { if (!isActive()) { return false; } Expression operand = node.getOperand(); int expressionTypeId = getTypeId(operand); String opToken = node.getOperator().toString(); int opTokenLength = opToken.length(); char char0 = opToken.charAt(0); char char1 = '\0'; if (opTokenLength > 1) { char1 = opToken.charAt(1); } boolean unrecognized = false; if (expressionTypeId == Instruction.T_Object) { int expressionUnBoxedTypeId = getUnBoxedTypeId(operand); ITypeBinding typeBinding = resolveTypeBinding(operand); if (typeBinding == null) { return false; } if (char1 == '\0') { switch (char0) { case '+': // unary plus push(new UnaryPlusOperator(expressionUnBoxedTypeId, fCounter)); break; case '-': // unary minus push(new UnaryMinusOperator(expressionUnBoxedTypeId, fCounter)); break; case '~': // twiddle push(new TwiddleOperator(expressionUnBoxedTypeId, fCounter)); break; case '!': // not push(new NotOperator(expressionUnBoxedTypeId, fCounter)); break; default: setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_unrecognized_prefix_operator____16 + opToken); return false; } unBoxing(typeBinding); operand.accept(this); storeInstruction(); // un-boxing } else { // plus plus and minus minus operators push(new AssignmentOperator(Instruction.T_Object, Instruction.T_Object, fCounter)); operand.accept(this); boxing(typeBinding, null); switch (char1) { case '+': push(new PlusOperator(expressionUnBoxedTypeId, expressionUnBoxedTypeId, expressionUnBoxedTypeId, fCounter)); break; case '-': push(new MinusOperator(expressionUnBoxedTypeId, expressionUnBoxedTypeId, expressionUnBoxedTypeId, fCounter)); break; default: setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_unrecognized_prefix_operator____16 + opToken); return false; } unBoxing(typeBinding); push(new Dup()); storeInstruction(); // dupe storeInstruction(); // un-boxing push(new PushInt(1)); storeInstruction(); // push 1 storeInstruction(); // operator storeInstruction(); // boxing } return false; } switch (char0) { case '+': // plus plus or unary plus switch (char1) { case '\0': // unary plus push(new UnaryPlusOperator(expressionTypeId, fCounter)); break; case '+': // plus plus push(new PrefixPlusPlusOperator(expressionTypeId, fCounter)); break; default: unrecognized = true; break; } break; case '-': // minus minus or unary minus switch (char1) { case '\0': // unary minus push(new UnaryMinusOperator(expressionTypeId, fCounter)); break; case '-': // minus minus push(new PrefixMinusMinusOperator(expressionTypeId, fCounter)); break; default: unrecognized = true; break; } break; case '~': // twiddle push(new TwiddleOperator(expressionTypeId, fCounter)); break; case '!': // not push(new NotOperator(expressionTypeId, fCounter)); break; default: unrecognized = true; break; } if (unrecognized) { setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_unrecognized_prefix_operator____16 + opToken); return false; } return true; } /** * @see ASTVisitor#visit(PrimitiveType) */ @Override public boolean visit(PrimitiveType node) { if (!isActive()) { return false; } ITypeBinding typeBinding = resolveTypeBinding(node); if (typeBinding != null) { push(new PushPrimitiveType(getTypeName(typeBinding))); } return false; } /** * @see ASTVisitor#visit(QualifiedName) */ @Override public boolean visit(QualifiedName node) { if (!isActive()) { return false; } if (hasErrors()) { return true; } IBinding binding = resolveBinding(node); if (binding == null) { return false; } switch (binding.getKind()) { case IBinding.TYPE: node.getName().accept(this); break; case IBinding.VARIABLE: SimpleName fieldName = node.getName(); IVariableBinding fieldBinding = (IVariableBinding) resolveBinding(fieldName); if (fieldBinding == null) { return false; } ITypeBinding declaringTypeBinding = fieldBinding .getDeclaringClass(); String fieldId = fieldName.getIdentifier(); if (Modifier.isStatic(fieldBinding.getModifiers())) { push(new PushStaticFieldVariable(fieldId, getTypeName(declaringTypeBinding), fCounter)); } else { if (declaringTypeBinding == null) { push(new PushArrayLength(fCounter)); } else { push(new PushFieldVariable(fieldId, getTypeSignature(declaringTypeBinding), fCounter)); } node.getQualifier().accept(this); } storeInstruction(); break; } return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * QualifiedType) */ @Override public boolean visit(QualifiedType node) { if (!isActive()) { return false; } ITypeBinding typeBinding = resolveTypeBinding(node); if (typeBinding != null) { push(new PushType(getTypeName(typeBinding))); } return false; } /** * @see ASTVisitor#visit(ReturnStatement) */ @Override public boolean visit(ReturnStatement node) { if (!isActive()) { return false; } push(new ReturnInstruction(fCounter)); return true; } /** * @see ASTVisitor#visit(SimpleName) */ @Override public boolean visit(SimpleName node) { if (!isActive()) { return false; } if (hasErrors()) { return true; } IBinding binding = resolveBinding(node); if (binding == null) { return true; } String variableId = node.getIdentifier(); switch (binding.getKind()) { case IBinding.TYPE: ITypeBinding typeBinding = (ITypeBinding) binding; push(new PushType(getTypeName(typeBinding))); break; case IBinding.VARIABLE: IVariableBinding variableBinding = (IVariableBinding) binding; ITypeBinding declaringTypeBinding = variableBinding .getDeclaringClass(); if (variableBinding.isField()) { if (Modifier.isStatic(variableBinding.getModifiers())) { push(new PushStaticFieldVariable(variableId, getTypeName(declaringTypeBinding), fCounter)); } else { push(new PushFieldVariable(variableId, getTypeSignature(declaringTypeBinding), fCounter)); push(new PushThis(getEnclosingLevel(node, declaringTypeBinding))); storeInstruction(); } } else { push(new PushLocalVariable(variableId)); } break; } return true; } /** * return false, don't visit child * * @see ASTVisitor#visit(SimpleType) */ @Override public boolean visit(SimpleType node) { if (!isActive()) { return false; } ITypeBinding typeBinding = resolveTypeBinding(node); if (typeBinding != null) { push(new PushType(getTypeName(typeBinding))); } return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * SingleMemberAnnotation) */ @Override public boolean visit(SingleMemberAnnotation node) { return false; } /** * @see ASTVisitor#visit(SingleVariableDeclaration) return * <code>false</code>, don't use the standard accept order. */ @Override public boolean visit(SingleVariableDeclaration node) { if (!isActive()) { return false; } ITypeBinding typeBinding = resolveTypeBinding(node.getType()); if (typeBinding != null) { int typeDimension = typeBinding.getDimensions(); if (typeDimension != 0) { typeBinding = typeBinding.getElementType(); } Expression initializer = node.getInitializer(); push(new LocalVariableCreation(node.getName().getIdentifier(), getTypeSignature(typeBinding), typeDimension, typeBinding.isPrimitive(), initializer != null, fCounter)); if (initializer != null) { initializer.accept(this); } } return false; } /** * @see ASTVisitor#visit(StringLiteral) */ @Override public boolean visit(StringLiteral node) { if (!isActive()) { return false; } push(new PushString(node.getLiteralValue())); return true; } /** * @see ASTVisitor#visit(SuperConstructorInvocation) */ @Override public boolean visit(SuperConstructorInvocation node) { if (!isActive()) { return false; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_super_constructor_invocation_cannot_be_used_in_an_evaluation_expression_19); return false; } /** * @see ASTVisitor#visit(SuperFieldAccess) */ @Override public boolean visit(SuperFieldAccess node) { if (!isActive()) { return false; } SimpleName fieldName = node.getName(); IVariableBinding fieldBinding = (IVariableBinding) resolveBinding(fieldName); if (fieldBinding == null) { return false; } ITypeBinding declaringTypeBinding = fieldBinding.getDeclaringClass(); String fieldId = fieldName.getIdentifier(); if (Modifier.isStatic(fieldBinding.getModifiers())) { push(new PushStaticFieldVariable(fieldId, getTypeName(declaringTypeBinding), fCounter)); } else { Name qualifier = node.getQualifier(); int superLevel = 1; int enclosingLevel = 0; if (qualifier != null) { ITypeBinding typeBinding = resolveTypeBinding(qualifier); if (typeBinding == null) { return false; } superLevel = getSuperLevel(typeBinding, declaringTypeBinding); ITypeBinding binding = (ITypeBinding) resolveBinding(qualifier); if (binding == null) { return false; } enclosingLevel = getEnclosingLevel(node, binding); } push(new PushFieldVariable(fieldId, superLevel, fCounter)); push(new PushThis(enclosingLevel)); storeInstruction(); } return false; } /** * return false, don't visit name, visit arguments * * @see ASTVisitor#visit(SuperMethodInvocation) */ @Override public boolean visit(SuperMethodInvocation node) { if (!isActive()) { return false; } IMethodBinding methodBinding = (IMethodBinding) resolveBinding(node .getName()); if (methodBinding == null) { return false; } if (containsALocalType(methodBinding)) { setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Method_which_contains_a_local_type_as_parameter_cannot_be_used_in_an_evaluation_expression_32); return false; } ITypeBinding[] parameterTypes = methodBinding.getParameterTypes(); int paramCount = parameterTypes.length; String selector = methodBinding.getName(); String signature = getMethodSignature(methodBinding, null); Name qualifier = node.getQualifier(); if (Modifier.isStatic(methodBinding.getModifiers())) { push(new SendStaticMessage( getTypeName(methodBinding.getDeclaringClass()), selector, signature, paramCount, fCounter)); } else { push(new SendMessage(selector, signature, paramCount, getTypeSignature(methodBinding.getDeclaringClass()), fCounter)); int enclosingLevel = 0; if (qualifier != null) { ITypeBinding typeBinding = (ITypeBinding) resolveBinding(qualifier); if (typeBinding == null) { return false; } enclosingLevel = getEnclosingLevel(node, typeBinding); } push(new PushThis(enclosingLevel)); storeInstruction(); } List<Expression> arguments = node.arguments(); int argCount = arguments.size(); ITypeBinding lastArgBinding = null; if (methodBinding.isVarargs()) { lastArgBinding = resolveTypeBinding(arguments.get(argCount - 1)); if (lastArgBinding == null) { return false; } } if (methodBinding.isVarargs() && !(paramCount == argCount && parameterTypes[paramCount - 1].getDimensions() == lastArgBinding.getDimensions())) { // if this method is a varargs, and if the method is invoked using // the varargs syntax // (multiple arguments) and not an array Iterator<Expression> iterator = arguments.iterator(); // process the first arguments (no part of the variable argument) for (int i = 0; i < paramCount - 1; i++) { Expression argument = iterator.next(); boolean storeRequired = checkAutoBoxing( argument.resolveTypeBinding(), parameterTypes[i]); argument.accept(this); if (storeRequired) { storeInstruction(); } } // create a array of the remainder arguments ITypeBinding varargsParameterType = parameterTypes[paramCount - 1]; ITypeBinding varargsElementType = varargsParameterType .getElementType(); push(new ArrayInitializerInstruction( getTypeSignature(varargsElementType), argCount - paramCount + 1, varargsParameterType.getDimensions(), fCounter)); while (iterator.hasNext()) { Expression argument = iterator.next(); boolean storeRequired = checkAutoBoxing( argument.resolveTypeBinding(), varargsElementType); argument.accept(this); if (storeRequired) { storeInstruction(); } } storeInstruction(); } else { Iterator<Expression> iterator = arguments.iterator(); int i = 0; while (iterator.hasNext()) { Expression argument = iterator.next(); boolean storeRequired = checkAutoBoxing( argument.resolveTypeBinding(), parameterTypes[i++]); argument.accept(this); if (storeRequired) { storeInstruction(); } } } return false; } /** * @see ASTVisitor#visit(SuperMethodReference) */ @Override public boolean visit(SuperMethodReference node) { if (!isActive()) { return true; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Reference_expressions_cannot_be_used_in_an_evaluation_expression); return false; } /** * @see ASTVisitor#visit(SwitchCase) */ @Override public boolean visit(SwitchCase node) { // never called return false; } class slot { ArrayList<ConditionalJump> jumps = new ArrayList<>(); ArrayList<Statement> stmts = null; } /** * @see ASTVisitor#visit(SwitchStatement) */ @Override public boolean visit(SwitchStatement node) { if (!isActive()) { return false; } push(new NoOp(fCounter)); int switchStart = fCounter; node.getExpression().accept(this); ArrayList<Statement> statementsDefault = null; Jump jumpDefault = null; ArrayList<slot> jumpsStatements = new ArrayList<>(); slot currentslot = new slot(); jumpsStatements.add(currentslot); for (Iterator<Statement> iter = node.statements().iterator(); iter.hasNext();) { Statement statement = iter.next(); if (statement instanceof SwitchCase) { SwitchCase switchCase = (SwitchCase) statement; if (switchCase.isDefault()) { jumpDefault = new Jump(); push(jumpDefault); storeInstruction(); // jump statementsDefault = new ArrayList<>(); } else { if (switchCase.getExpression() instanceof StringLiteral) { push(new SendMessage( "equals", "(Ljava/lang/Object;)Z", 1, null, fCounter)); //$NON-NLS-1$ //$NON-NLS-2$ } else { push(new EqualEqualOperator(Instruction.T_int, Instruction.T_int, true, fCounter)); } push(new Dup()); storeInstruction(); // dupe switchCase.getExpression().accept(this); storeInstruction(); // equal-equal ConditionalJump condJump = new ConditionalJump(true); push(condJump); storeInstruction(); // conditional jump if (currentslot.stmts != null) { currentslot = new slot(); jumpsStatements.add(currentslot); } currentslot.jumps.add(condJump); } } else { if (statementsDefault != null) { statementsDefault.add(statement); } else { if (currentslot.stmts == null) { currentslot.stmts = new ArrayList<>(); } currentslot.stmts.add(statement); } } } Jump jumpEnd = null; if (jumpDefault == null) { push(new Pop(0)); storeInstruction(); // pop jumpEnd = new Jump(); push(jumpEnd); storeInstruction(); // jump } for (Iterator<slot> iter = jumpsStatements.iterator(); iter.hasNext();) { currentslot = iter.next(); for (Iterator<ConditionalJump> iterator = currentslot.jumps.iterator(); iterator.hasNext();) { ConditionalJump condJump = iterator.next(); condJump.setOffset((fCounter - fInstructions.indexOf(condJump)) - 1); } if (currentslot.stmts != null) { push(new Pop(0)); storeInstruction(); // pop for (Iterator<Statement> iterator = currentslot.stmts.iterator(); iterator.hasNext();) { iterator.next().accept(this); } } } // default case if (jumpDefault != null) { jumpDefault.setOffset((fCounter - fInstructions.indexOf(jumpDefault)) - 1); push(new Pop(0)); storeInstruction(); // pop for (Iterator<Statement> iterator = statementsDefault.iterator(); iterator.hasNext();) { iterator.next().accept(this); } } else if(jumpEnd != null){ jumpEnd.setOffset((fCounter - fInstructions.indexOf(jumpEnd)) - 1); } // for each pending break or continue instruction which are related to // this loop, set the offset of the corresponding jump. String label = getLabel(node); for (Iterator<CompleteInstruction> iter = fCompleteInstructions.iterator(); iter.hasNext();) { CompleteInstruction instruction = iter.next(); Jump jumpInstruction = instruction.fInstruction; int instructionAddress = fInstructions.indexOf(jumpInstruction); if (instructionAddress > switchStart && (instruction.fLabel == null || instruction.fLabel.equals(label))) { iter.remove(); if (instruction.fIsBreak) { // jump to the instruction after the last instruction of the // switch jumpInstruction.setOffset((fCounter - instructionAddress) - 1); } } } return false; } /** * @see ASTVisitor#visit(SynchronizedStatement) */ @Override public boolean visit(SynchronizedStatement node) { if (!isActive()) { return false; } return true; } /* * (non-Javadoc) * * @see * org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.TagElement * ) */ @Override public boolean visit(TagElement node) { return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * TextElement) */ @Override public boolean visit(TextElement node) { return false; } /** * @see ASTVisitor#visit(ThisExpression) */ @Override public boolean visit(ThisExpression node) { if (!isActive()) { return false; } Name qualifier = node.getQualifier(); int enclosingLevel = 0; if (qualifier != null) { ITypeBinding binding = (ITypeBinding) resolveBinding(qualifier); if (binding == null) { return false; } enclosingLevel = getEnclosingLevel(node, binding); } push(new PushThis(enclosingLevel)); return false; } /** * @see ASTVisitor#visit(ThrowStatement) */ @Override public boolean visit(ThrowStatement node) { if (!isActive()) { return false; } push(new ThrowInstruction(fCounter)); return true; } /** * @see ASTVisitor#visit(TryStatement) */ @Override public boolean visit(TryStatement node) { if (!isActive()) { return false; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Try_statement_cannot_be_used_in_an_evaluation_expression_23); return false; } /** * @see ASTVisitor#visit(TypeDeclaration) */ @Override public boolean visit(TypeDeclaration node) { if (!isActive()) { return true; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Type_declaration_cannot_be_used_in_an_evaluation_expression_24); return false; } /** * @see ASTVisitor#visit(TypeDeclarationStatement) */ @Override public boolean visit(TypeDeclarationStatement node) { if (!isActive()) { return true; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Type_declaration_statement_cannot_be_used_in_an_evaluation_expression_25); return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * TypeParameter) */ @Override public boolean visit(TypeParameter node) { return false; } /** * @see ASTVisitor#visit(TypeLiteral) */ @Override public boolean visit(TypeLiteral node) { if (!isActive()) { return false; } push(new PushClassLiteralValue(fCounter)); return true; } /** * @see ASTVisitor#visit(TypeMethodReference) */ @Override public boolean visit(TypeMethodReference node) { if (!isActive()) { return true; } setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Reference_expressions_cannot_be_used_in_an_evaluation_expression); return false; } /** * @see ASTVisitor#visit(VariableDeclarationExpression) */ @Override public boolean visit(VariableDeclarationExpression node) { /* * if it is in the code to execute, return <code>false</code>, we don't * use the standard accept order. Otherwise, return true. We want to * search the code to execute in variable declarations (in case of inner * classes). */ if (!isActive()) { return true; } for (Iterator<VariableDeclarationFragment> iter = node.fragments().iterator(); iter.hasNext();) { iter.next().accept(this); } return false; } /** * @see ASTVisitor#visit(VariableDeclarationFragment) */ @Override public boolean visit(VariableDeclarationFragment node) { /* * if it is in the code to execute, return <code>false</code>, we don't * use the standard accept order. Otherwise, return true. We want to * search the code to execute in variable declarations (in case of inner * classes). */ if (!isActive()) { return true; } // get the type of the variable ITypeBinding varTypeBinding; ASTNode parent = node.getParent(); switch (parent.getNodeType()) { case ASTNode.VARIABLE_DECLARATION_EXPRESSION: varTypeBinding = resolveTypeBinding(((VariableDeclarationExpression) parent) .getType()); break; case ASTNode.VARIABLE_DECLARATION_STATEMENT: varTypeBinding = resolveTypeBinding(((VariableDeclarationStatement) parent) .getType()); break; default: setHasError(true); addErrorMessage(EvaluationEngineMessages.ASTInstructionCompiler_Error_in_type_declaration_statement); return false; } if (varTypeBinding == null) { return false; } int typeDimension = varTypeBinding.getDimensions(); ITypeBinding elementBinding = varTypeBinding; if (typeDimension != 0) { elementBinding = elementBinding.getElementType(); } Expression initializer = node.getInitializer(); push(new LocalVariableCreation(node.getName().getIdentifier(), getTypeSignature(elementBinding), typeDimension, elementBinding.isPrimitive(), initializer != null, fCounter)); if (initializer != null) { initializer.accept(this); ITypeBinding expBindnig = initializer.resolveTypeBinding(); if (expBindnig != null) { if (checkAutoBoxing(expBindnig, varTypeBinding)) { storeInstruction(); } } } return false; } /** * @see ASTVisitor#visit(VariableDeclarationStatement) */ @Override public boolean visit(VariableDeclarationStatement node) { /* * if it is in the code to execute, return <code>false</code>, we don't * use the standard accept order. Otherwise, return true. We want to * search the code to execute in variable declarations (in case of inner * classes). */ if (!isActive()) { return true; } for (Iterator<VariableDeclarationFragment> iter = node.fragments().iterator(); iter.hasNext();) { iter.next().accept(this); } return false; } /* * (non-Javadoc) * * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom. * WildcardType) */ @Override public boolean visit(WildcardType node) { // we shouldn't have to do anything return false; } /** * @see ASTVisitor#visit(WhileStatement) */ @Override public boolean visit(WhileStatement node) { if (!isActive()) { return false; } push(new NoOp(fCounter)); return true; } // -------------------------- private int getTypeId(Expression expression) { ITypeBinding typeBinding = expression.resolveTypeBinding(); if (typeBinding == null) { return Instruction.T_undefined; } String typeName = typeBinding.getQualifiedName(); if (typeBinding.isPrimitive()) { return getPrimitiveTypeId(typeName); } else if ("java.lang.String".equals(typeName)) { //$NON-NLS-1$ return Instruction.T_String; } else { return Instruction.T_Object; } } private int getUnBoxedTypeId(Expression expression) { ITypeBinding typeBinding = expression.resolveTypeBinding(); if (typeBinding == null) { return Instruction.T_undefined; } String typeName = typeBinding.getQualifiedName(); if (typeBinding.isPrimitive()) { return getPrimitiveTypeId(typeName); } else if ("java.lang.String".equals(typeName)) { //$NON-NLS-1$ return Instruction.T_String; } else { // un-boxing if ("java.lang.Integer".equals(typeName)) { //$NON-NLS-1$ return Instruction.T_int; } else if ("java.lang.Character".equals(typeName)) { //$NON-NLS-1$ return Instruction.T_char; } else if ("java.lang.Byte".equals(typeName)) { //$NON-NLS-1$ return Instruction.T_byte; } else if ("java.lang.Short".equals(typeName)) { //$NON-NLS-1$ return Instruction.T_short; } else if ("java.lang.Long".equals(typeName)) { //$NON-NLS-1$ return Instruction.T_long; } else if ("java.lang.Float".equals(typeName)) { //$NON-NLS-1$ return Instruction.T_float; } else if ("java.lang.Double".equals(typeName)) { //$NON-NLS-1$ return Instruction.T_double; } else if ("java.lang.Boolean".equals(typeName)) { //$NON-NLS-1$ return Instruction.T_boolean; } return Instruction.T_Object; } } private int getTypeId(Type type) { if (type.isPrimitiveType()) { return getPrimitiveTypeId(((PrimitiveType) type) .getPrimitiveTypeCode().toString()); } else if (type.isSimpleType()) { SimpleType simpleType = (SimpleType) type; if ("java.lang.String".equals(simpleType.getName().getFullyQualifiedName())) { //$NON-NLS-1$ return Instruction.T_String; } return Instruction.T_Object; } else if (type.isArrayType()) { return Instruction.T_Object; } else if (type.isParameterizedType()) { return Instruction.T_Object; } else { return Instruction.T_undefined; } } public static String removePrefixZerosAndUnderscores(String tokenString, boolean isLong) { char[] token = tokenString.toCharArray(); int max = token.length; int start = 0; int end = max - 1; if (isLong) { end--; // remove the 'L' or 'l' } if (max > 1 && token[0] == '0') { if (max > 2 && (token[1] == 'x' || token[1] == 'X')) { start = 2; } else if (max > 2 && (token[1] == 'b' || token[1] == 'B')) { start = 2; } else { start = 1; } } boolean modified = false; boolean ignore = true; loop: for (int i = start; i < max; i++) { char currentChar = token[i]; switch (currentChar) { case '0': // this is a prefix '0' if (ignore && !modified && (i < end)) { modified = true; } break; case '_': modified = true; break loop; default: ignore = false; } } if (!modified) { return tokenString; } ignore = true; StringBuffer buffer = new StringBuffer(); buffer.append(token, 0, start); loop: for (int i = start; i < max; i++) { char currentChar = token[i]; switch (currentChar) { case '0': if (ignore && (i < end)) { // this is a prefix '0' continue loop; } break; case '_': continue loop; default: ignore = false; } buffer.append(currentChar); } return buffer.toString(); } /** * Returns the method signature given the binding and the enclosing type * signature (if there is one) * * @param methodBinding * the binding to get the signature for * @param enclosingTypeSignature * the enclosing type signature or <code>null</code> * @return the method signature for the given binding and enclosing type * signature */ private String getMethodSignature(IMethodBinding methodBinding, String enclosingTypeSignature) { methodBinding = methodBinding.getMethodDeclaration(); ITypeBinding[] parameterTypes = methodBinding.getParameterTypes(); int offset = 0; int argCount; String[] parameterSignatures; if (enclosingTypeSignature == null) { argCount = parameterTypes.length; parameterSignatures = new String[argCount]; } else { offset = 1; argCount = parameterTypes.length + 1; parameterSignatures = new String[argCount]; parameterSignatures[0] = enclosingTypeSignature; } for (int i = 0; i < parameterTypes.length; i++) { parameterSignatures[i + offset] = getTypeSignature(parameterTypes[i]); } String signature = Signature.createMethodSignature(parameterSignatures, getTypeSignature(methodBinding.getReturnType())); return signature; } private int getPrimitiveTypeId(String typeName) { switch (typeName.charAt(0)) { case 'b': // byte or boolean switch (typeName.charAt(1)) { case 'o': // boolean; return Instruction.T_boolean; case 'y': // byte return Instruction.T_byte; } break; case 'c': // char return Instruction.T_char; case 'd': // double return Instruction.T_double; case 'f': // float return Instruction.T_float; case 'i': // int return Instruction.T_int; case 'l': // long return Instruction.T_long; case 'n': return Instruction.T_null; case 's': // short return Instruction.T_short; case 'v': // void return Instruction.T_void; } return Instruction.T_undefined; } /** * Resolves and returns the type binding from the given expression reporting * an error if the binding is <code>null</code>. * * @param expression * expression to resolve type binding for * @return type binding or <code>null</code> if not available */ private ITypeBinding resolveTypeBinding(Expression expression) { ITypeBinding typeBinding = expression.resolveTypeBinding(); if (typeBinding == null) { setHasError(true); addErrorMessage(MessageFormat.format( EvaluationEngineMessages.ASTInstructionCompiler_3, new Object[] { expression.toString() })); } return typeBinding; } /** * Resolves and returns the type binding for the give type reporting an * error if the binding is <code>null</code>. * * @param type * type to resolve binding for * @return type binding or <code>null</code> if not available */ private ITypeBinding resolveTypeBinding(Type type) { ITypeBinding typeBinding = type.resolveBinding(); if (typeBinding == null) { setHasError(true); addErrorMessage(MessageFormat.format( EvaluationEngineMessages.ASTInstructionCompiler_3, new Object[] { type.toString() })); } return typeBinding; } /** * Resolves and returns the binding for the given name reporting an error if * the binding is <code>null</code>. * * @param name * name to resolve binding for * @return binding or <code>null</code> if not available */ private IBinding resolveBinding(Name name) { IBinding binding = name.resolveBinding(); if (binding == null) { setHasError(true); addErrorMessage(MessageFormat.format( EvaluationEngineMessages.ASTInstructionCompiler_5, new Object[] { name.getFullyQualifiedName() })); } return binding; } /** * Resolves and returns the type binding for the given name reporting an * error if the binding is <code>null</code>. * * @param name * name to resolve type binding for * @return type binding or <code>null</code> if not available */ private ITypeBinding resolveTypeBinding(Name name) { ITypeBinding typeBinding = name.resolveTypeBinding(); if (typeBinding == null) { setHasError(true); addErrorMessage(MessageFormat.format( EvaluationEngineMessages.ASTInstructionCompiler_3, new Object[] { name.getFullyQualifiedName() })); } return typeBinding; } }