package bluej.parser;

import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Stack;

import bluej.debugger.gentype.GenTypeClass;
import bluej.debugger.gentype.GenTypeDeclTpar;
import bluej.debugger.gentype.GenTypeParameter;
import bluej.debugger.gentype.GenTypeSolid;
import bluej.debugger.gentype.JavaPrimitiveType;
import bluej.debugger.gentype.JavaType;
import bluej.debugger.gentype.Reflective;
import bluej.parser.TextAnalyzer.MethodCallDesc;
import bluej.parser.entity.ConstantBoolValue;
import bluej.parser.entity.ConstantFloatValue;
import bluej.parser.entity.ConstantIntValue;
import bluej.parser.entity.ConstantStringEntity;
import bluej.parser.entity.EntityResolver;
import bluej.parser.entity.ErrorEntity;
import bluej.parser.entity.JavaEntity;
import bluej.parser.entity.NullEntity;
import bluej.parser.entity.PackageOrClass;
import bluej.parser.entity.SolidTargEntity;
import bluej.parser.entity.TypeArgumentEntity;
import bluej.parser.entity.TypeEntity;
import bluej.parser.entity.UnboundedWildcardEntity;
import bluej.parser.entity.UnresolvedEntity;
import bluej.parser.entity.ValueEntity;
import bluej.parser.entity.WildcardExtendsEntity;
import bluej.parser.entity.WildcardSuperEntity;
import bluej.parser.lexer.JavaTokenTypes;
import bluej.parser.lexer.LocatableToken;
import bluej.utility.JavaReflective;
import threadchecker.OnThread;
import threadchecker.Tag;


