/*
 * Javassist, a Java-bytecode translator toolkit.
 * Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License.  Alternatively, the contents of this file may be used under
 * the terms of the GNU Lesser General Public License Version 2.1 or later,
 * or the Apache License Version 2.0.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 */

package javassist.compiler;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.FieldInfo;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
import javassist.compiler.ast.ASTList;
import javassist.compiler.ast.ASTree;
import javassist.compiler.ast.ArrayInit;
import javassist.compiler.ast.AssignExpr;
import javassist.compiler.ast.BinExpr;
import javassist.compiler.ast.CallExpr;
import javassist.compiler.ast.CastExpr;
import javassist.compiler.ast.CondExpr;
import javassist.compiler.ast.Declarator;
import javassist.compiler.ast.DoubleConst;
import javassist.compiler.ast.Expr;
import javassist.compiler.ast.InstanceOfExpr;
import javassist.compiler.ast.IntConst;
import javassist.compiler.ast.Keyword;
import javassist.compiler.ast.Member;
import javassist.compiler.ast.NewExpr;
import javassist.compiler.ast.StringL;
import javassist.compiler.ast.Symbol;
import javassist.compiler.ast.Variable;
import javassist.compiler.ast.Visitor;

public class TypeChecker extends Visitor implements Opcode, TokenId {
    static final String javaLangObject = "java.lang.Object";
    static final String jvmJavaLangObject = "java/lang/Object";
    static final String jvmJavaLangString = "java/lang/String";
    static final String jvmJavaLangClass = "java/lang/Class";

    /* The following fields are used by atXXX() methods
     * for returning the type of the compiled expression.
     */
    protected int exprType;     // VOID, NULL, CLASS, BOOLEAN, INT, ...
    protected int arrayDim;
    protected String className; // JVM-internal representation

    protected MemberResolver resolver;
    protected CtClass   thisClass;
    protected MethodInfo thisMethod;

    public TypeChecker(CtClass cc, ClassPool cp) {
        resolver = new MemberResolver(cp);
        thisClass = cc;
        thisMethod = null;
    }

    /*
     * Converts an array of tuples of exprType, arrayDim, and className
     * into a String object.
     */
    protected static String argTypesToString(int[] types, int[] dims,
                                             String[] cnames) {
        StringBuffer sbuf = new StringBuffer();
        sbuf.append('(');
        int n = types.length;
        if (n > 0) {
            int i = 0;
            while (true) {
                typeToString(sbuf, types[i], dims[i], cnames[i]);
                if (++i < n)
                    sbuf.append(',');
                else
                    break;
            }
        }

        sbuf.append(')');
        return sbuf.toString();
    }

    /*
     * Converts a tuple of exprType, arrayDim, and className
     * into a String object.
     */
    protected static StringBuffer typeToString(StringBuffer sbuf,
                                        int type, int dim, String cname) {
        String s;
        if (type == CLASS)
            s = MemberResolver.jvmToJavaName(cname);
        else if (type == NULL)
            s = "Object";
        else
            try {
                s = MemberResolver.getTypeName(type);
            }
            catch (CompileError e) {
                s = "?";
            }

        sbuf.append(s);
        while (dim-- > 0)
            sbuf.append("[]");

        return sbuf;
    }

    
Records the currently compiled method.
/** * Records the currently compiled method. */
public void setThisMethod(MethodInfo m) { thisMethod = m; } protected static void fatal() throws CompileError { throw new CompileError("fatal"); }
Returns the JVM-internal representation of this class name.
/** * Returns the JVM-internal representation of this class name. */
protected String getThisName() { return MemberResolver.javaToJvmName(thisClass.getName()); }
Returns the JVM-internal representation of this super class name.
/** * Returns the JVM-internal representation of this super class name. */
protected String getSuperName() throws CompileError { return MemberResolver.javaToJvmName( MemberResolver.getSuperclass(thisClass).getName()); } /* Converts a class name into a JVM-internal representation. * * It may also expand a simple class name to java.lang.*. * For example, this converts Object into java/lang/Object. */ protected String resolveClassName(ASTList name) throws CompileError { return resolver.resolveClassName(name); } /* Expands a simple class name to java.lang.*. * For example, this converts Object into java/lang/Object. */ protected String resolveClassName(String jvmName) throws CompileError { return resolver.resolveJvmClassName(jvmName); } @Override public void atNewExpr(NewExpr expr) throws CompileError { if (expr.isArray()) atNewArrayExpr(expr); else { CtClass clazz = resolver.lookupClassByName(expr.getClassName()); String cname = clazz.getName(); ASTList args = expr.getArguments(); atMethodCallCore(clazz, MethodInfo.nameInit, args); exprType = CLASS; arrayDim = 0; className = MemberResolver.javaToJvmName(cname); } } public void atNewArrayExpr(NewExpr expr) throws CompileError { int type = expr.getArrayType(); ASTList size = expr.getArraySize(); ASTList classname = expr.getClassName(); ASTree init = expr.getInitializer(); if (init != null) init.accept(this); if (size.length() > 1) atMultiNewArray(type, classname, size); else { ASTree sizeExpr = size.head(); if (sizeExpr != null) sizeExpr.accept(this); exprType = type; arrayDim = 1; if (type == CLASS) className = resolveClassName(classname); else className = null; } } @Override public void atArrayInit(ArrayInit init) throws CompileError { ASTList list = init; while (list != null) { ASTree h = list.head(); list = list.tail(); if (h != null) h.accept(this); } } protected void atMultiNewArray(int type, ASTList classname, ASTList size) throws CompileError { @SuppressWarnings("unused") int count, dim; dim = size.length(); for (count = 0; size != null; size = size.tail()) { ASTree s = size.head(); if (s == null) break; // int[][][] a = new int[3][4][]; ++count; s.accept(this); } exprType = type; arrayDim = dim; if (type == CLASS) className = resolveClassName(classname); else className = null; } @Override public void atAssignExpr(AssignExpr expr) throws CompileError { // =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, >>>= int op = expr.getOperator(); ASTree left = expr.oprand1(); ASTree right = expr.oprand2(); if (left instanceof Variable) atVariableAssign(expr, op, (Variable)left, ((Variable)left).getDeclarator(), right); else { if (left instanceof Expr) { Expr e = (Expr)left; if (e.getOperator() == ARRAY) { atArrayAssign(expr, op, (Expr)left, right); return; } } atFieldAssign(expr, op, left, right); } } /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=. * * expr and var can be null. */ private void atVariableAssign(Expr expr, int op, Variable var, Declarator d, ASTree right) throws CompileError { int varType = d.getType(); int varArray = d.getArrayDim(); String varClass = d.getClassName(); if (op != '=') atVariable(var); right.accept(this); exprType = varType; arrayDim = varArray; className = varClass; } private void atArrayAssign(Expr expr, int op, Expr array, ASTree right) throws CompileError { atArrayRead(array.oprand1(), array.oprand2()); int aType = exprType; int aDim = arrayDim; String cname = className; right.accept(this); exprType = aType; arrayDim = aDim; className = cname; } protected void atFieldAssign(Expr expr, int op, ASTree left, ASTree right) throws CompileError { CtField f = fieldAccess(left); atFieldRead(f); int fType = exprType; int fDim = arrayDim; String cname = className; right.accept(this); exprType = fType; arrayDim = fDim; className = cname; } @Override public void atCondExpr(CondExpr expr) throws CompileError { booleanExpr(expr.condExpr()); expr.thenExpr().accept(this); int type1 = exprType; int dim1 = arrayDim; @SuppressWarnings("unused") String cname1 = className; expr.elseExpr().accept(this); if (dim1 == 0 && dim1 == arrayDim) if (CodeGen.rightIsStrong(type1, exprType)) expr.setThen(new CastExpr(exprType, 0, expr.thenExpr())); else if (CodeGen.rightIsStrong(exprType, type1)) { expr.setElse(new CastExpr(type1, 0, expr.elseExpr())); exprType = type1; } } /* * If atBinExpr() substitutes a new expression for the original * binary-operator expression, it changes the operator name to '+' * (if the original is not '+') and sets the new expression to the * left-hand-side expression and null to the right-hand-side expression. */ @Override public void atBinExpr(BinExpr expr) throws CompileError { int token = expr.getOperator(); int k = CodeGen.lookupBinOp(token); if (k >= 0) { /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>> */ if (token == '+') { Expr e = atPlusExpr(expr); if (e != null) { /* String concatenation has been translated into * an expression using StringBuffer. */ e = CallExpr.makeCall(Expr.make('.', e, new Member("toString")), null); expr.setOprand1(e); expr.setOprand2(null); // <---- look at this! className = jvmJavaLangString; } } else { ASTree left = expr.oprand1(); ASTree right = expr.oprand2(); left.accept(this); int type1 = exprType; right.accept(this); if (!isConstant(expr, token, left, right)) computeBinExprType(expr, token, type1); } } else { /* equation: &&, ||, ==, !=, <=, >=, <, > */ booleanExpr(expr); } } /* EXPR must be a + expression. * atPlusExpr() returns non-null if the given expression is string * concatenation. The returned value is "new StringBuffer().append..". */ private Expr atPlusExpr(BinExpr expr) throws CompileError { ASTree left = expr.oprand1(); ASTree right = expr.oprand2(); if (right == null) { // this expression has been already type-checked. // see atBinExpr() above. left.accept(this); return null; } if (isPlusExpr(left)) { Expr newExpr = atPlusExpr((BinExpr)left); if (newExpr != null) { right.accept(this); exprType = CLASS; arrayDim = 0; className = "java/lang/StringBuffer"; return makeAppendCall(newExpr, right); } } else left.accept(this); int type1 = exprType; int dim1 = arrayDim; String cname = className; right.accept(this); if (isConstant(expr, '+', left, right)) return null; if ((type1 == CLASS && dim1 == 0 && jvmJavaLangString.equals(cname)) || (exprType == CLASS && arrayDim == 0 && jvmJavaLangString.equals(className))) { ASTList sbufClass = ASTList.make(new Symbol("java"), new Symbol("lang"), new Symbol("StringBuffer")); ASTree e = new NewExpr(sbufClass, null); exprType = CLASS; arrayDim = 0; className = "java/lang/StringBuffer"; return makeAppendCall(makeAppendCall(e, left), right); } computeBinExprType(expr, '+', type1); return null; } private boolean isConstant(BinExpr expr, int op, ASTree left, ASTree right) throws CompileError { left = stripPlusExpr(left); right = stripPlusExpr(right); ASTree newExpr = null; if (left instanceof StringL && right instanceof StringL && op == '+') newExpr = new StringL(((StringL)left).get() + ((StringL)right).get()); else if (left instanceof IntConst) newExpr = ((IntConst)left).compute(op, right); else if (left instanceof DoubleConst) newExpr = ((DoubleConst)left).compute(op, right); if (newExpr == null) return false; // not a constant expression expr.setOperator('+'); expr.setOprand1(newExpr); expr.setOprand2(null); newExpr.accept(this); // for setting exprType, arrayDim, ... return true; } /* CodeGen.atSwitchStmnt() also calls stripPlusExpr(). */ static ASTree stripPlusExpr(ASTree expr) { if (expr instanceof BinExpr) { BinExpr e = (BinExpr)expr; if (e.getOperator() == '+' && e.oprand2() == null) return e.getLeft(); } else if (expr instanceof Expr) { // note: BinExpr extends Expr. Expr e = (Expr)expr; int op = e.getOperator(); if (op == MEMBER) { ASTree cexpr = getConstantFieldValue((Member)e.oprand2()); if (cexpr != null) return cexpr; } else if (op == '+' && e.getRight() == null) return e.getLeft(); } else if (expr instanceof Member) { ASTree cexpr = getConstantFieldValue((Member)expr); if (cexpr != null) return cexpr; } return expr; }
If MEM is a static final field, this method returns a constant expression representing the value of that field.
/** * If MEM is a static final field, this method returns a constant * expression representing the value of that field. */
private static ASTree getConstantFieldValue(Member mem) { return getConstantFieldValue(mem.getField()); } public static ASTree getConstantFieldValue(CtField f) { if (f == null) return null; Object value = f.getConstantValue(); if (value == null) return null; if (value instanceof String) return new StringL((String)value); else if (value instanceof Double || value instanceof Float) { int token = (value instanceof Double) ? DoubleConstant : FloatConstant; return new DoubleConst(((Number)value).doubleValue(), token); } else if (value instanceof Number) { int token = (value instanceof Long) ? LongConstant : IntConstant; return new IntConst(((Number)value).longValue(), token); } else if (value instanceof Boolean) return new Keyword(((Boolean)value).booleanValue() ? TokenId.TRUE : TokenId.FALSE); else return null; } private static boolean isPlusExpr(ASTree expr) { if (expr instanceof BinExpr) { BinExpr bexpr = (BinExpr)expr; int token = bexpr.getOperator(); return token == '+'; } return false; } private static Expr makeAppendCall(ASTree target, ASTree arg) { return CallExpr.makeCall(Expr.make('.', target, new Member("append")), new ASTList(arg)); } private void computeBinExprType(BinExpr expr, int token, int type1) throws CompileError { // arrayDim should be 0. int type2 = exprType; if (token == LSHIFT || token == RSHIFT || token == ARSHIFT) exprType = type1; else insertCast(expr, type1, type2); if (CodeGen.isP_INT(exprType) && exprType != BOOLEAN) exprType = INT; // type1 may be BYTE, ... } private void booleanExpr(ASTree expr) throws CompileError { int op = CodeGen.getCompOperator(expr); if (op == EQ) { // ==, !=, ... BinExpr bexpr = (BinExpr)expr; bexpr.oprand1().accept(this); int type1 = exprType; int dim1 = arrayDim; bexpr.oprand2().accept(this); if (dim1 == 0 && arrayDim == 0) insertCast(bexpr, type1, exprType); } else if (op == '!') ((Expr)expr).oprand1().accept(this); else if (op == ANDAND || op == OROR) { BinExpr bexpr = (BinExpr)expr; bexpr.oprand1().accept(this); bexpr.oprand2().accept(this); } else // others expr.accept(this); exprType = BOOLEAN; arrayDim = 0; } private void insertCast(BinExpr expr, int type1, int type2) throws CompileError { if (CodeGen.rightIsStrong(type1, type2)) expr.setLeft(new CastExpr(type2, 0, expr.oprand1())); else exprType = type1; } @Override public void atCastExpr(CastExpr expr) throws CompileError { String cname = resolveClassName(expr.getClassName()); expr.getOprand().accept(this); exprType = expr.getType(); arrayDim = expr.getArrayDim(); className = cname; } @Override public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError { expr.getOprand().accept(this); exprType = BOOLEAN; arrayDim = 0; } @Override public void atExpr(Expr expr) throws CompileError { // array access, member access, // (unary) +, (unary) -, ++, --, !, ~ int token = expr.getOperator(); ASTree oprand = expr.oprand1(); if (token == '.') { String member = ((Symbol)expr.oprand2()).get(); if (member.equals("length")) try { atArrayLength(expr); } catch (NoFieldException nfe) { // length might be a class or package name. atFieldRead(expr); } else if (member.equals("class")) atClassObject(expr); // .class else atFieldRead(expr); } else if (token == MEMBER) { // field read String member = ((Symbol)expr.oprand2()).get(); if (member.equals("class")) atClassObject(expr); // .class else atFieldRead(expr); } else if (token == ARRAY) atArrayRead(oprand, expr.oprand2()); else if (token == PLUSPLUS || token == MINUSMINUS) atPlusPlus(token, oprand, expr); else if (token == '!') booleanExpr(expr); else if (token == CALL) // method call fatal(); else { oprand.accept(this); if (!isConstant(expr, token, oprand)) if (token == '-' || token == '~') if (CodeGen.isP_INT(exprType)) exprType = INT; // type may be BYTE, ... } } private boolean isConstant(Expr expr, int op, ASTree oprand) { oprand = stripPlusExpr(oprand); if (oprand instanceof IntConst) { IntConst c = (IntConst)oprand; long v = c.get(); if (op == '-') v = -v; else if (op == '~') v = ~v; else return false; c.set(v); } else if (oprand instanceof DoubleConst) { DoubleConst c = (DoubleConst)oprand; if (op == '-') c.set(-c.get()); else return false; } else return false; expr.setOperator('+'); return true; } @Override public void atCallExpr(CallExpr expr) throws CompileError { String mname = null; CtClass targetClass = null; ASTree method = expr.oprand1(); ASTList args = (ASTList)expr.oprand2(); if (method instanceof Member) { mname = ((Member)method).get(); targetClass = thisClass; } else if (method instanceof Keyword) { // constructor mname = MethodInfo.nameInit; // <init> if (((Keyword)method).get() == SUPER) targetClass = MemberResolver.getSuperclass(thisClass); else targetClass = thisClass; } else if (method instanceof Expr) { Expr e = (Expr)method; mname = ((Symbol)e.oprand2()).get(); int op = e.getOperator(); if (op == MEMBER) // static method targetClass = resolver.lookupClass(((Symbol)e.oprand1()).get(), false); else if (op == '.') { ASTree target = e.oprand1(); String classFollowedByDotSuper = isDotSuper(target); if (classFollowedByDotSuper != null) targetClass = MemberResolver.getSuperInterface(thisClass, classFollowedByDotSuper); else { try { target.accept(this); } catch (NoFieldException nfe) { if (nfe.getExpr() != target) throw nfe; // it should be a static method. exprType = CLASS; arrayDim = 0; className = nfe.getField(); // JVM-internal e.setOperator(MEMBER); e.setOprand1(new Symbol(MemberResolver.jvmToJavaName( className))); } if (arrayDim > 0) targetClass = resolver.lookupClass(javaLangObject, true); else if (exprType == CLASS /* && arrayDim == 0 */) targetClass = resolver.lookupClassByJvmName(className); else badMethod(); } } else badMethod(); } else fatal(); MemberResolver.Method minfo = atMethodCallCore(targetClass, mname, args); expr.setMethod(minfo); } private static void badMethod() throws CompileError { throw new CompileError("bad method"); }
Returns non-null if target is something like Foo.super for accessing the default method in an interface. Otherwise, null.
Returns:the class name followed by .super or null.
/** * Returns non-null if target is something like Foo.super * for accessing the default method in an interface. * Otherwise, null. * * @return the class name followed by {@code .super} or null. */
static String isDotSuper(ASTree target) { if (target instanceof Expr) { Expr e = (Expr)target; if (e.getOperator() == '.') { ASTree right = e.oprand2(); if (right instanceof Keyword && ((Keyword)right).get() == SUPER) return ((Symbol)e.oprand1()).get(); } } return null; }
Returns: a pair of the class declaring the invoked method and the MethodInfo of that method. Never null.
/** * @return a pair of the class declaring the invoked method * and the MethodInfo of that method. Never null. */
public MemberResolver.Method atMethodCallCore(CtClass targetClass, String mname, ASTList args) throws CompileError { int nargs = getMethodArgsLength(args); int[] types = new int[nargs]; int[] dims = new int[nargs]; String[] cnames = new String[nargs]; atMethodArgs(args, types, dims, cnames); MemberResolver.Method found = resolver.lookupMethod(targetClass, thisClass, thisMethod, mname, types, dims, cnames); if (found == null) { String clazz = targetClass.getName(); String signature = argTypesToString(types, dims, cnames); String msg; if (mname.equals(MethodInfo.nameInit)) msg = "cannot find constructor " + clazz + signature; else msg = mname + signature + " not found in " + clazz; throw new CompileError(msg); } String desc = found.info.getDescriptor(); setReturnType(desc); return found; } public int getMethodArgsLength(ASTList args) { return ASTList.length(args); } public void atMethodArgs(ASTList args, int[] types, int[] dims, String[] cnames) throws CompileError { int i = 0; while (args != null) { ASTree a = args.head(); a.accept(this); types[i] = exprType; dims[i] = arrayDim; cnames[i] = className; ++i; args = args.tail(); } } void setReturnType(String desc) throws CompileError { int i = desc.indexOf(')'); if (i < 0) badMethod(); char c = desc.charAt(++i); int dim = 0; while (c == '[') { ++dim; c = desc.charAt(++i); } arrayDim = dim; if (c == 'L') { int j = desc.indexOf(';', i + 1); if (j < 0) badMethod(); exprType = CLASS; className = desc.substring(i + 1, j); } else { exprType = MemberResolver.descToType(c); className = null; } } private void atFieldRead(ASTree expr) throws CompileError { atFieldRead(fieldAccess(expr)); } private void atFieldRead(CtField f) throws CompileError { FieldInfo finfo = f.getFieldInfo2(); String type = finfo.getDescriptor(); int i = 0; int dim = 0; char c = type.charAt(i); while (c == '[') { ++dim; c = type.charAt(++i); } arrayDim = dim; exprType = MemberResolver.descToType(c); if (c == 'L') className = type.substring(i + 1, type.indexOf(';', i + 1)); else className = null; } /* if EXPR is to access a static field, fieldAccess() translates EXPR * into an expression using '#' (MEMBER). For example, it translates * java.lang.Integer.TYPE into java.lang.Integer#TYPE. This translation * speeds up type resolution by MemberCodeGen. */ protected CtField fieldAccess(ASTree expr) throws CompileError { if (expr instanceof Member) { Member mem = (Member)expr; String name = mem.get(); try { CtField f = thisClass.getField(name); if (Modifier.isStatic(f.getModifiers())) mem.setField(f); return f; } catch (NotFoundException e) { // EXPR might be part of a static member access? throw new NoFieldException(name, expr); } } else if (expr instanceof Expr) { Expr e = (Expr)expr; int op = e.getOperator(); if (op == MEMBER) { Member mem = (Member)e.oprand2(); CtField f = resolver.lookupField(((Symbol)e.oprand1()).get(), mem); mem.setField(f); return f; } else if (op == '.') { try { e.oprand1().accept(this); } catch (NoFieldException nfe) { if (nfe.getExpr() != e.oprand1()) throw nfe; /* EXPR should be a static field. * If EXPR might be part of a qualified class name, * lookupFieldByJvmName2() throws NoFieldException. */ return fieldAccess2(e, nfe.getField()); } CompileError err = null; try { if (exprType == CLASS && arrayDim == 0) return resolver.lookupFieldByJvmName(className, (Symbol)e.oprand2()); } catch (CompileError ce) { err = ce; } /* If a filed name is the same name as a package's, * a static member of a class in that package is not * visible. For example, * * class Foo { * int javassist; * } * * It is impossible to add the following method: * * String m() { return javassist.CtClass.intType.toString(); } * * because javassist is a field name. However, this is * often inconvenient, this compiler allows it. The following * code is for that. */ ASTree oprnd1 = e.oprand1(); if (oprnd1 instanceof Symbol) return fieldAccess2(e, ((Symbol)oprnd1).get()); if (err != null) throw err; } } throw new CompileError("bad filed access"); } private CtField fieldAccess2(Expr e, String jvmClassName) throws CompileError { Member fname = (Member)e.oprand2(); CtField f = resolver.lookupFieldByJvmName2(jvmClassName, fname, e); e.setOperator(MEMBER); e.setOprand1(new Symbol(MemberResolver.jvmToJavaName(jvmClassName))); fname.setField(f); return f; } public void atClassObject(Expr expr) throws CompileError { exprType = CLASS; arrayDim = 0; className =jvmJavaLangClass; } public void atArrayLength(Expr expr) throws CompileError { expr.oprand1().accept(this); if (arrayDim == 0) throw new NoFieldException("length", expr); exprType = INT; arrayDim = 0; } public void atArrayRead(ASTree array, ASTree index) throws CompileError { array.accept(this); int type = exprType; int dim = arrayDim; String cname = className; index.accept(this); exprType = type; arrayDim = dim - 1; className = cname; } private void atPlusPlus(int token, ASTree oprand, Expr expr) throws CompileError { boolean isPost = oprand == null; // ++i or i++? if (isPost) oprand = expr.oprand2(); if (oprand instanceof Variable) { Declarator d = ((Variable)oprand).getDeclarator(); exprType = d.getType(); arrayDim = d.getArrayDim(); } else { if (oprand instanceof Expr) { Expr e = (Expr)oprand; if (e.getOperator() == ARRAY) { atArrayRead(e.oprand1(), e.oprand2()); // arrayDim should be 0. int t = exprType; if (t == INT || t == BYTE || t == CHAR || t == SHORT) exprType = INT; return; } } atFieldPlusPlus(oprand); } } protected void atFieldPlusPlus(ASTree oprand) throws CompileError { CtField f = fieldAccess(oprand); atFieldRead(f); int t = exprType; if (t == INT || t == BYTE || t == CHAR || t == SHORT) exprType = INT; } @Override public void atMember(Member mem) throws CompileError { atFieldRead(mem); } @Override public void atVariable(Variable v) throws CompileError { Declarator d = v.getDeclarator(); exprType = d.getType(); arrayDim = d.getArrayDim(); className = d.getClassName(); } @Override public void atKeyword(Keyword k) throws CompileError { arrayDim = 0; int token = k.get(); switch (token) { case TRUE : case FALSE : exprType = BOOLEAN; break; case NULL : exprType = NULL; break; case THIS : case SUPER : exprType = CLASS; if (token == THIS) className = getThisName(); else className = getSuperName(); break; default : fatal(); } } @Override public void atStringL(StringL s) throws CompileError { exprType = CLASS; arrayDim = 0; className = jvmJavaLangString; } @Override public void atIntConst(IntConst i) throws CompileError { arrayDim = 0; int type = i.getType(); if (type == IntConstant || type == CharConstant) exprType = (type == IntConstant ? INT : CHAR); else exprType = LONG; } @Override public void atDoubleConst(DoubleConst d) throws CompileError { arrayDim = 0; if (d.getType() == DoubleConstant) exprType = DOUBLE; else exprType = FLOAT; } }