| A parser for the codepad. | | @author Davin McCall | public class TextParser extends JavaParser{ private EntityResolver resolver; private JavaEntity accessType; private boolean staticAccess; protected Stack<JavaEntity> valueStack = new Stack<JavaEntity>(); private int arrayCount = 0;
| A class to represent operators, possibly associated with a token | protected class Operator { int type; LocatableToken token; Operator(int type, LocatableToken token) { this.type = type; this.token = token; } int getType() { return type; } LocatableToken getToken() { return token; } } protected Stack<Operator> operatorStack = new Stack<Operator>();
| A stack of type argument lists - one list for each method call on the operator stack | protected Stack<List<LocatableToken>> typeArgStack = new Stack<List<LocatableToken>>();
| | Make up some operators - only for cases where there is not a token | type we could use, or for where the token is ambiguos: | private static final int CAST_OPERATOR = JavaTokenTypes.INVALID + 1; private static final int BAD_CAST_OPERATOR = CAST_OPERATOR + 1; private static final int PAREN_OPERATOR = BAD_CAST_OPERATOR + 1; private static final int MEMBER_CALL_OP = PAREN_OPERATOR + 1; private static final int METHOD_CALL_OP = MEMBER_CALL_OP + 1; private static final int CONSTRUCTOR_CALL_OP = METHOD_CALL_OP + 1; private static final int UNARY_PLUS_OP = CONSTRUCTOR_CALL_OP + 1; private static final int UNARY_MINUS_OP = UNARY_PLUS_OP + 1;
| | Parse states - mainly so we know what to do with a type specification | when we seen one: | private static final int STATE_NONE = 0; private static final int STATE_NEW = 1; private static final int STATE_NEW_ARGS = 2; private static final int STATE_INSTANCEOF = 3; private int state = STATE_NONE;
| Arguments for a method or constructor call are added to the list at the top of this stack | private Stack<List<JavaEntity>> argumentStack = new Stack<List<JavaEntity>>();
| Construct a text parser for parsing an expression. | | @param resolver Resolver to resolve symbols | @param r Reader to read the expression source | @param accessType The containing type | @param staticAccess Whether the expression occurs in a static context | public TextParser(EntityResolver resolver, Reader r, JavaEntity accessType, boolean staticAccess) { super(r); this.resolver = resolver; this.accessType = accessType; this.staticAccess = staticAccess; }
| Construct a text parser for parsing an expression, where the expression is located | at a particular line and column in the source. | | @param resolver Resolver to resolve symbols | @param r Reader to read the expression source | @param accessType The containing type | @param staticAccess Whether the expression occurs in a static context | @param line The line in the source where the expression occurs | @param col The column in the source where the expression occurs | public TextParser(EntityResolver resolver, Reader r, JavaEntity accessType, boolean staticAccess, int line, int col, int pos) { super(r, line, col, pos); this.resolver = resolver; this.accessType = accessType; this.staticAccess = staticAccess; }
| Construct a text parser for parsing an expression which is represented in a String. | | @param resolver Resolver to resolve symbols | @param s A string containing the expression | @param accessType The containing type | @param staticAccess Whether the expression occurs in a static context | public TextParser(EntityResolver resolver, String s, JavaEntity accessType, boolean staticAccess) { this(resolver, new StringReader(s), accessType, staticAccess); }
| Check whether the parsed expression ended at the end of the input. | | @return true iff the parsed expression ended at the end of the input (reader or string); | false otherwise. | public boolean atEnd() { return tokenStream.LA(1).getType() == JavaTokenTypes.EOF; }
| Get the type of the parsed expression. If the type is unknown or an error occurred, | returns {}code null}. | public JavaEntity getExpressionType() { processHigherPrecedence(getPrecedence(JavaTokenTypes.EOF) - 1); if (valueStack.isEmpty()) { return null; } return valueStack.pop(); }
| Pop an item from the value stack. If there are no values to pop, supply an error entity. | protected JavaEntity popValueStack() { if (! valueStack.isEmpty()) { return valueStack.pop(); } return new ErrorEntity(); }
| Get the precedence level for a given operator type. | private int getPrecedence(int tokenType) { switch (tokenType) { case JavaTokenTypes.LCURLY: return -10; case PAREN_OPERATOR: return -2; case JavaTokenTypes.LITERAL_new: return -1; case JavaTokenTypes.ASSIGN: case JavaTokenTypes.BAND_ASSIGN: case JavaTokenTypes.BOR_ASSIGN: case JavaTokenTypes.PLUS_ASSIGN: case JavaTokenTypes.MINUS_ASSIGN: case JavaTokenTypes.STAR_ASSIGN: case JavaTokenTypes.DIV_ASSIGN: case JavaTokenTypes.SL_ASSIGN: case JavaTokenTypes.SR_ASSIGN: case JavaTokenTypes.BSR_ASSIGN: case JavaTokenTypes.MOD_ASSIGN: case JavaTokenTypes.BXOR_ASSIGN: return 0; case JavaTokenTypes.QUESTION: return 1; case JavaTokenTypes.LT: case JavaTokenTypes.LE: case JavaTokenTypes.GT: case JavaTokenTypes.GE: case JavaTokenTypes.LITERAL_instanceof: return 8; case JavaTokenTypes.EQUAL: case JavaTokenTypes.NOT_EQUAL: return 9; case JavaTokenTypes.SL: case JavaTokenTypes.SR: case JavaTokenTypes.BSR: return 10; case JavaTokenTypes.PLUS: case JavaTokenTypes.MINUS: return 11; case JavaTokenTypes.STAR: case JavaTokenTypes.DIV: return 12; case JavaTokenTypes.LNOT: case JavaTokenTypes.BNOT: case JavaTokenTypes.INC: case JavaTokenTypes.DEC: case UNARY_PLUS_OP: case UNARY_MINUS_OP: case CAST_OPERATOR: case BAD_CAST_OPERATOR: return 13; case JavaTokenTypes.DOT: case MEMBER_CALL_OP: case METHOD_CALL_OP: case CONSTRUCTOR_CALL_OP: return 20; default: } return -1; }
| Process all on-stack operators with a higher precedence than that given | private void processHigherPrecedence(int precedence) { while (! operatorStack.isEmpty()){ Operator top = operatorStack.peek(); if (getPrecedence(top.getType()) <= precedence) { break; } operatorStack.pop(); processOperator(top); } }
| Process an operator, take the operands from the value stack and leave the result on the | stack. | private void processOperator(Operator operator) { int tokenType = operator.getType(); JavaEntity arg1; JavaEntity arg2; switch (tokenType) { case JavaTokenTypes.PLUS: case JavaTokenTypes.MINUS: case JavaTokenTypes.STAR: case JavaTokenTypes.DIV: case JavaTokenTypes.MOD: case JavaTokenTypes.SL: case JavaTokenTypes.SR: case JavaTokenTypes.BSR: case JavaTokenTypes.LT: case JavaTokenTypes.LE: case JavaTokenTypes.GT: case JavaTokenTypes.GE: case JavaTokenTypes.EQUAL: case JavaTokenTypes.NOT_EQUAL: case JavaTokenTypes.ASSIGN: case JavaTokenTypes.BAND_ASSIGN: case JavaTokenTypes.BOR_ASSIGN: case JavaTokenTypes.PLUS_ASSIGN: case JavaTokenTypes.MINUS_ASSIGN: case JavaTokenTypes.STAR_ASSIGN: case JavaTokenTypes.DIV_ASSIGN: case JavaTokenTypes.SL_ASSIGN: case JavaTokenTypes.SR_ASSIGN: case JavaTokenTypes.BSR_ASSIGN: case JavaTokenTypes.MOD_ASSIGN: case JavaTokenTypes.BXOR_ASSIGN: case JavaTokenTypes.BAND: case JavaTokenTypes.BOR: case JavaTokenTypes.BXOR: case JavaTokenTypes.LOR: case JavaTokenTypes.LAND: arg2 = popValueStack(); arg1 = popValueStack(); doBinaryOp(arg1, arg2, operator); break; case JavaTokenTypes.LNOT: case JavaTokenTypes.BNOT: case JavaTokenTypes.INC: case JavaTokenTypes.DEC: case UNARY_MINUS_OP: case UNARY_PLUS_OP: arg1 = popValueStack(); checkArg(arg1, operator); break; case CAST_OPERATOR: doCast(); break; case BAD_CAST_OPERATOR: popValueStack(); valueStack.push(new ErrorEntity()); break; case JavaTokenTypes.QUESTION: processQuestionOperator(); break; } } @OnThread(Tag.FXPlatform) private strictfp void doCast() { ValueEntity varg1 = popValueStack().resolveAsValue(); TypeEntity castType = popValueStack().resolveAsType(); if (varg1 == null || castType == null) { valueStack.push(new ErrorEntity()); return; } if (ValueEntity.isConstant(varg1)) { JavaType jctype = castType.getType(); if (jctype.isIntegralType()) { long ival; if (varg1.hasConstantIntValue()) { ival = varg1.getConstantIntValue(); if (jctype.typeIs(JavaType.JT_BYTE)) { ival = (byte) ival; } else if (jctype.typeIs(JavaType.JT_CHAR)) { ival = (char) ival; } else if (jctype.typeIs(JavaType.JT_INT)) { ival = (int) ival; } else if (jctype.typeIs(JavaType.JT_SHORT)) { ival = (short) ival; } } else if (varg1.hasConstantFloatValue()) { if (jctype.typeIs(JavaType.JT_BYTE)) { ival = (byte) varg1.getConstantFloatValue(); } else if (jctype.typeIs(JavaType.JT_CHAR)) { ival = (char) varg1.getConstantFloatValue(); } else if (jctype.typeIs(JavaType.JT_INT)) { ival = (int) varg1.getConstantFloatValue(); } else if (jctype.typeIs(JavaType.JT_SHORT)) { ival = (short) varg1.getConstantFloatValue(); } else { ival = (long) varg1.getConstantFloatValue(); } } else { valueStack.push(new ErrorEntity()); return; } valueStack.push(new ConstantIntValue(null, jctype, ival)); return; } else if (jctype.typeIs(JavaType.JT_FLOAT) || jctype.typeIs(JavaType.JT_DOUBLE)) { double dval; if (varg1.hasConstantIntValue()) { dval = varg1.getConstantIntValue(); } else if (varg1.hasConstantFloatValue()) { dval = varg1.getConstantFloatValue(); } else { valueStack.push(new ErrorEntity()); return; } if (jctype.typeIs(JavaType.JT_FLOAT)) { dval = (float) dval; } valueStack.push(new ConstantFloatValue(jctype, dval)); return; } else if (jctype.toString().equals("java.lang.String")) { if (varg1.isConstantString()) { valueStack.push(varg1); } else { valueStack.push(new ErrorEntity()); } return; } } JavaType argType = varg1.getType(); JavaType jctype = castType.getType(); if (argType.isPrimitive() && ! jctype.isPrimitive()) { if (argType.typeIs(JavaType.JT_NULL)) { valueStack.push(new ValueEntity(jctype)); return; } String jctypeStr = jctype.toString(); if (argType.typeIs(JavaType.JT_BOOLEAN) && jctypeStr.equals("java.lang.Boolean") || argType.typeIs(JavaType.JT_CHAR) && jctypeStr.equals("java.lang.Character") || argType.typeIs(JavaType.JT_BYTE) && jctypeStr.equals("java.lang.Byte") || argType.typeIs(JavaType.JT_SHORT) && jctypeStr.equals("java.lang.Short") || argType.typeIs(JavaType.JT_INT) && jctypeStr.equals("java.lang.Integer") || argType.typeIs(JavaType.JT_LONG) && jctypeStr.equals("java.lang.Long") || argType.typeIs(JavaType.JT_FLOAT) && jctypeStr.equals("java.lang.Float") || argType.typeIs(JavaType.JT_DOUBLE) && jctypeStr.equals("java.lang.Double")) { valueStack.push(new ValueEntity(jctype)); } else { valueStack.push(new ErrorEntity()); } return; } JavaType argUnboxed = TextAnalyzer.unBox(argType); if (argUnboxed.isNumeric() && jctype.isNumeric()) { valueStack.push(new ValueEntity(jctype)); return; } if (jctype.isPrimitive()) { if (jctype.typeIs(JavaType.JT_BOOLEAN) && argUnboxed.typeIs(JavaType.JT_BOOLEAN)) { valueStack.push(new ValueEntity(jctype)); } else { valueStack.push(new ErrorEntity()); } return; } valueStack.push(new ValueEntity(jctype)); } @Override protected void gotArrayElementAccess() { JavaEntity index = popValueStack(); processHigherPrecedence(getPrecedence(JavaTokenTypes.DOT) - 1); JavaEntity array = popValueStack(); index = index.resolveAsValue(); array = array.resolveAsValue(); if (index == null || array == null) { valueStack.push(new ErrorEntity()); return; } JavaType componentType = array.getType().getArrayComponent(); if (componentType == null) { valueStack.push(new ErrorEntity()); } else { valueStack.push(new ValueEntity(componentType)); } } private void processNewOperator(Operator token) { if (! argumentStack.isEmpty()) { argumentStack.pop(); } else { popValueStack(); valueStack.push(new ErrorEntity()); } }
| Process the "member call operator". |*/ private void processMemberCall(Operator op) { // See JLS 15.12 // step 1 - determine type to search // Seeing as this is a member call, that's already mostly done. JavaEntity target = popValueStack(); | |JavaEntity vtarget = target.resolveAsValue(); | |GenTypeSolid targetType; | |if (vtarget != null) { | |GenTypeSolid stype = vtarget.getType().asSolid(); | |if (stype == null) { | |// call on a primitive | |valueStack.push(new ErrorEntity()); | |return; | |} | |targetType = stype; | |} | |else { | |// entity may be a type rather than a value | |target = target.resolveAsType(); | |if (target == null) { | |valueStack.push(new ErrorEntity()); | |return; | |} | |GenTypeSolid stype = target.getType().getCapture().asSolid(); | |if (stype == null) { | |// call on a primitive | |valueStack.push(new ErrorEntity()); | |return; | |} | |targetType = stype; | |} | |if (op.getToken().getType() == JavaTokenTypes.IDENT) { | |processMethodCall(targetType, op.getToken().getText(), typeArgStack.pop()); | |} | |else { | |valueStack.push(new ErrorEntity()); | |} | |} | |private void processMethodCall(Operator op) | |{ | |if (op.getToken().getType() == JavaTokenTypes.IDENT) { | |processMethodCall(accessType.getType().asClass(), op.getToken().getText(), | |Collections.<LocatableToken>emptyList()); | |} | |else { | |valueStack.push(new ErrorEntity()); | |} | |} | |private void processMethodCall(GenTypeSolid targetType, String methodName, List<LocatableToken> typeArgTokens) | |{ | |GenTypeClass accessClass = accessType.getType().asClass(); | |if (accessClass == null) { | |valueStack.push(new ErrorEntity()); | |return; | |} | |// Gather the argument types. | |List<JavaEntity> argList = argumentStack.pop(); | |JavaType [] argTypes = new JavaType[argList.size()]; | |for (int i = 0; i < argTypes.length; i++) { | |JavaEntity cent = argList.get(i).resolveAsValue(); | |if (cent == null) { | |valueStack.push(new ErrorEntity()); | |return; | |} | |argTypes[i] = cent.getType(); | |} | |// Determine type arguments to method invocation | |List<GenTypeParameter> typeArgs; | |if (! typeArgTokens.isEmpty()) { | |DepthRef depthRef = new DepthRef(); | |ListIterator<LocatableToken> i = typeArgTokens.listIterator(1); // skip '<' | |List<TypeArgumentEntity> typeArgEnts = readTypeArguments(i, depthRef); | |if (typeArgEnts == null) { | |valueStack.push(new ErrorEntity()); | |return; | |} | |typeArgs = new ArrayList<GenTypeParameter>(typeArgEnts.size()); | |for (TypeArgumentEntity typeArgEnt : typeArgEnts) { | |GenTypeParameter targType = typeArgEnt.getType(); | |if (targType == null || targType.isPrimitive() || targType.isWildcard()) { | |valueStack.push(new ErrorEntity()); | |return; | |} | |typeArgs.add(targType); | |} | |} | |else { | |typeArgs = Collections.emptyList(); | |} | |ArrayList<MethodCallDesc> suitable = TextAnalyzer.getSuitableMethods(methodName, | |targetType, argTypes, typeArgs, accessClass.getReflective()); | |if (suitable.size() == 0) { | |valueStack.push(new ErrorEntity()); | |return; | |} | |// JLS 3rd ed. 15.12.2.5. We already have a list of the maximally specific methods: | |// - if one or more methods have non override-equivalent signatures, the call is ambiguous | |// - otherwise, if there is exactly one non-abstract method, choose that one; | |// - otherwise, arbitrarily choose one from the set with the most specific return type. | |int nonAbstractCount = 0; | |MethodCallDesc nonAbstractMethod = null; | |MethodCallDesc mostSpecificMethod = null; | |Iterator<MethodCallDesc> i = suitable.iterator(); | |MethodCallDesc first = i.next(); | |if (! first.method.isAbstract()) { | |nonAbstractCount++; | |nonAbstractMethod = first; | |} | |mostSpecificMethod = first; | |if (suitable.size() > 1) { | |List<GenTypeDeclTpar> tpars = first.method.getTparTypes(); | |List<JavaType> paramTypes = first.method.getParamTypes(); | |while (i.hasNext()){ | |MethodCallDesc next = i.next(); | |if (!checkOverrideEquivalence(tpars, paramTypes, next.method.getTparTypes(), next.method.getParamTypes())) { | |// Non override equivalent signatures: ambiguous call | |valueStack.push(new ErrorEntity()); | |return; | |} | |if (! next.method.isAbstract()) { | |nonAbstractCount++; | |nonAbstractMethod = next; | |} | |if (mostSpecificMethod.retType.isAssignableFrom(next.retType)) { | |mostSpecificMethod = next; | |} | |} | |} | |MethodCallDesc chosenMethod = nonAbstractMethod; | |if (nonAbstractCount != 1) { | |chosenMethod = mostSpecificMethod; | |} | |// TODO check applicability of chosen method as per JLS 3rd ed. 15.12.3 | |valueStack.push(new ValueEntity(chosenMethod.retType)); | |} | |private static boolean checkOverrideEquivalence(List<GenTypeDeclTpar> firstTpars, List<JavaType> firstParamTypes, | |List<GenTypeDeclTpar> secondTpars, List<JavaType> secondParamTypes) | |{ | |if (firstTpars.size() != 0 && secondTpars.size() != 0) { | |// Type parameters must be matchable | |if (firstTpars.size() != secondTpars.size()) { | |return false; | |} | |// Create a map from second method tpar name to first method tpar | |Map<String,GenTypeParameter> tparMap = new HashMap<String,GenTypeParameter>(); | |Iterator<GenTypeDeclTpar> i = firstTpars.iterator(); | |Iterator<GenTypeDeclTpar> j = secondTpars.iterator(); | |while (i.hasNext()){ | |GenTypeDeclTpar firstTpar = i.next(); | |GenTypeDeclTpar secondTpar = j.next(); | |tparMap.put(secondTpar.getTparName(), firstTpar); | |if (!secondTpar.getUpperBound().mapTparsToTypes(tparMap).equals(firstTpar.getUpperBound())) { | |return false; | |} | |} | |// Check argument types | |Iterator<JavaType> k = firstParamTypes.iterator(); | |Iterator<JavaType> l = secondParamTypes.iterator(); | |while (k.hasNext()){ | |if (!l.next().mapTparsToTypes(tparMap).equals(k.next())) { | |return false; | |} | |} | |return true; | |} | |if (firstTpars.isEmpty() && secondTpars.isEmpty()) { | |// The parameter types might match exactly | |boolean doMatch = true; | |Iterator<JavaType> k = firstParamTypes.iterator(); | |Iterator<JavaType> l = secondParamTypes.iterator(); | |while (k.hasNext()){ | |if (!l.next().equals(k.next())) { | |doMatch = false; | |break; | |} | |} | |if (doMatch) { | |return true; | |} | |} | |if (firstTpars.isEmpty()) { | |// The first signature might be the erasure of the second | |boolean doMatch = true; | |Iterator<JavaType> k = firstParamTypes.iterator(); | |Iterator<JavaType> l = secondParamTypes.iterator(); | |while (k.hasNext()){ | |if (!l.next().getErasedType().equals(k.next())) { | |doMatch = false; | |break; | |} | |} | |if (doMatch) { | |return true; | |} | |} | |if (secondTpars.isEmpty()) { | |// The second signature might be the erasure of the first | |boolean doMatch = true; | |Iterator<JavaType> k = firstParamTypes.iterator(); | |Iterator<JavaType> l = secondParamTypes.iterator(); | |while (k.hasNext()){ | |if (!l.next().equals(k.next().getErasedType())) { | |doMatch = false; | |break; | |} | |} | |return doMatch; | |} | |return false; | |} | |/** | For a unary operator, check that the argument is valid, | then process the operator. | private void checkArg(JavaEntity arg1, Operator op) { JavaEntity rarg1 = arg1.resolveAsValue(); if (rarg1 == null) { valueStack.push(new ErrorEntity()); return; } doUnaryOp(rarg1, op); }
| Do a unary plus operation (JLS 3rd ed. 15.15.3) | private void doUnaryPlus(ValueEntity arg) { JavaType argType = arg.getType(); if (arg.hasConstantIntValue()) { long value = arg.getConstantIntValue(); JavaType rtype = TextAnalyzer.unaryNumericPromotion(argType); valueStack.push(new ConstantIntValue(null, rtype, value)); } else if (arg.hasConstantFloatValue()) { double value = arg.getConstantFloatValue(); valueStack.push(new ConstantFloatValue(argType, value)); } else { JavaType rtype = TextAnalyzer.unaryNumericPromotion(argType); if (rtype != null) { valueStack.push(new ValueEntity(rtype)); } else { valueStack.push(new ErrorEntity()); } } }
| Do a unary minus operation (JLS 3rd ed. 15.15.4) | private void doUnaryMinus(ValueEntity arg) { JavaType argType = arg.getType(); if (arg.hasConstantIntValue()) { long value = -arg.getConstantIntValue(); JavaType rtype = TextAnalyzer.unaryNumericPromotion(argType); valueStack.push(new ConstantIntValue(null, rtype, value)); } else if (arg.hasConstantFloatValue()) { double value = -arg.getConstantFloatValue(); valueStack.push(new ConstantFloatValue(argType, value)); } else { JavaType rtype = TextAnalyzer.unaryNumericPromotion(argType); if (rtype != null) { valueStack.push(new ValueEntity(rtype)); } else { valueStack.push(new ErrorEntity()); } } }
| Process a unary operator. | private void doUnaryOp(JavaEntity arg, Operator op) { ValueEntity varg = arg.resolveAsValue(); if (varg == null) { valueStack.push(new ErrorEntity()); return; } JavaType argType = varg.getType(); int ttype = op.getType(); switch (ttype) { case JavaTokenTypes.LNOT: if (varg.hasConstantBooleanValue()) { boolean rval = ! varg.getConstantBooleanValue(); valueStack.push(new ConstantBoolValue(rval)); } else if (argType.typeIs(JavaType.JT_BOOLEAN) || argType.toString().equals("java.lang.Boolean")) { valueStack.push(new ValueEntity(JavaPrimitiveType.getBoolean())); } else { valueStack.push(new ErrorEntity()); } break; case JavaTokenTypes.BNOT: if (varg.hasConstantIntValue()) { long rval = ~ varg.getConstantIntValue(); JavaType rtype = TextAnalyzer.unaryNumericPromotion(argType); valueStack.push(new ConstantIntValue(null, rtype, rval)); } else { JavaType argTypeUnboxed = TextAnalyzer.unBox(argType); if (argTypeUnboxed.isIntegralType()) { valueStack.push(new ValueEntity(TextAnalyzer.unaryNumericPromotion(argTypeUnboxed))); } else { valueStack.push(new ErrorEntity()); } } break; case JavaTokenTypes.INC: case JavaTokenTypes.DEC: JavaType argTypeUnboxed = TextAnalyzer.unBox(argType); if (argTypeUnboxed.isIntegralType()) { valueStack.push(new ValueEntity(argType)); } else { valueStack.push(new ErrorEntity()); } break; case UNARY_PLUS_OP: doUnaryPlus(varg); break; case UNARY_MINUS_OP: doUnaryMinus(varg); break; } }
| Cast an integer value to the given integer type, and return the resulting value. | The returned value will be within the range of values representable by the type. | private long limitResult(JavaType type, long value) { if (type.typeIs(JavaType.JT_BYTE)) { return (byte) value; } else if (type.typeIs(JavaType.JT_CHAR)) { return (char) value; } else if (type.typeIs(JavaType.JT_SHORT)) { return (short) value; } else if (type.typeIs(JavaType.JT_INT)) { return (int) value; } return value; }
| Promote an integer or float to a float, return the result | private float promoteToFloat(ValueEntity arg1) { if (arg1.hasConstantFloatValue()) { return (float) arg1.getConstantFloatValue(); } else { return arg1.getConstantIntValue(); } }
| Promote an integer, float or double to a double, return the result. | private double promoteToDouble(ValueEntity arg1) { if (arg1.hasConstantFloatValue()) { return (float) arg1.getConstantFloatValue(); } else { return arg1.getConstantIntValue(); } }
| Process the '+' operator | private void doOpPlus(Operator op, ValueEntity arg1, ValueEntity arg2) { JavaType a1type = arg1.getType(); JavaType a2type = arg2.getType(); GenTypeSolid a1solid = a1type.asSolid(); GenTypeSolid a2solid = a2type.asSolid(); GenTypeClass a1class = null; GenTypeClass a2class = null; if (a1solid != null) { GenTypeClass [] stypes = a1solid.getReferenceSupertypes(); if (stypes.length > 0) { a1class = a1solid.getReferenceSupertypes()[0]; } } if (a2solid != null) { GenTypeClass [] stypes = a2solid.getReferenceSupertypes(); if (stypes.length > 0) { a2class = a2solid.getReferenceSupertypes()[0]; } } if (a1class != null && a1class.toString().equals("java.lang.String")) { valueStack.push(new ValueEntity(a1class)); return; } if (a2class != null && a2class.toString().equals("java.lang.String")) { valueStack.push(new ValueEntity(a2class)); return; } doBnpOp(op, arg1, arg2); }
| Process an operator which performs binary numeric promotion and which allows | the result to be a constant expression. | private strictfp void doBnpOp(Operator op, ValueEntity arg1, ValueEntity arg2) { JavaType a1type = arg1.getType(); JavaType a2type = arg2.getType(); JavaType resultType = TextAnalyzer.binaryNumericPromotion(a1type, a2type); if (resultType == null) { valueStack.push(new ErrorEntity()); return; } if ((arg1.hasConstantIntValue() || arg1.hasConstantFloatValue()) && (arg2.hasConstantIntValue() || arg2.hasConstantFloatValue())) { if (resultType.isIntegralType()) { long a1 = arg1.getConstantIntValue(); long a2 = arg2.getConstantIntValue(); long rval; switch (op.type) { case JavaTokenTypes.PLUS: rval = a1 + a2; break; case JavaTokenTypes.MINUS: rval = a1 - a2; break; case JavaTokenTypes.STAR: rval = a1 * a2; break; case JavaTokenTypes.DIV: if (a2 == 0) { valueStack.push(new ValueEntity(resultType)); return; } rval = a1 / a2; break; case JavaTokenTypes.MOD: if (a2 == 0) { valueStack.push(new ValueEntity(resultType)); return; } rval = a1 % a2; break; case JavaTokenTypes.BAND: rval = a1 & a2; break; case JavaTokenTypes.BOR: rval = a1 | a2; break; case JavaTokenTypes.BXOR: rval = a1 ^ a2; break; default: rval = 0; break; } rval = limitResult(resultType, rval); valueStack.push(new ConstantIntValue(null, resultType, rval)); } else if (resultType.typeIs(JavaType.JT_FLOAT)) { float a1, a2; a1 = promoteToFloat(arg1); a2 = promoteToFloat(arg2); float rval; switch (op.type) { case JavaTokenTypes.PLUS: rval = a1 + a2; break; case JavaTokenTypes.MINUS: rval = a1 - a2; break; case JavaTokenTypes.STAR: rval = a1 * a2; break; case JavaTokenTypes.DIV: rval = a1 / a2; break; case JavaTokenTypes.MOD: rval = a1 % a2; break; case JavaTokenTypes.BAND: case JavaTokenTypes.BOR: case JavaTokenTypes.BXOR: default: valueStack.push(new ErrorEntity()); return; } valueStack.push(new ConstantFloatValue(resultType, rval)); } else { double a1 = promoteToDouble(arg1); double a2 = promoteToDouble(arg2); double rval; switch (op.type) { case JavaTokenTypes.PLUS: rval = a1 + a2; break; case JavaTokenTypes.MINUS: rval = a1 - a2; break; case JavaTokenTypes.STAR: rval = a1 * a2; break; case JavaTokenTypes.DIV: rval = a1 / a2; break; case JavaTokenTypes.MOD: rval = a1 % a2; break; case JavaTokenTypes.BAND: case JavaTokenTypes.BOR: case JavaTokenTypes.BXOR: default: valueStack.push(new ErrorEntity()); return; } valueStack.push(new ConstantFloatValue(resultType, rval)); } return; } valueStack.push(new ValueEntity(null, resultType)); }
| Process equality operators '==' and '!=' | private void doEqualityOp(Operator op, ValueEntity arg1, ValueEntity arg2) { JavaType a1type = arg1.getType(); JavaType a2type = arg2.getType(); if (ValueEntity.isConstant(arg1) && ValueEntity.isConstant(arg2)) { if (a1type.isNumeric()) { if (! a2type.isNumeric()) { valueStack.push(new ErrorEntity()); return; } JavaType promotedType = TextAnalyzer.binaryNumericPromotion(a1type, a2type); if (promotedType.isIntegralType()) { long a1 = arg1.getConstantIntValue(); long a2 = arg2.getConstantIntValue(); boolean rval= (a1 == a2) ^ (op.type != JavaTokenTypes.EQUAL); valueStack.push(new ConstantBoolValue(rval)); } else if (promotedType.typeIs(JavaType.JT_FLOAT)) { float a1 = promoteToFloat(arg1); float a2 = promoteToFloat(arg2); boolean rval= (a1 == a2) ^ (op.type != JavaTokenTypes.EQUAL); valueStack.push(new ConstantBoolValue(rval)); } else { double a1 = promoteToDouble(arg1); double a2 = promoteToDouble(arg2); boolean rval= (a1 == a2) ^ (op.type != JavaTokenTypes.EQUAL); valueStack.push(new ConstantBoolValue(rval)); } return; } if (arg1.hasConstantBooleanValue() && arg2.hasConstantBooleanValue()) { boolean a1 = arg1.getConstantBooleanValue(); boolean a2 = arg2.getConstantBooleanValue(); boolean rval = op.type == JavaTokenTypes.EQUAL ? a1 == a2 : a1 != a2; valueStack.push(new ConstantBoolValue(rval)); return; } if (arg1.isConstantString() && arg2.isConstantString()) { String a1 = arg1.getConstantString(); String a2 = arg2.getConstantString(); boolean rval = (op.type != JavaTokenTypes.EQUAL) ^ a1.equals(a2); valueStack.push(new ConstantBoolValue(rval)); return; } valueStack.push(new ErrorEntity()); } if (a1type.isNumeric() || a2type.isNumeric()) { JavaType promotedType = TextAnalyzer.binaryNumericPromotion(a1type, a2type); if (promotedType == null) { valueStack.push(new ErrorEntity()); } else { valueStack.push(new ValueEntity("", JavaPrimitiveType.getBoolean())); } } else if (a1type.isNull() && a2type.isNull() || a1type.isNull() && a2type.asSolid() != null || a1type.asSolid() != null && a2type.isNull()) { |
valueStack.push(new ValueEntity(JavaPrimitiveType.getBoolean())); } else if (a1type.asSolid() != null && a2type.asSolid() != null) { valueStack.push(new ValueEntity(JavaPrimitiveType.getBoolean())); } else { valueStack.push(new ErrorEntity()); } }
| Process a relationship operator - '<', '>', '<=', '>=' | private void doRelationshipOp(Operator op, ValueEntity arg1, ValueEntity arg2) { JavaType a1type = arg1.getType(); JavaType a2type = arg2.getType(); JavaType promotedType = TextAnalyzer.binaryNumericPromotion(a1type, a2type); if (promotedType == null) { valueStack.push(new ErrorEntity()); } else { if (ValueEntity.isConstant(arg1) && ValueEntity.isConstant(arg2)) { if (promotedType.isIntegralType()) { long a1 = arg1.getConstantIntValue(); long a2 = arg2.getConstantIntValue(); boolean rval; switch (op.type) { case JavaTokenTypes.LT: rval = a1 < a2; break; case JavaTokenTypes.LE: rval = a1 <= a2; break; case JavaTokenTypes.GT: rval = a1 > a2; break; default: rval = a1 >= a2; } valueStack.push(new ConstantBoolValue(rval)); } else if (promotedType.typeIs(JavaType.JT_FLOAT)) { float a1 = promoteToFloat(arg1); float a2 = promoteToFloat(arg2); boolean rval; switch (op.type) { case JavaTokenTypes.LT: rval = a1 < a2; break; case JavaTokenTypes.LE: rval = a1 <= a2; break; case JavaTokenTypes.GT: rval = a1 > a2; break; default: rval = a1 >= a2; } valueStack.push(new ConstantBoolValue(rval)); } else { double a1 = promoteToDouble(arg1); double a2 = promoteToDouble(arg2); boolean rval; switch (op.type) { case JavaTokenTypes.LT: rval = a1 < a2; break; case JavaTokenTypes.LE: rval = a1 <= a2; break; case JavaTokenTypes.GT: rval = a1 > a2; break; default: rval = a1 >= a2; } valueStack.push(new ConstantBoolValue(rval)); } return; } valueStack.push(new ValueEntity("", JavaPrimitiveType.getBoolean())); } }
| Do a bitwise operation - '&', '|' or '^' | private void doBitwiseOp(Operator op, ValueEntity arg1, ValueEntity arg2) { JavaType a1type = arg1.getType(); if (a1type.typeIs(JavaType.JT_BOOLEAN) || a1type.toString().equals("java.lang.Boolean")) { JavaType a2type = arg2.getType(); if (a2type.typeIs(JavaType.JT_BOOLEAN) || a2type.toString().equals("java.lang.Boolean")) { if (arg1.hasConstantBooleanValue() && arg2.hasConstantBooleanValue()) { boolean a1 = arg1.getConstantBooleanValue(); boolean a2 = arg2.getConstantBooleanValue(); boolean rval; switch (op.type) { case JavaTokenTypes.BAND: rval = a1 & a2; break; case JavaTokenTypes.BOR: rval = a1 | a2; break; default: rval = a1 ^ a2; } valueStack.push(new ConstantBoolValue(rval)); } else { valueStack.push(new ValueEntity(JavaPrimitiveType.getBoolean())); } return; } } doBnpOp(op, arg1, arg2); }
| Do a logical operation - '&&' or '||' | @param op | @param arg1 | @param arg2 | private void doLogicalOp(Operator op, ValueEntity arg1, ValueEntity arg2) { JavaType a1type = arg1.getType(); if (a1type.typeIs(JavaType.JT_BOOLEAN) || a1type.toString().equals("java.lang.Boolean")) { JavaType a2type = arg2.getType(); if (a2type.typeIs(JavaType.JT_BOOLEAN) || a2type.toString().equals("java.lang.Boolean")) { if (arg1.hasConstantBooleanValue() && arg2.hasConstantBooleanValue()) { boolean a1 = arg1.getConstantBooleanValue(); boolean a2 = arg2.getConstantBooleanValue(); boolean rval; switch (op.type) { case JavaTokenTypes.LAND: rval = a1 && a2; default: rval = a1 || a2; } valueStack.push(new ConstantBoolValue(rval)); } else { valueStack.push(new ValueEntity(JavaPrimitiveType.getBoolean())); } return; } } valueStack.push(new ErrorEntity()); }
| Process a binary operator. Arguments have been resolved as values. | The result is pushed back onto the value stack. | private void doBinaryOp(JavaEntity uarg1, JavaEntity uarg2, Operator op) { ValueEntity arg1 = uarg1.resolveAsValue(); ValueEntity arg2 = uarg2.resolveAsValue(); if (arg1 == null || arg2 == null) { valueStack.push(new ErrorEntity()); return; } JavaType a1type = arg1.getType().getCapture(); int ttype = op.getType(); switch (ttype) { case JavaTokenTypes.PLUS: doOpPlus(op, arg1, arg2); return; case JavaTokenTypes.MINUS: case JavaTokenTypes.STAR: case JavaTokenTypes.DIV: case JavaTokenTypes.MOD: doBnpOp(op, arg1, arg2); break; case JavaTokenTypes.BAND: case JavaTokenTypes.BOR: case JavaTokenTypes.BXOR: doBitwiseOp(op, arg1, arg2); break; case JavaTokenTypes.LOR: case JavaTokenTypes.LAND: doLogicalOp(op, arg1, arg2); break; case JavaTokenTypes.SL: case JavaTokenTypes.SR: case JavaTokenTypes.BSR: JavaType a1typeP = TextAnalyzer.unaryNumericPromotion(a1type); JavaType a2typeP = TextAnalyzer.unaryNumericPromotion(a1type); if (a1typeP == null || a2typeP == null || !a1typeP.isIntegralType() || ! a2typeP.isIntegralType()) { valueStack.push(new ErrorEntity()); } else { if (arg1.hasConstantIntValue() && arg2.hasConstantIntValue()) { long a1 = arg1.getConstantIntValue(); long a2 = arg2.getConstantIntValue(); long rval; if (ttype == JavaTokenTypes.SL) { rval = a1 << a2; } else if (ttype == JavaTokenTypes.SR) { rval = a1 >> a2; } else { rval = a1 >>> a2; } rval = limitResult(a1typeP, rval); valueStack.push(new ConstantIntValue(null, a1typeP, rval)); } else { valueStack.push(new ValueEntity("", a1typeP)); } } break; case JavaTokenTypes.LT: case JavaTokenTypes.LE: case JavaTokenTypes.GT: case JavaTokenTypes.GE: doRelationshipOp(op, arg1, arg2); break; case JavaTokenTypes.EQUAL: case JavaTokenTypes.NOT_EQUAL: doEqualityOp(op, arg1, arg2); break; case JavaTokenTypes.ASSIGN: case JavaTokenTypes.BAND_ASSIGN: case JavaTokenTypes.BOR_ASSIGN: case JavaTokenTypes.PLUS_ASSIGN: case JavaTokenTypes.MINUS_ASSIGN: case JavaTokenTypes.STAR_ASSIGN: case JavaTokenTypes.DIV_ASSIGN: case JavaTokenTypes.SL_ASSIGN: case JavaTokenTypes.SR_ASSIGN: case JavaTokenTypes.BSR_ASSIGN: case JavaTokenTypes.MOD_ASSIGN: case JavaTokenTypes.BXOR_ASSIGN: valueStack.push(arg1); break; case JavaTokenTypes.DOT: valueStack.push(new ErrorEntity()); default: } } private void processQuestionOperator() { JavaEntity rhs = popValueStack(); JavaEntity lhs = popValueStack(); JavaEntity condition = popValueStack(); ValueEntity conditionv = condition.resolveAsValue(); if (conditionv == null) { valueStack.push(new ErrorEntity()); return; } JavaType ctype = conditionv.getType(); if (!ctype.typeIs(JavaType.JT_BOOLEAN) && !ctype.toString().equals("java.lang.Boolean")) { valueStack.push(new ErrorEntity()); return; } ValueEntity rhsv = rhs.resolveAsValue(); ValueEntity lhsv = lhs.resolveAsValue(); if (rhsv == null || lhsv == null) { valueStack.push(new ErrorEntity()); return; } ValueEntity rent = TextAnalyzer.questionOperator15(conditionv, lhsv, rhsv); valueStack.push(rent); } @Override protected void beginExpression(LocatableToken token) { operatorStack.push(new Operator(PAREN_OPERATOR, token)); } @Override protected void endExpression(LocatableToken token, boolean isEmpty) { if (isEmpty) { valueStack.push(new ErrorEntity()); } processHigherPrecedence(getPrecedence(PAREN_OPERATOR)); operatorStack.pop(); if (!operatorStack.isEmpty()) { if (operatorStack.peek().type == JavaTokenTypes.LCURLY) { popValueStack(); } } } private void gotStringLiteral(LocatableToken token) { String ctext = token.getText(); StringBuffer sb = new StringBuffer(ctext.length()); for (int pos = 1; pos < ctext.length(); pos++) { char c = ctext.charAt(pos); if (c == '\"') { break; } if (c == '\\') { c = ctext.charAt(2); if (c == 'b') { c = '\b'; } else if (c == 't') { c = '\t'; } else if (c == 'n') { c = '\n'; } else if (c == 'f') { c = '\f'; } else if (c == 'r') { c = '\r'; } else if (c == '\'') { c = '\''; } else if (c == '\"') { c = '\"'; } else if (c == '\\') { c = '\\'; } else if (c >= '0' && c <= '7') { int val = c - '0'; while (++pos < ctext.length()){ char d = ctext.charAt(pos); if (d == '\'') break; if (d < '0' || d > '7') { valueStack.push(new ErrorEntity()); return; } val = val * 8 + (d - '0'); if (val > 0377) { valueStack.push(new ErrorEntity()); return; } } c = (char) val; } else { valueStack.push(new ErrorEntity()); return; } } sb.append(c); } JavaType stringType = new GenTypeClass(new JavaReflective(String.class)); ValueEntity ent = new ConstantStringEntity(stringType, sb.toString()); valueStack.push(ent); } @Override protected void gotLiteral(LocatableToken token) { if (token.getType() == JavaTokenTypes.CHAR_LITERAL) { String ctext = token.getText(); char c = ctext.charAt(1); if (c == '\\') { c = ctext.charAt(2); if (c == 'b') { c = '\b'; } else if (c == 't') { c = '\t'; } else if (c == 'n') { c = '\n'; } else if (c == 'f') { c = '\f'; } else if (c == 'r') { c = '\r'; } else if (c == '\'') { c = '\''; } else if (c == '\"') { c = '\"'; } else if (c == '\\') { c = '\\'; } else if (c >= '0' && c <= '7') { int val = c - '0'; int pos = 3; while (pos < ctext.length()){ char d = ctext.charAt(pos++); if (d == '\'') break; if (d < '0' || d > '7') { valueStack.push(new ErrorEntity()); return; } val = val * 8 + (d - '0'); if (val > 0377) { valueStack.push(new ErrorEntity()); return; } } c = (char) val; } else { valueStack.push(new ErrorEntity()); return; } } valueStack.push(new ConstantIntValue(null, JavaPrimitiveType.getChar(), c)); } else if (token.getType() == JavaTokenTypes.NUM_INT) { try { valueStack.push(new ConstantIntValue(null, JavaPrimitiveType.getInt(), Integer.decode(token.getText()))); } catch (NumberFormatException nfe) { valueStack.push(new ErrorEntity()); } } else if (token.getType() == JavaTokenTypes.NUM_LONG) { try { String text = token.getText(); text = text.substring(0, text.length() - 1); valueStack.push(new ConstantIntValue(null, JavaPrimitiveType.getLong(), Long.decode(text))); } catch (NumberFormatException nfe) { valueStack.push(new ErrorEntity()); } } else if (token.getType() == JavaTokenTypes.NUM_FLOAT) { try { valueStack.push(new ConstantFloatValue(JavaPrimitiveType.getFloat(), Float.parseFloat(token.getText()))); } catch (NumberFormatException nfe) { valueStack.push(new ErrorEntity()); } } else if (token.getType() == JavaTokenTypes.NUM_DOUBLE) { try { valueStack.push(new ConstantFloatValue(JavaPrimitiveType.getDouble(), Double.parseDouble(token.getText()))); } catch (NumberFormatException nfe) { valueStack.push(new ErrorEntity()); } } else if (token.getType() == JavaTokenTypes.LITERAL_null) { valueStack.push(new NullEntity()); } else if (token.getType() == JavaTokenTypes.STRING_LITERAL) { gotStringLiteral(token); } else if (token.getType() == JavaTokenTypes.LITERAL_true || token.getType() == JavaTokenTypes.LITERAL_false) { |
valueStack.push(new ConstantBoolValue(token.getType() == JavaTokenTypes.LITERAL_true)); } else if (token.getType() == JavaTokenTypes.LITERAL_this) { if (staticAccess) { valueStack.push(new ErrorEntity()); } else { JavaType type = accessType.getType(); if (type != null) { valueStack.push(new ValueEntity(type)); } else { valueStack.push(new ErrorEntity()); } } } else if (token.getType() == JavaTokenTypes.LITERAL_super) { for (JavaType type : accessType.getType().asClass().getReflective().getSuperTypes()) { if (type != null) { valueStack.push(new ValueEntity(type)); } else { valueStack.push(new ErrorEntity()); } } } else { valueStack.push(new ErrorEntity()); } } @Override protected void gotIdentifier(LocatableToken token) { String ident = token.getText(); Reflective accessSource = getAccessSource(); valueStack.push(UnresolvedEntity.getEntity(resolver, ident, accessSource)); arrayCount = 0; }
| Get the access type as a reflective. | private Reflective getAccessSource() { if (accessType != null) { GenTypeClass accessClass = accessType.getType().asClass(); if (accessClass != null) { return accessClass.getReflective(); } } return null; } @Override protected void gotMemberAccess(LocatableToken token) { String ident = token.getText(); JavaEntity top = valueStack.pop(); if (token.getText().equals("length")) { JavaEntity topVal = top.resolveAsValue(); if (topVal != null) { if (topVal.getType().getArrayComponent() != null) { valueStack.push(new ValueEntity(JavaPrimitiveType.getInt())); return; } } } JavaEntity newTop = top.getSubentity(ident, getAccessSource()); if (newTop != null) { valueStack.push(newTop); } else { valueStack.push(new ErrorEntity()); } } @Override protected void gotMemberCall(LocatableToken token, List<LocatableToken> typeArgs) { operatorStack.push(new Operator(MEMBER_CALL_OP, token)); typeArgStack.push(typeArgs); } @Override protected void gotMethodCall(LocatableToken token) { operatorStack.push(new Operator(METHOD_CALL_OP, token)); } @Override protected void gotConstructorCall(LocatableToken token) { operatorStack.push(new Operator(METHOD_CALL_OP, token)); } @Override protected void gotUnaryOperator(LocatableToken token) { int ttype = token.getType(); if (ttype == JavaTokenTypes.PLUS) { ttype = UNARY_PLUS_OP; } else if (ttype == JavaTokenTypes.MINUS) { ttype = UNARY_MINUS_OP; } processHigherPrecedence(getPrecedence(ttype)); operatorStack.push(new Operator(ttype, token)); } @Override protected void gotBinaryOperator(LocatableToken token) { processHigherPrecedence(getPrecedence(token.getType()) - 1); operatorStack.push(new Operator(token.getType(), token)); } @Override protected void gotInstanceOfOperator(LocatableToken token) { processHigherPrecedence(getPrecedence(token.getType()) - 1); operatorStack.push(new Operator(token.getType(), token)); state = STATE_INSTANCEOF; } @Override protected void gotQuestionOperator(LocatableToken token) { processHigherPrecedence(getPrecedence(token.getType()) - 1); operatorStack.push(new Operator(token.getType(), token)); } @Override protected void gotTypeSpec(List<LocatableToken> tokens) { if (state == STATE_NEW) { JavaEntity entity = resolveTypeSpec(tokens); state = STATE_NEW_ARGS; if (entity != null) { valueStack.push(new ValueEntity(entity.getType())); } else { valueStack.push(new ErrorEntity()); } } else if (state == STATE_INSTANCEOF) { TypeEntity entity = resolveTypeSpec(tokens); if (entity != null) { popValueStack(); valueStack.push(new ValueEntity(JavaPrimitiveType.getBoolean())); } else { popValueStack(); valueStack.push(new ErrorEntity()); } } } @Override protected void gotTypeCast(List<LocatableToken> tokens) { JavaEntity entity = resolveTypeSpec(tokens); if (entity != null) { valueStack.push(entity); operatorStack.push(new Operator(CAST_OPERATOR, null)); } else { operatorStack.push(new Operator(BAD_CAST_OPERATOR, null)); } } @Override protected void gotArrayDeclarator() { arrayCount++; } @Override protected void gotNewArrayDeclarator(boolean withDimension) { if (state != STATE_NONE) { state = STATE_NONE; operatorStack.pop(); } if (withDimension) { valueStack.pop(); } JavaEntity top = valueStack.pop(); JavaEntity ttop = top.resolveAsValue(); if (ttop != null) { valueStack.push(new ValueEntity(ttop.getType().getArray())); } else { valueStack.push(new ErrorEntity()); } } @Override protected void gotPrimitiveTypeLiteral(LocatableToken token) { List<LocatableToken> ltokens = new ArrayList<LocatableToken>(1); ltokens.add(token); TypeEntity tent = resolveTypeSpec(ltokens); valueStack.push(tent); arrayCount = 0; } @Override @OnThread(Tag.FXPlatform) protected void gotClassLiteral(LocatableToken token) { JavaEntity ent = popValueStack(); TypeEntity tent = ent.resolveAsType(); if (tent != null) { JavaType ttype = tent.getType(); if (arrayCount > 0) { while (arrayCount-- > 0){ ttype = ttype.getArray(); } tent = new TypeEntity(ttype); } if (! ttype.isPrimitive()) { TypeEntity jlcEnt = resolver.resolveQualifiedClass("java.lang.Class"); if (jlcEnt != null) { List<TypeArgumentEntity> targs = new ArrayList<TypeArgumentEntity>(1); targs.add(new SolidTargEntity(tent)); jlcEnt = jlcEnt.setTypeArgs(targs); if (jlcEnt != null) { valueStack.push(new ValueEntity(jlcEnt.getType())); return; } } } else { String repClass = null; if (ttype.typeIs(JavaType.JT_BOOLEAN)) { repClass = "java.lang.Boolean"; } else if (ttype.typeIs(JavaType.JT_BYTE)) { repClass = "java.lang.Byte"; } else if (ttype.typeIs(JavaType.JT_CHAR)) { repClass = "java.lang.Char"; } else if (ttype.typeIs(JavaType.JT_DOUBLE)) { repClass = "java.lang.Double"; } else if (ttype.typeIs(JavaType.JT_FLOAT)) { repClass = "java.lang.Float"; } else if (ttype.typeIs(JavaType.JT_INT)) { repClass = "java.lang.Integer"; } else if (ttype.typeIs(JavaType.JT_LONG)) { repClass = "java.lang.Long"; } else if (ttype.typeIs(JavaType.JT_VOID)) { repClass = "java.lang.Void"; } if (repClass != null) { TypeEntity jlcEnt = resolver.resolveQualifiedClass("java.lang.Class"); TypeEntity repEnt = resolver.resolveQualifiedClass(repClass); if (jlcEnt != null && repEnt != null) { List<TypeArgumentEntity> targs = new ArrayList<TypeArgumentEntity>(1); targs.add(new SolidTargEntity(repEnt)); jlcEnt = jlcEnt.setTypeArgs(targs); if (jlcEnt != null) { valueStack.push(new ValueEntity(jlcEnt.getType())); return; } } } } } valueStack.push(new ErrorEntity()); } private class DepthRef { int depth = 0; }
| Resolve a type specification. Returns null if the type couldn't be resolved. | private TypeEntity resolveTypeSpec(List<LocatableToken> tokens) { DepthRef dr = new DepthRef(); return resolveTypeSpec(tokens.listIterator(), dr); }
| Resolve a type specification. Returns null if the type couldn't be resolved. | @OnThread(Tag.FXPlatform) private TypeEntity resolveTypeSpec(ListIterator<LocatableToken> i, DepthRef depthRef) { LocatableToken token = i.next(); if (isPrimitiveType(token)) { if (token.getType() == JavaTokenTypes.LITERAL_void) { return new TypeEntity(JavaPrimitiveType.getVoid()); } JavaType type = null; switch (token.getType()) { case JavaTokenTypes.LITERAL_int: type = JavaPrimitiveType.getInt(); break; case JavaTokenTypes.LITERAL_short: type = JavaPrimitiveType.getShort(); break; case JavaTokenTypes.LITERAL_char: type = JavaPrimitiveType.getChar(); break; case JavaTokenTypes.LITERAL_byte: type = JavaPrimitiveType.getByte(); break; case JavaTokenTypes.LITERAL_boolean: type = JavaPrimitiveType.getBoolean(); break; case JavaTokenTypes.LITERAL_long: type = JavaPrimitiveType.getLong(); break; case JavaTokenTypes.LITERAL_double: type = JavaPrimitiveType.getDouble(); break; case JavaTokenTypes.LITERAL_float: type = JavaPrimitiveType.getFloat(); } while (i.hasNext()){ token = i.next(); if (token.getType() == JavaTokenTypes.LBRACK) { type = type.getArray(); i.next(); } else { return null; } } return new TypeEntity(type); } String text = token.getText(); PackageOrClass poc = resolver.resolvePackageOrClass(text, getAccessSource()); while (poc != null && i.hasNext()){ token = i.next(); if (token.getType() == JavaTokenTypes.LT) { TypeEntity classEnt = poc.resolveAsType(); if (classEnt != null) { classEnt = processTypeArgs(classEnt, i, depthRef); } poc = classEnt; if (poc == null) { return null; } if (! i.hasNext()) { return classEnt; } token = i.next(); } if (token.getType() != JavaTokenTypes.DOT) { poc = poc.resolveAsType(); if (poc == null) { return null; } while (token.getType() == JavaTokenTypes.LBRACK){ poc = new TypeEntity(poc.getType().getArray()); if (i.hasNext()) { token = i.next(); } if (! i.hasNext()) { return poc.resolveAsType(); } token = i.next(); } i.previous(); return poc.resolveAsType(); } token = i.next(); if (token.getType() != JavaTokenTypes.IDENT) { break; } poc = poc.getPackageOrClassMember(token.getText()); } if (poc != null) { return poc.resolveAsType(); } else { return null; } }
| Process tokens as type arguments | @param base The base type, i.e. the type to which the arguments are applied | @param i A ListIterator to iterate through the tokens | @param depthRef The argument depth | @return A ClassEntity representing the type with type arguments applied (or null) | private TypeEntity processTypeArgs(TypeEntity base, ListIterator<LocatableToken> i, DepthRef depthRef) { List<TypeArgumentEntity> taList = readTypeArguments(i, depthRef); if (taList == null) { return null; } return base.setTypeArgs(taList); }
| Read a list of type arguments (the opening angle-bracket has already been read) from a list of tokens. | Returns null if there is an error. | private List readTypeArguments(ListIterator<LocatableToken> i, DepthRef depthRef) { int startDepth = depthRef.depth; List<TypeArgumentEntity> taList = new LinkedList<TypeArgumentEntity>(); depthRef.depth++; mainLoop: while (i.hasNext() && depthRef.depth > startDepth){ LocatableToken token = i.next(); if (token.getType() == JavaTokenTypes.QUESTION) { if (! i.hasNext()) { return null; } token = i.next(); if (token.getType() == JavaTokenTypes.LITERAL_super) { TypeEntity taEnt = resolveTypeSpec(i, depthRef); if (taEnt == null) { return null; } taList.add(new WildcardSuperEntity(taEnt)); } else if (token.getType() == JavaTokenTypes.LITERAL_extends) { TypeEntity taEnt = resolveTypeSpec(i, depthRef); if (taEnt == null) { return null; } taList.add(new WildcardExtendsEntity(taEnt)); } else { taList.add(new UnboundedWildcardEntity(resolver)); i.previous(); } } else { i.previous(); TypeEntity taEnt = resolveTypeSpec(i, depthRef); if (taEnt == null) { return null; } JavaType taType = taEnt.getType(); if (taType.isPrimitive()) { return null; } taList.add(new SolidTargEntity(new TypeEntity(taType))); } if (! i.hasNext()) { return null; } token = i.next(); int ttype = token.getType(); while (ttype == JavaTokenTypes.GT || ttype == JavaTokenTypes.SR || ttype == JavaTokenTypes.BSR){ switch (ttype) { case JavaTokenTypes.BSR: depthRef.depth--; case JavaTokenTypes.SR: depthRef.depth--; default: depthRef.depth--; } if (! i.hasNext()) { break mainLoop; } token = i.next(); ttype = token.getType(); } if (ttype != JavaTokenTypes.COMMA) { i.previous(); break; } } return taList; } @Override protected void beginArgumentList(LocatableToken token) { state = STATE_NONE; argumentStack.push(new ArrayList<JavaEntity>()); } @Override protected void endArgument() { if (! valueStack.isEmpty()) { argumentStack.peek().add(valueStack.pop()); } } @Override protected void endArgumentList(LocatableToken token) { if (! operatorStack.isEmpty()) { Operator top = operatorStack.pop(); if (top.getType() == JavaTokenTypes.LITERAL_new) { processNewOperator(top); } else if (top.getType() == MEMBER_CALL_OP) { processMemberCall(top); } else if (top.getType() == METHOD_CALL_OP) { processMethodCall(top); } else { argumentStack.pop(); } } } @Override protected void gotExprNew(LocatableToken token) { state = STATE_NEW; operatorStack.push(new Operator(token.getType(), token)); } @Override protected void endExprNew(LocatableToken token, boolean included) { if (state != STATE_NONE) { if (state == STATE_NEW_ARGS) { popValueStack(); } operatorStack.pop(); valueStack.push(new ErrorEntity()); state = STATE_NONE; } } @Override protected void beginAnonClassBody(LocatableToken token, boolean isEnumMember) { operatorStack.push(new Operator(token.getType(), token)); } @Override protected void beginArrayInitList(LocatableToken token) { operatorStack.push(new Operator(token.getType(), token)); } }
top, use, map, class TextParser

top, use, map, class TextParser . Operator

.   TextParser
.   TextParser
.   TextParser
.   atEnd
.   getExpressionType
.   popValueStack
.   getPrecedence
.   processHigherPrecedence
.   processOperator
.   doCast
.   gotArrayElementAccess
.   processNewOperator
.   checkArg
.   doUnaryPlus
.   doUnaryMinus
.   doUnaryOp
.   limitResult
.   promoteToFloat
.   promoteToDouble
.   doOpPlus
.   doBnpOp
.   doEqualityOp
.   doRelationshipOp
.   doBitwiseOp
.   doLogicalOp
.   doBinaryOp
.   processQuestionOperator
.   beginExpression
.   endExpression
.   gotStringLiteral
.   gotLiteral
.   gotIdentifier
.   getAccessSource
.   gotMemberAccess
.   gotMemberCall
.   gotMethodCall
.   gotConstructorCall
.   gotUnaryOperator
.   gotBinaryOperator
.   gotInstanceOfOperator
.   gotQuestionOperator
.   gotTypeSpec
.   gotTypeCast
.   gotArrayDeclarator
.   gotNewArrayDeclarator
.   gotPrimitiveTypeLiteral
.   gotClassLiteral

top, use, map, class DepthRef

.   resolveTypeSpec
.   resolveTypeSpec
.   processTypeArgs
.   readTypeArguments
.   beginArgumentList
.   endArgument
.   endArgumentList
.   gotExprNew
.   endExprNew
.   beginAnonClassBody
.   beginArrayInitList




2835 neLoCode + 277 LoComm