Code Search for Developers
 
 
  

AbstractTypeChecker.java from DrJava at Krugle


Show AbstractTypeChecker.java syntax highlighted

/*BEGIN_COPYRIGHT_BLOCK
 *
 * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *    * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 *      names of its contributors may be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software is Open Source Initiative approved Open Source Software.
 * Open Source Initative Approved is a trademark of the Open Source Initiative.
 * 
 * This file is part of DrJava.  Download the current version of this project
 * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 * 
 * END_COPYRIGHT_BLOCK*/

/*
 * DynamicJava - Copyright (C) 1999-2001
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the
 * following conditions:
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL DYADE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Dyade shall not be
 * used in advertising or otherwise to promote the sale, use or other
 * dealings in this Software without prior written authorization from
 * Dyade.
 *
 */

package koala.dynamicjava.interpreter;

import java.lang.reflect.*;
import java.util.*;

import koala.dynamicjava.interpreter.context.*;
import koala.dynamicjava.interpreter.error.*;
import koala.dynamicjava.interpreter.modifier.*;
import koala.dynamicjava.tree.*;
import koala.dynamicjava.tree.visitor.*;
import koala.dynamicjava.util.*;

/**
 * This abstract tree visitor checks the typing rules and loads
 * the classes, fields and methods. The nature of the methods in its child classes is dependent upon 
 * which runtime environment version is being used.
 * 
 * NOTE: This class replaces the class TypeChecker. The static method makeTypeChecker should be used
 * whenever a new TypeChecker is instantiated.
 *
 */

public abstract class AbstractTypeChecker extends VisitorObject<Class<?>> {
  /**
   * The context
   */
  protected Context context;

  /**
   * Creates a new name visitor
   * @param ctx the context
   */
  public AbstractTypeChecker(Context ctx) {
    context = ctx;
  }
  
  /**
   * Builds an appropriate type checker object
   */
  public static AbstractTypeChecker makeTypeChecker(Context ctx) {
    if(!TigerUtilities.isTigerEnabled()) {
      return new TypeChecker14(ctx);
    }
    else {
      return new TypeChecker15(ctx);
    }    
  }
  
  /**
   * Visits a PackageDeclaration
   * @param node the node to visit
   * @return null
   */
  public Class<?> visit(PackageDeclaration node) {
    context.setCurrentPackage(node.getName());
    return null;
  }

  //ImportDeclaration does not currently support static import /**/
  /**
   * Visits an ImportDeclaration
   * @param node the node to visit
   */
  public Class<?> visit(ImportDeclaration node) {
    if(node.isStatic()) {
      staticImportHandler(node);
    }
    else {
      // Declare the package or class importation
      if (node.isPackage()) {
        context.declarePackageImport(node.getName());
      } else {
        try {
          context.declareClassImport(node.getName());
        } catch (ClassNotFoundException e) {
          throw new CatchedExceptionError(e, node);
        }
      }
    }
    return null;
  }
  
  /**
   * Checks if the ImportDeclaration node is a static import and throws an exception if static import is not supported in the current version of the runtime environment
   * @param node the ImportDeclaration node being checked
   */
  protected abstract void staticImportHandler(ImportDeclaration node);

  /**
   * Visits a WhileStatement first by checking the 
   * condition type and unboxing if necessary then by 
   * recurring to its body.
   * @param node the node to visit
   */
  public Class<?> visit(WhileStatement whileStmt) {
    // Check the condition
    Expression exp = whileStmt.getCondition();

    Class<?> type = exp.acceptVisitor(this);
    if (type != boolean.class && type != Boolean.class) {
      throw new ExecutionError("condition.type", whileStmt);
    }

    // Auto unbox: Boolean->boolean
    if (type == Boolean.class) {
      // add method call on expression:
      //   "exp.booleanValue();"
      whileStmt.setCondition(_unbox(exp, type));
    }

    whileStmt.getBody().acceptVisitor(this);
    return null;
  }

  /**
   * Visits a ForEachStatement
   * @param node the node to visit
   */
  public abstract Class<?> visit(ForEachStatement node);

  /**
   * Visits a ForStatement
   * @param node the node to visit
   */
  public Class<?> visit(ForStatement node) {
    // Enter a new scope
    context.enterScope();

    List<Node> l;
    // Check the statements
    if ((l = node.getInitialization()) != null) {
      checkList(l);
    }

    Expression cond = node.getCondition();
    if (cond != null) {

      Class<?> type = cond.acceptVisitor(this);
      if (type != boolean.class && type != Boolean.class) {
        throw new ExecutionError("condition.type", node);
      }

      // Auto unbox: Boolean->boolean
      if (type == Boolean.class) {
        // add method call on expression:
        //   "cond.booleanValue();"
        node.setCondition(_unbox(cond, type));
      }
    }

    if ((l = node.getUpdate()) != null) {
      checkList(l);
    }

    node.getBody().acceptVisitor(this);

    // Leave the current scope and store the defined variables
    // (a map of String-Class<?> mappings) in the "variables" property
    node.setProperty(NodeProperties.VARIABLES, context.leaveScope());
    return null;
  }

  /**
   * Visits a DoStatement
   * @param node the node to visit
   */
  public Class<?> visit(DoStatement node) {
    node.getBody().acceptVisitor(this);

    Expression exp = node.getCondition();
    Class<?> type = exp.acceptVisitor(this);
    if (type != boolean.class && type != Boolean.class) {
      throw new ExecutionError("condition.type", node);
    }

    // Auto unbox: Boolean->boolean
    if (type == Boolean.class) {
      // add method call on expression:
      //   "exp.booleanValue();"
      node.setCondition(_unbox(exp, type));
    }
    return null;
  }

  /**
   * Visits a SwitchStatement
   * @param node the node to visit
   */
  public Class<?> visit(SwitchStatement node) {
    // Visits the components of this node
    Expression exp = node.getSelector();
    Class<?> c = exp.acceptVisitor(this);
    if (c != char.class      && c != byte.class && c != short.class && c != int.class  && 
        c != Character.class && c != Byte.class && c != Short.class && c != Integer.class &&
        // (TigerUtilities.isTigerEnabled() && (c.getSuperclass() != Class.forName("java.lang.Enum")))) {
        !TigerUtilities.isEnum(c) ) {
      node.setProperty(NodeProperties.ERROR_STRINGS,
                       new String[] { c.getName() });
      throw new ExecutionError("selector.type", node);
    }
    // unbox it if needed
    if (c == Character.class || c == Byte.class || c == Short.class || c == Integer.class) {
      node.setSelector(_unbox(exp, c));
    }
    
    // Check the type of the case labels
    Iterator<SwitchBlock> it = node.getBindings().iterator();
    while (it.hasNext()) {
      SwitchBlock sb = it.next();
      sb.acceptVisitor(this);
      exp = sb.getExpression();
      if (exp != null) {
        Class<?> lc = NodeProperties.getType(exp);
        if (lc != char.class &&  lc != byte.class &&
            lc != short.class && lc != int.class &&
            //  (TigerUtilities.isTigerEnabled() && (lc.getSuperclass() != Class.forName("java.lang.Enum")))) {
            (TigerUtilities.isTigerEnabled() && !TigerUtilities.isEnum(lc))) {
          node.setProperty(NodeProperties.ERROR_STRINGS,
                           new String[] { lc.getName() });
          throw new ExecutionError("switch.label.type", node);
        }
        if (c != lc) {
          Number n = null;
          if (exp.hasProperty(NodeProperties.VALUE)) {
            Object cst = exp.getProperty(NodeProperties.VALUE);
            if (lc == char.class) {
              n = new Integer(((Character)cst).charValue());
            } else {
              n = (Number)cst;
            }
          }
          if (c == byte.class) {
            if (exp.hasProperty(NodeProperties.VALUE)) {
              if (n.byteValue() != n.intValue()) {
                node.setProperty(NodeProperties.ERROR_STRINGS,
                                 new String[] { c.getName() });
                throw new ExecutionError
                  ("switch.label.type", node);
              }
            } else {
              throw new ExecutionError("switch.label.type", node);
            }
          } else if (c == short.class || c == char.class) {
            if (exp.hasProperty(NodeProperties.VALUE)) {
              if (n.shortValue() != n.intValue()) {
                node.setProperty(NodeProperties.ERROR_STRINGS,
                                 new String[] { c.getName() });
                throw new ExecutionError
                  ("switch.label.type", node);
              }
            } else if (lc == int.class) {
              node.setProperty(NodeProperties.ERROR_STRINGS,
                               new String[] { c.getName() });
              throw new ExecutionError("switch.label.type", node);
            }
          }
        }
      }
    }
    
    return null;
  }

  /**
   * Visits a SwitchBlock
   * @param node the node to visit
   */
  public Class<?> visit(SwitchBlock node) {
    Expression exp = node.getExpression();
    if (exp != null) {
      exp.acceptVisitor(this);
    }
    List<Node> l = node.getStatements();
    if (l != null) {
      checkList(l);
    }
    return null;
  }

  /**
   * Visits a LabeledStatement
   * @param node the node to visit
   */
  public Class<?> visit(LabeledStatement node) {
    node.getStatement().acceptVisitor(this);
    return null;
  }

  /**
   * Visits a TryStatement
   * @param node the node to visit
   */
  public Class<?> visit(TryStatement node) {
    node.getTryBlock().acceptVisitor(this);
    Iterator<CatchStatement> it = node.getCatchStatements().iterator();
    while (it.hasNext()) {
      it.next().acceptVisitor(this);
    }
    Node n = node.getFinallyBlock();
    if (n != null) {
      n.acceptVisitor(this);
    }
    return null;
  }

  /**
   * Visits a CatchStatement
   * @param node the node to visit
   */
  public Class<?> visit(CatchStatement node) {
    // Enter a new scope
    context.enterScope();

    Class<?> c = node.getException().acceptVisitor(this);
    if (!Throwable.class.isAssignableFrom(c)) {
      node.setProperty(NodeProperties.ERROR_STRINGS,
                       new String[] { c.getName() });
      throw new ExecutionError("catch.type", node);
    }

    node.getBlock().acceptVisitor(this);

    // Leave the current scope
    context.leaveScope();
    node.setProperty(NodeProperties.TYPE, c);
    return null;
  }

  /**
   * Visits a ThrowStatement
   * @param node the node to visit
   */
  public Class<?> visit(ThrowStatement node) {
    Class<?> c = node.getExpression().acceptVisitor(this);
    if (!Throwable.class.isAssignableFrom(c)) {
      node.setProperty(NodeProperties.ERROR_STRINGS,
                       new String[] { c.getName() });
      throw new ExecutionError("throw.type", node);
    }
    return null;
  }

  /**
   * Visits a ReturnStatement
   * @param node the node to visit
   */
  public Class<?> visit(ReturnStatement node) {
    Expression e = node.getExpression();
    if (e != null) {
      e.acceptVisitor(this);
    }
    return null;
  }

  /**
   * Visits an IfThenStatement
   * @param node the node to visit
   */
  public Class<?> visit(IfThenStatement node) {
    Expression cond = node.getCondition();
    
    // Check the condition
    Class<?> type = cond.acceptVisitor(this);
    if (type != boolean.class && type != Boolean.class) {
      throw new ExecutionError("condition.type", node);
    }
    
    // Auto unbox: Boolean->boolean
    if (type == Boolean.class) {
      // add method call on expression:
      //   "cond.booleanValue();"
      node.setCondition(_unbox(cond, type));
    }
    
    node.getThenStatement().acceptVisitor(this);
    return null;
  }

  /**
   * Visits an IfThenElseStatement
   * @param node the node to visit
   */
  public Class<?> visit(IfThenElseStatement node) {
    Expression cond = node.getCondition();
    
    // Check the condition
    Class<?> type = cond.acceptVisitor(this);
    if (type != boolean.class && type != Boolean.class) {
      throw new ExecutionError("condition.type", node);
    }
    
    // Auto unbox: Boolean->boolean
    if (type == Boolean.class) {
      // add method call on expression:
      //   "cond.booleanValue();"
      node.setCondition(_unbox(cond, type));
    }

    node.getThenStatement().acceptVisitor(this);
    node.getElseStatement().acceptVisitor(this);
    return null;
  }

    /**
   * Visits an AssertStatement
   * @param node the node to visit
   */
  public Class<?> visit(AssertStatement node) {
    Expression cond = node.getCondition();
    
    //Check the condition
    Class<?> type = cond.acceptVisitor(this);
    if(type != boolean.class && type != Boolean.class) {
      throw new ExecutionError("condition.type", node);
    }
    
    // Auto unbox; Boolean->boolean
    if(type == Boolean.class) {
      //add method call on expression;
      // "cond.booleanValue();"
      node.setCondition(_unbox(cond, type));
    }
    
    //Check the failure string
    Expression failString = node.getFailString();
    if(failString != null) {
      Class<?> type2 = failString.acceptVisitor(this);
    }
    return null;
  }
  
  /**
   * Visits a SynchronizedStatement
   * @param node the node to visit
   */
  public Class<?> visit(SynchronizedStatement node) {
    // Check the lock
    if ((node.getLock().acceptVisitor(this)).isPrimitive()) {
      throw new ExecutionError("lock.type", node);
    }

    node.getBody().acceptVisitor(this);
    return null;
  }

  /**
   * Visits a Literal
   * @param node the node to visit
   */
  public Class<?> visit(Literal node) {
    // Set the properties of the node
    Class<?> c = node.getType();
    node.setProperty(NodeProperties.VALUE, node.getValue());
    node.setProperty(NodeProperties.TYPE, c);
    return c;
  }

  /**
   * Visits a VariableDeclaration
   * @param node the node to visit
   */
  public Class<?> visit(VariableDeclaration node) {
    // Define the variable
    Class<?> lc = node.getType().acceptVisitor(this);
    if (node.isFinal()) {
      context.defineConstant(node.getName(), lc);
    } 
    else {
      context.define(node.getName(), lc);
    }

    // Get the type of the initializer
    Expression init = node.getInitializer();
    if (init != null) {
      Class<?> rc = init.acceptVisitor(this);
      Expression exp = checkAssignmentStaticRules(lc, rc, node, init);
      node.setInitializer(exp);
    }
    
    return null;
  }

//  /**
//   * This method visits a variable declaration without actually defining
//   * anything in the context.  This method was added to allow other objects
//   * to type check a variable declaration without actually creating a new
//   * binding in the context.  This behavior was added so that when a
//   * variable declaration has some error in it, the declaration would not
//   * make it into the context. (this method should be run on an individual 
//   * variable declaration before the type checker visitor is formally 
//   * executed on the entire AST)
//   * @param node the variable declaration to check
//   */
//  public void preCheckVariableDeclaration(VariableDeclaration node) {
//    Class<?> lc = node.getType().acceptVisitor(this);
//    Expression init = node.getInitializer();
//    if (init != null) {
//      Class<?> rc = init.acceptVisitor(this);
//      // this call to checkAssignmentStaticRules is not
//      // intended to mutate the AST for autoboxing/unboxing
//      checkAssignmentStaticRules(lc, rc, node, init);
//    }
//  }

  /**
   * Visits a BlockStatement
   * @param node the node to visit
   */
  public Class<?> visit(BlockStatement node) {
    // Enter a new scope
    context.enterScope();

    // Do the type checking of the nested statements
    checkList(node.getStatements());

    // Leave the current scope and store the defined variables
    // (a map of String-Class<?> mappings) in the "variables" property
    node.setProperty(NodeProperties.VARIABLES, context.leaveScope());

    return null;
  }

  /**
   * Visits an ObjectFieldAccess
   * @param node the node to visit
   */
  public Class<?> visit(ObjectFieldAccess node) {
    // Visit the expression
    Class<?> c = node.getExpression().acceptVisitor(this);

    // Load the field object
    if (!c.isArray()) {
      Field f = null;
      try {
        f = context.getField(c, node.getFieldName());
      } catch (Exception e) { throw new CatchedExceptionError(e, node); }

      // Set the node properties
      c = f.getType();
      node.setProperty(NodeProperties.FIELD, f);
      node.setProperty(NodeProperties.TYPE, c);
      node.setProperty(NodeProperties.MODIFIER, context.getModifier(node));
      return c;
      
    } else {
      if (!node.getFieldName().equals("length")) {
        String s0 = "length";
        String s1 = c.getComponentType().getName() + " array";
        node.setProperty(NodeProperties.ERROR_STRINGS, new String[] { s0, s1 });
        throw new ExecutionError("no.such.field", node);
      }
      node.setProperty(NodeProperties.TYPE,  int.class);
      node.setProperty(NodeProperties.MODIFIER, new InvalidModifier(node));
      return int.class;
    }
  }

  /**
   * Visits a SuperFieldAccess
   * @param node the node to visit
   */
  public Class<?> visit(SuperFieldAccess node) {
    Field f = null;
    try {
      f = context.getSuperField(node, node.getFieldName());
    } catch (Exception e) {
      throw new CatchedExceptionError(e, node);
    }

    // Set the node properties
    node.setProperty(NodeProperties.FIELD, f);
    Class<?> c;
    node.setProperty(NodeProperties.TYPE,  c = f.getType());
    node.setProperty(NodeProperties.MODIFIER, context.getModifier(node));
    return c;
  }

  /**
   * Visits a StaticFieldAccess
   * @param node the node to visit
   */
  public Class<?> visit(StaticFieldAccess node) {
    // Visit the field type
    Class<?> c = node.getFieldType().acceptVisitor(this);

    // Load the field object
    Field f = null;
    try {
      f = context.getField(c, node.getFieldName());
    } catch (Exception e) {
      throw new CatchedExceptionError(e, node);
    }

    // Set the node properties
    node.setProperty(NodeProperties.FIELD, f);
    node.setProperty(NodeProperties.TYPE,  c = f.getType());

    node.setProperty(NodeProperties.MODIFIER, context.getModifier(node));
    return c;
  }

  /**
   * Visits an ObjectMethodCall
   * @param node the node to visit
   */
  public Class<?> visit(ObjectMethodCall node) {
    // Check the receiver
    Expression exp = node.getExpression();
    Class<?>      c   = exp.acceptVisitor(this);
    String     mn  = node.getMethodName();

    if (!c.isArray() || (c.isArray() && !mn.equals("clone"))) {
      // Do the type checking of the arguments
      List<Expression> args = node.getArguments();
      Class<?>[] cargs = Constants.EMPTY_CLASS_ARRAY;
      if (args != null) {
        cargs = new Class[args.size()];
        Iterator<Expression> it = args.iterator();
        int i  = 0;
        while (it.hasNext()) {
          cargs[i++] = it.next().acceptVisitor(this);
        }
      }
      Method m = null;
      try {
        m = context.lookupMethod(exp, mn, cargs);
      }
      catch (NoSuchMethodException e) {
        String s = c.getName();
        String sargs = "";
        for (int i = 0; i < cargs.length-1; i++) {
          sargs += cargs[i].getName() + ", ";
        }
        if (cargs.length > 0) {
          sargs += cargs[cargs.length-1].getName();
        }
        node.setProperty(NodeProperties.ERROR_STRINGS, new String[] { mn, s, sargs });
        throw new ExecutionError("no.such.method.with.args", node);
      }
      catch (MethodModificationError e) {
        Expression expr = e.getExpression();
        expr.acceptVisitor(this);
        node.setExpression(expr);
        m = e.getMethod();
      }

      // Set the node properties
      node.setProperty(NodeProperties.METHOD, m);
      node.setProperty(NodeProperties.TYPE,   c = m.getReturnType());
      return c;
    } 
    else {
      if (!mn.equals("clone") || node.getArguments() != null) {
        String s0 = "clone";
        String s1 = c.getComponentType().getName() + " array";
        node.setProperty(NodeProperties.ERROR_STRINGS, new String[] { s0, s1 });
        throw new ExecutionError("no.such.method", node);
      }
      node.setProperty(NodeProperties.TYPE, c = Object.class);
      return c;
    }
  }

  /**
   * Visits a MethodDeclaration
   * Note: the checkVarArgs here may or may not actually work. The test case in Distinction1415 failed when tiger features
   * were disabled, and when tracing through, the visitor that actually acted upon the method declaration node was the ClassInfoCompiler, and
   * there is another statement throwing a WrongVersionException in this file.
   * @param node the node to visit
   */
  public Class<?> visit(MethodDeclaration node) {
    checkVarArgs(node);
    context.defineFunction(node);

    node.setProperty(NodeProperties.TYPE, node.getReturnType().acceptVisitor(this));
    node.setProperty(NodeProperties.FUNCTIONS, context.getFunctions());
    node.setProperty(NodeProperties.IMPORTATION_MANAGER,
                     context.getImportationManager().clone());

    context.enterScope();
    checkList(node.getParameters());
    context.leaveScope();
    return null;
  }
  
  /**
   * Checks to see if the MethodDeclaration has variable arguments and whether or not they are allowed
   * @param node - the MethodDeclaration which may or may not contain variable arguments
   */
  protected abstract void checkVarArgs(MethodDeclaration node);

  
  /**
   * Visits a FunctionCall
   * @param node the node to visit
   */
  public Class<?> visit(FunctionCall node) {
    // Do the type checking of the arguments
    List<Expression> args = node.getArguments();
    Class<?>[] cargs = Constants.EMPTY_CLASS_ARRAY;
    if (args != null) {
      cargs = new Class[args.size()];
      Iterator<Expression> it = args.iterator();
      int i  = 0;
      while (it.hasNext()) {
        cargs[i++] = it.next().acceptVisitor(this);
      }
    }
    MethodDeclaration f = null;
    try {
      f = context.lookupFunction(node.getMethodName(), cargs);
    } catch (NoSuchFunctionException e) {
      String s = node.getMethodName();
      node.setProperty(NodeProperties.ERROR_STRINGS, new String[] { s });
      throw new ExecutionError("no.such.function", node);
    }

    // Set the node properties
    Class<?> c;
    node.setProperty(NodeProperties.FUNCTION, f);
    node.setProperty(NodeProperties.TYPE,  c = NodeProperties.getType(f));
    return c;
  }

  /**
   * Visits a SuperMethodCall
   * @param node the node to visit
   */
  public Class<?> visit(SuperMethodCall node) {
    // Do the type checking of the arguments
    List<Expression> args = node.getArguments();
    Class<?>[] pt = Constants.EMPTY_CLASS_ARRAY;
    if (args != null) {
      pt = new Class[args.size()];
      Iterator<Expression> it = args.iterator();
      int i = 0;
      while (it.hasNext()) {
        pt[i++] = it.next().acceptVisitor(this);
      }
    }
    Method m = null;
    try {
      m = context.lookupSuperMethod(node, node.getMethodName(), pt);
    } catch (Exception e) {
      throw new CatchedExceptionError(e, node);
    }

    // Set the node properties
    Class<?> c;
    node.setProperty(NodeProperties.METHOD, m);
    node.setProperty(NodeProperties.TYPE,   c = m.getReturnType());
    return c;
  }

  /**
   * Visits a StaticMethodCall
   * @param node the node to visit
   */
  public Class<?> visit(StaticMethodCall node) {
    // Do the type checking of the arguments
    List<Expression> args = node.getArguments();
    Class<?>[] cargs = Constants.EMPTY_CLASS_ARRAY;
    if (args != null) {
      cargs = new Class[args.size()];
      Iterator<Expression> it = args.iterator();
      int      i  = 0;
      while (it.hasNext()) {
        cargs[i++] = it.next().acceptVisitor(this);
      }
    }
    Method m = null;
    Node   n = node.getMethodType();
    Class<?>  c = n.acceptVisitor(this);

    try {
      m = context.lookupMethod(n, node.getMethodName(), cargs);
    } catch (NoSuchMethodException e) {
      String s0 = node.getMethodName();
      String s1 = c.getName();
      String sargs = "";
      for (int i = 0; i < cargs.length-1; i++) {
        sargs += cargs[i].getName() + ", ";
      }
      if (cargs.length > 0) {
        sargs += cargs[cargs.length-1].getName();
      }
      node.setProperty(NodeProperties.ERROR_STRINGS, new String[] { s0, s1, sargs });
      throw new ExecutionError("no.such.method.with.args", node);
    }

    // Set the node properties
    node.setProperty(NodeProperties.METHOD, m);
    node.setProperty(NodeProperties.TYPE,   c = m.getReturnType());
    return c;
  }

  /**
   * Visits a SimpleAssignExpression
   * @param node the node to visit
   */
  public Class<?> visit(SimpleAssignExpression node) {
    Expression left  = node.getLeftExpression();
    Expression right = node.getRightExpression();
    Class<?> rc = right.acceptVisitor(this);

    // Perhaps is this assignment a variable declaration
    if (left instanceof QualifiedName) {
      String var = ((QualifiedName)left).getRepresentation();
      if (!context.exists(var)) {
        context.define(var, (rc == null) ? Object.class : rc);
      }
    }
    
    // Get the type of the left hand side
    Class<?> lc = left.acceptVisitor(this);

    // The left subexpression must be a variable
    if (!left.hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("left.expression", node);
    }

    // Check the validity of the assignment
    Expression exp = checkAssignmentStaticRules(lc, rc, node, right);
    node.setRightExpression(exp);

    node.setProperty(NodeProperties.TYPE, lc);
    return lc;
  }

  /**
   * Visits a QualifiedName
   * @param node the node to visit
   */
  public Class<?> visit(QualifiedName node) {
    String var = node.getRepresentation();
    
    // Set the modifier
    Class<?> c = (Class<?>) context.get(var);
    node.setProperty(NodeProperties.TYPE, c);
    
    node.setProperty(NodeProperties.MODIFIER, context.getModifier(node));
    return c;
    
  }

  /**
   * Visits a SimpleAllocation
   * @param node the node to visit
   */
  public Class<?> visit(SimpleAllocation node) {
    // Check the type to declare
    Node type = node.getCreationType();
    Class<?> c  = type.acceptVisitor(this);

    // Do the type checking of the arguments
    List<Expression> args = node.getArguments();
    Class<?>[] cargs = Constants.EMPTY_CLASS_ARRAY;

    if (args != null) {
      cargs = new Class[args.size()];

      ListIterator<Expression> it = args.listIterator();
      int i  = 0;
      while (it.hasNext()) {
        cargs[i++] = it.next().acceptVisitor(this);
      }
    }

    return context.setProperties(node, c, cargs);
  }

  /**
   * Visits a InnerAllocation
   * @param node the node to visit
   */
  public Class<?> visit(InnerAllocation node) {
    // Visit the expression
    Class<?> ec = node.getExpression().acceptVisitor(this);

    // Check the type to declare
    Node type = node.getCreationType();
    if (type instanceof ReferenceTypeName) {
      ReferenceTypeName rt = (ReferenceTypeName)type;
      rt.setRepresentation(ec.getName() + "$" + rt.getRepresentation());
    } else {
      throw new ExecutionError("allocation.type", node);
    }
    Class<?> c = type.acceptVisitor(this);
    Class<?> dc = InterpreterUtilities.getDeclaringClass(c);

    // Do the type checking of the arguments
    List<Expression> args = node.getArguments();
    Class<?>[] cargs = null;

    if (dc != null && dc.isAssignableFrom(ec)) {
      // Adds an argument if the class to build is an innerclass
      if (args != null) {
        cargs = new Class<?>[args.size() + 1];

        cargs[0] = ec;
        ListIterator<Expression> it = args.listIterator();
        int i  = 1;
        while (it.hasNext()) {
          cargs[i++] = it.next().acceptVisitor(this);
        }
      } else {
        cargs = new Class<?>[] { ec };
      }
    } else {
      throw new ExecutionError("allocation.type", node);
    }
    Constructor cons = null;
    try {
      cons = context.lookupConstructor(c, cargs);
    } catch (Exception e) {
      throw new CatchedExceptionError(e, node);
    }

    // Set the properties of this node
    node.setProperty(NodeProperties.TYPE,        c);
    node.setProperty(NodeProperties.CONSTRUCTOR, cons);

    return c;
  }

  /**
   * Visits a ClassAllocation
   * @param node the node to visit
   */
  public Class<?> visit(ClassAllocation node) {
    // If the class allocation is the initializer of a field,
    // it is possible that it has already been visited
    if (node.hasProperty(NodeProperties.TYPE)) {
      return NodeProperties.getType(node);
    } else {
      // Get the class to allocate
      Node   ctn   = node.getCreationType();
      Class<?>   ct   = ctn.acceptVisitor(this);
      List<Expression>   largs = node.getArguments();
      Class<?>[] args = Constants.EMPTY_CLASS_ARRAY;

      if (largs != null) {
        args = new Class[largs.size()];
        Iterator<Expression> it = largs.iterator();
        int i = 0;
        while (it.hasNext()) {
          args[i++] = it.next().acceptVisitor(this);
        }
      }
      return context.setProperties(node, ct, args, node.getMembers());
    }
  }

  /**
   * Visits an ArrayAllocation
   * @param node the node to visit
   */
  public Class<?> visit(ArrayAllocation node) {
    // Do the checking of the size expressions
    ListIterator<Expression> it = node.getSizes().listIterator();
    
    while (it.hasNext()) {
      Expression exp = it.next();
      Class<?> c = exp.acceptVisitor(this);
      
      // Dimension expression must be of an integral type, but not long
      if (c != char.class      && c != byte.class && c != short.class && c != int.class &&
          c != Character.class && c != Byte.class && c != Short.class && c != Integer.class) {
        throw new ExecutionError("array.dimension.type", node);
      }
      
      // un-box the size of the array, if necessary
      if (TigerUtilities.isBoxingType(c)) {
        it.set(_unbox(exp, c));
      }
    }

    // TypeName-check the type of the array
    Class<?> c = node.getCreationType().acceptVisitor(this);

    // Visits the initializer if one
    if (node.getInitialization() != null) {
      node.getInitialization().acceptVisitor(this);
    }

    // Set the type properties of this node
    Class<?> ac = Array.newInstance(c, new int[node.getDimension()]).getClass();
    node.setProperty(NodeProperties.TYPE, ac);
    node.setProperty(NodeProperties.COMPONENT_TYPE, c);
    return ac;
  }

  /**
   * Visits a ArrayInitializer
   * @param node the node to visit
   */
  public Class<?> visit(ArrayInitializer node) {
    node.getElementType().acceptVisitor(this);

    checkList(node.getCells());
    return null;
  }

  /**
   * Visits an ArrayAccess
   * @param node the node to visit
   */
  public Class<?> visit(ArrayAccess node) {
    // Visits the expression on which this array access applies
    Class<?> c = node.getExpression().acceptVisitor(this);

    // Make sure this is an array
    if (!c.isArray()) {
      node.setProperty(NodeProperties.ERROR_STRINGS,
                       new String[] { c.getName() });
      throw new ExecutionError("array.required", node);
    }

    // Sets the properties of this node
    Class<?> result = c.getComponentType();
    node.setProperty(NodeProperties.TYPE, result);
    node.setProperty(NodeProperties.MODIFIER, new ArrayModifier(node));

    // Visits the cell number expression
    c = node.getCellNumber().acceptVisitor(this);

    // The index must be of an integral type, but not a long
    if (c != char.class      && c != byte.class && c != short.class && c != int.class &&
        c != Character.class && c != Byte.class && c != Short.class && c != Integer.class) {
      throw new ExecutionError("array.index.type", node);
    }
    
    // un-box the index into the array, if necessary
    if (TigerUtilities.isBoxingType(c)) {
      node.setCellNumber(_unbox(node.getCellNumber(), c));
    }
    
    return result;
  }

  /**
   * Visits a PrimitiveTypeName
   * @param node the node to visit
   */
  public Class<?> visit(PrimitiveTypeName node) {
    Class<?> c = node.getValue();
    node.setProperty(NodeProperties.TYPE, c);
    return c;
  }

  /**
   * Visits a ReferenceTypeName
   * @param node the node to visit
   */
  public Class<?> visit(ReferenceTypeName node) {
    checkGenericReferenceType(node);
    Class<?> c = null;
    try {
      c = context.lookupClass(node.getRepresentation());
    } 
    catch (ClassNotFoundException e) {
      node.setProperty(NodeProperties.ERROR_STRINGS,
                       new String[] { node.getRepresentation() });
      throw new ExecutionError("undefined.class", node);
    }
    node.setProperty(NodeProperties.TYPE, c);
    return c;
  }
  
  /**
   * If java earlier than 1.5, checks if the node is a 
   * GenericReferenceTypeName
   * @param node unused
   */  
  protected abstract void checkGenericReferenceType(ReferenceTypeName node);
  

  /**
   * Visits an ArrayTypeName
   * @param node the node to visit
   */
  public Class<?> visit(ArrayTypeName node) {
    Node eType = node.getElementType();
    Class<?> c = eType.acceptVisitor(this);
    Class<?> ac = Array.newInstance(c, 0).getClass();

    // Set the type property of this node
    node.setProperty(NodeProperties.TYPE, ac);
    return ac;
  }

  /**
   * Visits a TypeExpression
   * @param node the node to visit
   */
  public Class<?> visit(TypeExpression node) {
    Class<?> c = node.getType().acceptVisitor(this);
    node.setProperty(NodeProperties.TYPE, Class.class);
    node.setProperty(NodeProperties.VALUE, c);
    return Class.class;
  }

  /**
   * Visits a NotExpression
   * @param node the node to visit
   */
  public Class<?> visit(NotExpression node) {
    // Check the type
    Expression exp = node.getExpression();
    Class<?> c = exp.acceptVisitor(this);

    if (c != boolean.class && c != Boolean.class) {
      throw new ExecutionError("not.expression.type", node);
    }
    node.setProperty(NodeProperties.TYPE, boolean.class);

    // Auto-unbox, if necessary
    if (TigerUtilities.isBoxingType(c)) {
      node.setExpression(_unbox(exp, c));
    }

    // Compute the expression if it is constant
    if (exp.hasProperty(NodeProperties.VALUE)) {
      if (((Boolean) exp.getProperty(NodeProperties.VALUE)).booleanValue()) {
        node.setProperty(NodeProperties.VALUE, Boolean.FALSE);
      } 
      else {
        node.setProperty(NodeProperties.VALUE, Boolean.TRUE);
      }
    }
    return boolean.class;
  }

  /**
   * Visits a ComplementExpression
   * @param node the node to visit
   */
  public Class<?> visit(ComplementExpression node) {
    // Check the type
    Expression e = node.getExpression();
    Class<?> c = e.acceptVisitor(this);
    Class<?> returnType = c;

    if (c == char.class      || c == byte.class || c == short.class ||
        c == Character.class || c == Byte.class || c == Short.class) {
      node.setProperty(NodeProperties.TYPE, int.class);
    } 
    else if (c == int.class     || c == long.class ||
             c == Integer.class || c == Long.class) {
      if (c == Integer.class) {
        returnType = int.class;
      }
      else if (c == Long.class) {
        returnType = long.class;
      }
      node.setProperty(NodeProperties.TYPE, returnType);
    } 
    else {
      throw new ExecutionError("complement.expression.type", node);
    }

    // Auto-unbox, if necessary
    if (TigerUtilities.isBoxingType(c)) {
      node.setExpression(_unbox(e, c));
    }
    
    // Compute the expression if it is constant
    if (e.hasProperty(NodeProperties.VALUE)) {
      Object o = e.getProperty(NodeProperties.VALUE);
      
      if (o instanceof Character) {
        o = new Integer(((Character)o).charValue());
      }
      if (c == int.class) {
        o = new Integer(~((Number)o).intValue());
      } 
      else {
        o = new Long(~((Number)o).longValue());
      }
      node.setProperty(NodeProperties.VALUE, o);
    }
    return returnType;
  }

  /**
   * Visits a PlusExpression
   * @param node the node to visit
   */
  public Class<?> visit(PlusExpression node) {
    Class<?> c = visitUnaryOperation(node, "plus.expression.type");

    // Compute the expression if it is constant
    Node  n = node.getExpression();
    if (n.hasProperty(NodeProperties.VALUE)) {
      node.setProperty(NodeProperties.VALUE,
                       InterpreterUtilities.plus(c, n.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits a MinusExpression
   * @param node the node to visit
   */
  public Class<?> visit(MinusExpression node) {
    Class<?> c = visitUnaryOperation(node, "minus.expression.type");

    // Compute the expression if it is constant
    Node  n = node.getExpression();
    if (n.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         InterpreterUtilities.minus(c, n.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits an AddExpression
   * @param node the node to visit
   */
  public Class<?> visit(AddExpression node) {
    // Check the types
    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();
    Class<?> lc = ln.acceptVisitor(this);
    Class<?> rc = rn.acceptVisitor(this);
    Class<?> c  = String.class;

    if (lc != String.class && rc != String.class) {
      c = visitNumericExpression(node, "addition.type");
    } 
    else {
      node.setProperty(NodeProperties.TYPE, c);
    }

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) && 
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty(NodeProperties.VALUE,
                       InterpreterUtilities.add(c, ln.getProperty(NodeProperties.VALUE),
                                                rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits an AddAssignExpression
   * @param node the node to visit
   */
  public Class<?> visit(AddAssignExpression node) {
    // Check the types
    Node  ln = node.getLeftExpression();
    Class<?> lc = ln.acceptVisitor(this);
    Class<?> rc = node.getRightExpression().acceptVisitor(this);

    // Do some error checking for null, void, etc.
    if (lc != String.class) {
      if (lc == null          || rc == null          ||
          lc == void.class    || rc == void.class    ||
          lc == boolean.class || rc == boolean.class || 
          lc == Boolean.class || rc == Boolean.class ||
          !(lc.isPrimitive()  || TigerUtilities.isBoxingType(lc))  || 
          !(rc.isPrimitive()  || TigerUtilities.isBoxingType(rc)) ) {
        throw new ExecutionError("addition.type", node);
      }
    }

    // The left subexpression must be a variable
    if (!ln.hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("left.expression", node);
    }

    // Sets the type property of this node
    node.setProperty(NodeProperties.TYPE, lc);
    return lc;
  }

  /**
   * Visits a SubtractExpression
   * @param node the node to visit
   */
  public Class<?> visit(SubtractExpression node) {
    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();
    ln.acceptVisitor(this);
    rn.acceptVisitor(this);
    Class<?> c = visitNumericExpression(node, "subtraction.type");

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         InterpreterUtilities.subtract(c,
                                       ln.getProperty(NodeProperties.VALUE),
                                       rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits an SubtractAssignExpression
   * @param node the node to visit
   */
  public Class<?> visit(SubtractAssignExpression node) {
    // Check the types
    Node  ln = node.getLeftExpression();
    Class<?> lc = ln.acceptVisitor(this);
    Class<?> rc = node.getRightExpression().acceptVisitor(this);


    // Do some error checking for null, void, etc.
    if (lc == null          || rc == null          ||
        lc == void.class    || rc == void.class    ||
        lc == boolean.class || rc == boolean.class ||
        lc == Boolean.class || rc == Boolean.class ||
        !(lc.isPrimitive()  || TigerUtilities.isBoxingType(lc))  || 
        !(rc.isPrimitive()  || TigerUtilities.isBoxingType(rc)) ) {
      throw new ExecutionError("subtraction.type", node);
    }

    // The left subexpression must be a variable
    if (!ln.hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("left.expression", node);
    }

    // Sets the type property of this node
    node.setProperty(NodeProperties.TYPE, lc);
    return lc;
  }

  /**
   * Visits a MultiplyExpression
   * @param node the node to visit
   */
  public Class<?> visit(MultiplyExpression node) {
    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();
    ln.acceptVisitor(this);
    rn.acceptVisitor(this);
    Class<?> c = visitNumericExpression(node, "multiplication.type");

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         InterpreterUtilities.multiply(c,
                                       ln.getProperty(NodeProperties.VALUE),
                                       rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits an MultiplyAssignExpression
   * @param node the node to visit
   */
  public Class<?> visit(MultiplyAssignExpression node) {
    // Check the types
    Node  ln = node.getLeftExpression();
    Class<?> lc = ln.acceptVisitor(this);
    Class<?> rc = node.getRightExpression().acceptVisitor(this);

    // Do some error checking for null, void, etc.
    if (lc == null          || rc == null          ||
        lc == void.class    || rc == void.class    ||
        lc == boolean.class || rc == boolean.class ||
        lc == Boolean.class || rc == Boolean.class ||
        !(lc.isPrimitive()  || TigerUtilities.isBoxingType(lc))  || 
        !(rc.isPrimitive()  || TigerUtilities.isBoxingType(rc)) ) {
      throw new ExecutionError("multiplication.type", node);
    }

    // The left subexpression must be a variable
    if (!ln.hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("left.expression", node);
    }

    // Sets the type property of this node
    node.setProperty(NodeProperties.TYPE, lc);
    return lc;
  }

//  /**
//   * Visits a DivideExpression
//   * @param node the node to visit
//   */
//  public Class<?> visit(DivideExpression node) {
//    Node  ln = node.getLeftExpression();
//    Node  rn = node.getRightExpression();
//    ln.acceptVisitor(this);
//    rn.acceptVisitor(this);
//    Class<?> c = visitNumericExpression(node, "division.type");
//
//    // Compute the expression if it is constant
//    if (ln.hasProperty(NodeProperties.VALUE) &&
//        rn.hasProperty(NodeProperties.VALUE)) {
//      node.setProperty
//        (NodeProperties.VALUE,
//         InterpreterUtilities.divide(c,
//                                     ln.getProperty(NodeProperties.VALUE),
//                                     rn.getProperty(NodeProperties.VALUE)));
//    }
//    return c;
//  }
  
    /**
   * Visits a DivideExpression
   * @param node the node to visit
   */
  public Class<?> visit(DivideExpression node) {
    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();
    ln.acceptVisitor(this);
    rn.acceptVisitor(this);
    Class<?> c = visitNumericExpression(node, "division.type");
    return c;
  }

  /**
   * Visits a RemainderExpression
   * @param node the node to visit
   */
  public Class<?> visit(RemainderExpression node) {
    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();
    ln.acceptVisitor(this);
    rn.acceptVisitor(this);
    Class<?> c = visitNumericExpression(node, "remainder.type");
    return c;
  }
  

  /**
   * Visits an DivideAssignExpression
   * @param node the node to visit
   */
  public Class<?> visit(DivideAssignExpression node) {
    // Check the types
    Node  ln = node.getLeftExpression();
    Class<?> lc = ln.acceptVisitor(this);
    Class<?> rc = node.getRightExpression().acceptVisitor(this);

    // Do some error checking for null, void, etc.
    if (lc == null          || rc == null          ||
        lc == void.class    || rc == void.class    ||
        lc == boolean.class || rc == boolean.class ||
        lc == Boolean.class || rc == Boolean.class ||
        !(lc.isPrimitive()  || TigerUtilities.isBoxingType(lc))  || 
        !(rc.isPrimitive()  || TigerUtilities.isBoxingType(rc)) ) {
      throw new ExecutionError("division.type", node);
    }

    // The left subexpression must be a variable
    if (!ln.hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("left.expression", node);
    }

    // Sets the type property of this node
    node.setProperty(NodeProperties.TYPE, lc);
    return lc;
  }

//  /**
//   * Visits a RemainderExpression
//   * @param node the node to visit
//   */
//  public Class<?> visit(RemainderExpression node) {
//    Node  ln = node.getLeftExpression();
//    Node  rn = node.getRightExpression();
//    ln.acceptVisitor(this);
//    rn.acceptVisitor(this);
//    Class<?> c = visitNumericExpression(node, "remainder.type");
//
//    // Compute the expression if it is constant
//    if (ln.hasProperty(NodeProperties.VALUE) &&
//        rn.hasProperty(NodeProperties.VALUE)) {
//      node.setProperty
//        (NodeProperties.VALUE,
//         InterpreterUtilities.remainder(c,
//                                        ln.getProperty(NodeProperties.VALUE),
//                                        rn.getProperty(NodeProperties.VALUE)));
//    }
//    return c;
//  }

  /**
   * Visits an RemainderAssignExpression
   * @param node the node to visit
   */
  public Class<?> visit(RemainderAssignExpression node) {
    // Check the types
    Node  ln = node.getLeftExpression();
    Class<?> lc = ln.acceptVisitor(this);
    Class<?> rc = node.getRightExpression().acceptVisitor(this);

    // Do some error checking for null, void, etc.
    if (lc == null          || rc == null          ||
        lc == void.class    || rc == void.class    ||
        lc == boolean.class || rc == boolean.class ||
        lc == Boolean.class || rc == Boolean.class ||
        !(lc.isPrimitive()  || TigerUtilities.isBoxingType(lc))  || 
        !(rc.isPrimitive()  || TigerUtilities.isBoxingType(rc)) ) {
      throw new ExecutionError("remainder.type", node);
    }

    // The left subexpression must be a variable
    if (!ln.hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("left.expression", node);
    }

    // Sets the type property of this node
    node.setProperty(NodeProperties.TYPE, lc);
    return lc;
  }

  /**
   * Visits an EqualExpression
   * @param node the node to visit
   */
  public Class<?> visit(EqualExpression node) {
    // Check the types
    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();
    Class<?> lc = ln.acceptVisitor(this);
    Class<?> rc = rn.acceptVisitor(this);

    // Check the equality rules, and un-box
    checkEqualityStaticRules(lc, rc, node);

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty(NodeProperties.VALUE,
                       InterpreterUtilities.equalTo(lc, rc,
                                                    ln.getProperty(NodeProperties.VALUE),
                                                    rn.getProperty(NodeProperties.VALUE)));
    }

    // Set the type property
    node.setProperty(NodeProperties.TYPE, boolean.class);
    
    // Return the type of the expression (always boolean)
    return boolean.class;
  }

  /**
   * Visits an NotEqualExpression
   * @param node the node to visit
   */
  public Class<?> visit(NotEqualExpression node) {
    // Check the types
    Node ln = node.getLeftExpression();
    Node rn = node.getRightExpression();
    Class<?> lc = ln.acceptVisitor(this);
    Class<?> rc = rn.acceptVisitor(this);

    // Check the equality rules, and un-box
    checkEqualityStaticRules(lc, rc, node);

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty(NodeProperties.VALUE,
                       InterpreterUtilities.notEqualTo(lc, rc,
                                                       ln.getProperty(NodeProperties.VALUE),
                                                       rn.getProperty(NodeProperties.VALUE)));
    }

    // Set the type property
    node.setProperty(NodeProperties.TYPE, boolean.class);
    
    // Return boolean type
    return boolean.class;
  }

  /**
   * Visits a LessExpression
   * @param node the node to visit
   */
  public Class<?> visit(LessExpression node) {
    Class<?> c = visitRelationalExpression(node);

    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         InterpreterUtilities.lessThan(ln.getProperty(NodeProperties.VALUE),
                                       rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits a LessOrEqualExpression
   * @param node the node to visit
   */
  public Class<?> visit(LessOrEqualExpression node) {
    Class<?> c = visitRelationalExpression(node);

    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         InterpreterUtilities.lessOrEqual(ln.getProperty(NodeProperties.VALUE),
                                          rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits a GreaterExpression
   * @param node the node to visit
   */
  public Class<?> visit(GreaterExpression node) {
    Class<?> c = visitRelationalExpression(node);

    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         InterpreterUtilities.greaterThan(ln.getProperty(NodeProperties.VALUE),
                                          rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits a GreaterOrEqualExpression
   * @param node the node to visit
   */
  public Class<?> visit(GreaterOrEqualExpression node) {
    Class<?> c = visitRelationalExpression(node);

    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         InterpreterUtilities.greaterOrEqual(ln.getProperty(NodeProperties.VALUE),
                                             rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits a BitAndExpression
   * @param node the node to visit
   */
  public Class<?> visit(BitAndExpression node) {
    Class<?> c = visitBitwiseExpression(node);

    Node ln = node.getLeftExpression();
    Node rn = node.getRightExpression();

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty(NodeProperties.VALUE,
                       InterpreterUtilities.bitAnd(c,
                                                   ln.getProperty(NodeProperties.VALUE),
                                                   rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits a BitAndAssignExpression
   * @param node the node to visit
   */
  public Class<?> visit(BitAndAssignExpression node) {
    return visitBitwiseAssign(node);
  }

  /**
   * Visits a BitOrExpression
   * @param node the node to visit
   */
  public Class<?> visit(BitOrExpression node) {
    Class<?> c = visitBitwiseExpression(node);

    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         InterpreterUtilities.bitOr
           (c,
            ln.getProperty(NodeProperties.VALUE),
            rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits a BitOrAssignExpression
   * @param node the node to visit
   */
  public Class<?> visit(BitOrAssignExpression node) {
    return visitBitwiseAssign(node);
  }

  /**
   * Visits a ExclusiveOrExpression
   * @param node the node to visit
   */
  public Class<?> visit(ExclusiveOrExpression node) {
    Class<?> c = visitBitwiseExpression(node);

    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         InterpreterUtilities.xOr
           (c,
            ln.getProperty(NodeProperties.VALUE),
            rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits a ExclusiveOrAssignExpression
   * @param node the node to visit
   */
  public Class<?> visit(ExclusiveOrAssignExpression node) {
    return visitBitwiseAssign(node);
  }

  /**
   * Visits a ShiftLeftExpression
   * @param node the node to visit
   */
  public Class<?> visit(ShiftLeftExpression node) {
    Class<?> c = visitShiftExpression(node);

    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         InterpreterUtilities.shiftLeft
           (NodeProperties.getType(node),
            ln.getProperty(NodeProperties.VALUE),
            rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits a ShiftLeftAssignExpression
   * @param node the node to visit
   */
  public Class<?> visit(ShiftLeftAssignExpression node) {
    Class<?> c = visitShiftExpression(node);

    // The left subexpression must be a variable
    if (!node.getLeftExpression().hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("shift.left.type", node);
    }

    return c;
  }

  /**
   * Visits a ShiftRightExpression
   * @param node the node to visit
   */
  public Class<?> visit(ShiftRightExpression node) {
    Class<?> c = visitShiftExpression(node);

    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         InterpreterUtilities.shiftRight
           (NodeProperties.getType(node),
            ln.getProperty(NodeProperties.VALUE),
            rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits a ShiftRightAssignExpression
   * @param node the node to visit
   */
  public Class<?> visit(ShiftRightAssignExpression node) {
    Class<?> c = visitShiftExpression(node);

    // The left subexpression must be a variable
    if (!node.getLeftExpression().hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("shift.right.type", node);
    }

    return c;
  }

  /**
   * Visits a UnsignedShiftRightExpression
   * @param node the node to visit
   */
  public Class<?> visit(UnsignedShiftRightExpression node) {
    Class<?> c = visitShiftExpression(node);

    Node  ln = node.getLeftExpression();
    Node  rn = node.getRightExpression();

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         InterpreterUtilities.unsignedShiftRight
           (NodeProperties.getType(node),
            ln.getProperty(NodeProperties.VALUE),
            rn.getProperty(NodeProperties.VALUE)));
    }
    return c;
  }

  /**
   * Visits a UnsignedShiftRightAssignExpression
   * @param node the node to visit
   */
  public Class<?> visit(UnsignedShiftRightAssignExpression node) {
    Class<?> c = visitShiftExpression(node);

    // The left subexpression must be a variable
    if (!node.getLeftExpression().hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("unsigned.shift.right.type", node);
    }

    return c;
  }

  /**
   * Visits an AndExpression
   * @param node the node to visit
   */
  public Class<?> visit(AndExpression node) {
    Expression le = node.getLeftExpression();
    Expression re = node.getRightExpression();
    Class<?> lc = le.acceptVisitor(this);
    Class<?> rc = re.acceptVisitor(this);

    // Check the types of the operands
    if (!(lc == boolean.class || lc == Boolean.class) || 
        !(rc == boolean.class || rc == Boolean.class) ) {
      throw new ExecutionError("and.type", node);
    }

    // Auto-unbox, if necessary
    if (lc == Boolean.class) {
      node.setLeftExpression(_unbox(le, lc));
    }
    if (rc == Boolean.class) {
      node.setRightExpression(_unbox(re, rc));
    }
    
    // Compute the expression if it is constant
    if (le.hasProperty(NodeProperties.VALUE) &&
        re.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         (((Boolean)le.getProperty(NodeProperties.VALUE)).booleanValue() &&
          ((Boolean)re.getProperty(NodeProperties.VALUE)).booleanValue())
           ? Boolean.TRUE : Boolean.FALSE);
    }

    // Set the type property
    node.setProperty(NodeProperties.TYPE, boolean.class);
    return boolean.class;
  }

  /**
   * Visits an OrExpression
   * @param node the node to visit
   */
  public Class<?> visit(OrExpression node) {
    Expression ln = node.getLeftExpression();
    Expression rn = node.getRightExpression();
    Class<?> lc = ln.acceptVisitor(this);
    Class<?> rc = rn.acceptVisitor(this);

    // Check the types of the operands
    if (!(lc == boolean.class || lc == Boolean.class) || 
        !(rc == boolean.class || rc == Boolean.class) ) {
      throw new ExecutionError("or.type", node);
    }

    // Auto-unbox, if necessary
    if (lc == Boolean.class) {
      node.setLeftExpression(_unbox(ln, lc));
    }
    if (rc == Boolean.class) {
      node.setRightExpression(_unbox(rn, rc));
    }

    // Compute the expression if it is constant
    if (ln.hasProperty(NodeProperties.VALUE) &&
        rn.hasProperty(NodeProperties.VALUE)) {
      node.setProperty
        (NodeProperties.VALUE,
         (((Boolean)ln.getProperty(NodeProperties.VALUE)).booleanValue() ||
          ((Boolean)rn.getProperty(NodeProperties.VALUE)).booleanValue())
           ? Boolean.TRUE : Boolean.FALSE);
    }

    // Set the type property
    node.setProperty(NodeProperties.TYPE, boolean.class);
    return boolean.class;
  }

//  /**
//   * Visits a InstanceOfExpression
//   * @param node the node to visit
//   */
//  public Class<?> visit(InstanceOfExpression node) {
//    node.getReferenceType().acceptVisitor(this);
//
//    // The expression must not have a primitive type
//    if ((node.getExpression().acceptVisitor(this)).isPrimitive()) {
//      throw new ExecutionError("left.expression", node);
//    }
//
//    // Set the type property
//    node.setProperty(NodeProperties.TYPE, boolean.class);
//    return boolean.class;
//  }
  
  
  /**
   * Visits a InstanceOfExpression
   * @param node the node to visit
   */
  public Class<?> visit(InstanceOfExpression node) {
    node.getReferenceType().acceptVisitor(this);

    // The expression must not have a primitive type
    Class<?> c = node.getExpression().acceptVisitor(this);
    if ((c != null) && c.isPrimitive()) {
      throw new ExecutionError("left.expression", node);
    }

    // Set the type property
    node.setProperty(NodeProperties.TYPE, boolean.class);
    return boolean.class;
  }

  /**
   * Visits a ConditionalExpression
   * @param node the node to visit
   */
  public Class<?> visit(ConditionalExpression node) {
    // Get the type of the conidition expression
    Expression condExp = node.getConditionExpression();
    Class<?> type = condExp.acceptVisitor(this);
    
    // Check the condition
    if (type != boolean.class && type != Boolean.class) {
      throw new ExecutionError("condition.type", node);
    }

    // Auto-unbox, if necessary
    if (type == Boolean.class) {
      node.setConditionExpression(_unbox(condExp, type));
    }

    // Determine the type of the expression
    Expression exp1 = node.getIfTrueExpression();
    Expression exp2 = node.getIfFalseExpression();
    Class<?> c1 = exp1.acceptVisitor(this);
    Class<?> c2 = exp2.acceptVisitor(this);
    Class<?> ec = null;

    // unbox a boxing type, except when the boxing type is 
    // Boolean and the other is not boolean
    if (TigerUtilities.isBoxingType(c1) && c2.isPrimitive()) {
      if (!(c1 == Boolean.class && c2 != boolean.class)) {
        exp1 = _unbox(exp1, c1);
        c1 = _correspondingPrimType(c1).getValue();
        node.setIfTrueExpression(exp1);
      }
    }
    else if (TigerUtilities.isBoxingType(c2) && c1.isPrimitive()) {
      if (!(c2 == Boolean.class && c1 != boolean.class)) {
        exp2 = _unbox(exp2, c2);
        c2 = _correspondingPrimType(c2).getValue();
        node.setIfFalseExpression(exp2);
      }
    }
    
    // See if the expression is typable
    if (c1 == c2) {
      ec = c1;
    } 
    else if (c1 == null) {
      ec = c2;
    }
    else if (c2 == null) {
      ec = c1;
    }
    else if (!c1.isPrimitive() && !c2.isPrimitive()) {
      if (c1.isAssignableFrom(c2)) {
        ec = c1;
      }
      else if (c2.isAssignableFrom(c1)) {
        ec = c2;
      }
      else {
        // originally set to throw an error, this case now 
        // simply returns Object, the lowest common type
        ec = Object.class;
      }
    }
    else if (c1 == void.class || c2 == void.class) {
      throw new ExecutionError("incompatible.types", node);
    }
    else if (c1 == boolean.class || c2 == boolean.class ||
             c1 == Boolean.class || c2 == Boolean.class) {
      if ((c1 == boolean.class && c2.isPrimitive()) || 
          (c2 == boolean.class && c1.isPrimitive())) {
        // box both
        node.setIfTrueExpression (_box(exp1, TigerUtilities.correspondingBoxingType(c1)));
        node.setIfFalseExpression(_box(exp2, TigerUtilities.correspondingBoxingType(c2)));
        ec = Object.class;
      }
      else if (c1 == Boolean.class && c2.isPrimitive()) {
        // box c2
        node.setIfFalseExpression(_box(exp2, TigerUtilities.correspondingBoxingType(c2)));
        ec = Object.class;
      }
      else if (c2 == Boolean.class && c1.isPrimitive()) {
        // box c1
        node.setIfTrueExpression(_box(exp1, TigerUtilities.correspondingBoxingType(c1)));
        ec = Object.class;
      }
      else if (c1 == boolean.class && TigerUtilities.isBoxingType(c2)) {
        // box c1
        node.setIfTrueExpression(_box(exp1, Boolean.class));
        ec = Object.class;
      }
      else if (c2 == boolean.class && TigerUtilities.isBoxingType(c1)) {
        // box c2
        node.setIfFalseExpression(_box(exp2, Boolean.class));
        ec = Object.class;
      }
      else {
        throw new ExecutionError("incompatible.types", node);
      }
    }
    else if ((c1 == short.class && c2 == byte.class) ||
             (c1 == byte.class  && c2 == short.class)) {
      ec = short.class;
    }
    else if ((c2 == byte.class || c2 == short.class || c2 == char.class) &&
             exp1.hasProperty(NodeProperties.VALUE) && c1 == int.class) {
      Number n = (Number) exp1.getProperty(NodeProperties.VALUE);
      if (c2 == byte.class) {
        if (n.intValue() == n.byteValue()) {
          ec = byte.class;
        }
        else {
          ec = int.class;
        }
      } 
      else if (n.intValue() == n.shortValue()) {
        ec = (c2 == char.class) ? char.class : short.class;
      }
      else {
        ec = int.class;
      }
    } 
    else if ((c1 == byte.class || c1 == short.class || c1 == char.class) &&
               exp2.hasProperty(NodeProperties.VALUE) && c2 == int.class) {
      Number n = (Number)exp2.getProperty(NodeProperties.VALUE);
      if (c1 == byte.class) {
        if (n.intValue() == n.byteValue()) {
          ec = byte.class;
        } 
        else {
          ec = int.class;
        }
      } 
      else if (n.intValue() == n.shortValue()) {
        ec = (c1 == char.class) ? char.class : short.class;
      }
      else {
        ec = int.class;
      }
    } 
    else if (c1 == double.class || c2 == double.class) {
      ec = double.class;
    }
    else if (c1 == float.class || c2 == float.class) {
      ec = float.class;
    } 
    else if (c1 == long.class || c2 == long.class) {
      ec = long.class;
    }
    else {
      ec = int.class;
    }
    node.setProperty(NodeProperties.TYPE, ec);

    return ec;
  }

  /**
   * Visits a FormalParameter
   * @param node the node to visit
   * @return the class of the parameter
   */
  public Class<?> visit(FormalParameter node) {
    Class<?> c = node.getType().acceptVisitor(this);

    if (node.isFinal()) {
      context.defineConstant(node.getName(), c);
    } else {
      context.define(node.getName(), c);
    }
    node.setProperty(NodeProperties.TYPE, c);

    return c;
  }

  /**
   * Visits a PostIncrement
   * @param node the node to visit
   */
  public Class<?> visit(PostIncrement node) {

    Node exp = node.getExpression();
    Class<?> c  = exp.acceptVisitor(this);
    // The type of the subexpression must be numeric
    if (!(c.isPrimitive() || TigerUtilities.isBoxingType(c)) ||
        c == void.class     ||
        c == boolean.class  ||
        c == Boolean.class) {
      throw new ExecutionError("post.increment.type", node);
    }

    // The subexpression must be a variable
    if (!exp.hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("post.increment.type", node);
    }

    node.setProperty(NodeProperties.TYPE, c);
    return c;
  }

  /**
   * Visits a PreIncrement
   * @param node the node to visit
   */
  public Class<?> visit(PreIncrement node) {
    Node exp = node.getExpression();
    Class<?> c  = exp.acceptVisitor(this);

    // The type of the subexpression must be numeric
    if (!(c.isPrimitive() || TigerUtilities.isBoxingType(c)) ||
        c == void.class     ||
        c == boolean.class  ||
        c == Boolean.class) {
      throw new ExecutionError("pre.increment.type", node);
    }

    // The subexpression must be a variable
    if (!exp.hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("pre.increment.type", node);
    }

    node.setProperty(NodeProperties.TYPE, c);
    return c;
  }

  /**
   * Visits a PostDecrement
   * @param node the node to visit
   */
  public Class<?> visit(PostDecrement node) {
    Node exp = node.getExpression();
    Class<?> c  = exp.acceptVisitor(this);

    // The type of the subexpression must be numeric
    if (!(c.isPrimitive() || TigerUtilities.isBoxingType(c)) ||
        c == void.class     ||
        c == boolean.class  ||
        c == Boolean.class) {
      throw new ExecutionError("post.decrement.type", node);
    }

    // The subexpression must be a variable
    if (!exp.hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("post.decrement.type", node);
    }

    node.setProperty(NodeProperties.TYPE, c);
    return c;
  }

  /**
   * Visits a PreDecrement
   * @param node the node to visit
   */
  public Class<?> visit(PreDecrement node) {
    Node exp = node.getExpression();
    Class<?> c  = exp.acceptVisitor(this);

    // The type of the subexpression must be numeric
    if (!(c.isPrimitive() || TigerUtilities.isBoxingType(c)) ||
        c == void.class     ||
        c == boolean.class  ||
        c == Boolean.class) {
      throw new ExecutionError("pre.decrement.type", node);
    }

    // The subexpression must be a variable
    if (!exp.hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("pre.decrement.type", node);
    }

    node.setProperty(NodeProperties.TYPE, c);
    return c;
  }

  /**
   * Visits a CastExpression
   * @param node the node to visit
   */
  public Class<?> visit(CastExpression node) {
    Class<?> c = node.getTargetType().acceptVisitor(this);
    checkCastStaticRules(c, node.getExpression().acceptVisitor(this), node);

    node.setProperty(NodeProperties.TYPE, c);
    return c;
  }

  /**
   * Visits an unary operation.
   */
  private Class<?> visitUnaryOperation(UnaryExpression node, String s) {
    Expression exp = node.getExpression();
    Class<?> c = exp.acceptVisitor(this);
    Class<?> returnType = c;

    if (c == char.class      || c == byte.class || c == short.class || c == int.class ||
        c == Character.class || c == Byte.class || c == Short.class || c == Integer.class) {
      node.setProperty(NodeProperties.TYPE, int.class);
    }
    else if (c == long.class || c == float.class || c == double.class ||
             c == Long.class || c == Float.class || c == Double.class) {
      if (c == Long.class) {
        returnType = long.class;
      }
      else if (c == Float.class) {
        returnType = float.class;
      }
      else if (c == Double.class) {
        returnType = double.class;
      }
      node.setProperty(NodeProperties.TYPE, returnType);
    }
    else {
      throw new ExecutionError(s, node);
    }
    
    // Auto-unbox, if necessary
    if (TigerUtilities.isBoxingType(c)) {
      node.setExpression(_unbox(exp, c));
    }
    
    return returnType;
  }

  /**
   * Visits a numeric expression.
   * This method checks the types on any numeric binary operator
   * performing any needed unboxing or numeric promotion on either
   * of the operands.
   * @param node the Binary expression to type check
   * @param s The key to use when throwing an ExecutionError
   * @return the type of the expression after any numeric promotion or 
   * unboxing has been performed
   */
  protected Class<?> visitNumericExpression(BinaryExpression node, String s) {
    Expression leftExp = node.getLeftExpression();
    Expression rightExp = node.getRightExpression();
    
    // Set the type property of the given node
    Class<?> lc = NodeProperties.getType(leftExp);
    Class<?> rc = NodeProperties.getType(rightExp);
    Class<?> c  = null;

    // Check to make sure the left and right types are valid
    if (lc == null           || rc == null          ||
        lc == boolean.class  || rc == boolean.class ||
        !(lc.isPrimitive()   || TigerUtilities.isBoxingType(lc))  || 
        !(rc.isPrimitive()   || TigerUtilities.isBoxingType(rc))  ||
        lc == void.class     || rc == void.class) {
      throw new ExecutionError(s, node);
    } 

    // Auto-unbox, if necessary
    if (TigerUtilities.isBoxingType(lc)) {
      node.setLeftExpression(_unbox(leftExp, lc));
    }
    if (TigerUtilities.isBoxingType(rc)) {
      node.setRightExpression(_unbox(rightExp, rc));
    }
    
    // Set the type of the node to be the binary promotion
    if (lc == double.class || lc == Double.class || 
        rc == double.class || rc == Double.class) {
      c = double.class;
      node.setProperty(NodeProperties.TYPE, c);
    } 
    else if (lc == float.class || lc == Float.class ||
             rc == float.class || rc == Float.class) {
      c = float.class;
      node.setProperty(NodeProperties.TYPE, c);
    } 
    else if (lc == long.class || lc == Long.class ||
             rc == long.class || rc == Long.class) {
      c = long.class;
      node.setProperty(NodeProperties.TYPE, c);
    } 
    else {
      c = int.class;
      node.setProperty(NodeProperties.TYPE, c);
    }
    
    // Return the type of the node
    return c;
  }
  
  
  /**
   * Checks the typing rules for an assignment
   * @param lc   the class of the left part of an assignment
   * @param rc   the class of the right part of an assignment
   * @param node the entire assignment node
   * @param v    The right-hand side of the expression
   * @return The right-hand side of the assignment.  This expression will
   * be the unboxing/boxing of the RHS if necessary.
   */
  public Expression checkAssignmentStaticRules(Class<?> lc, Class<?> rc,
                                                 Node node, Expression v) {
    if (lc != null) {
      if (lc.isPrimitive()) {
        if (lc == boolean.class && rc != boolean.class) {
          if (rc == Boolean.class) {
            return _unbox(v,Boolean.class);
          }
          throw new ExecutionError("assignment.types", node);
        }
        else if (lc == byte.class && rc != byte.class) {
          if (rc == Byte.class) {
            return _unbox(v,Byte.class);
          }
          if (rc == int.class && v.hasProperty(NodeProperties.VALUE)) {
            Number n = (Number)v.getProperty(NodeProperties.VALUE);
            if (n.intValue() == n.byteValue()) {
              return v;
            }
          }
          throw new ExecutionError("assignment.types", node);
        }
        else if ((lc == short.class || lc == char.class) &&
                 (rc != byte.class && rc != short.class && rc != char.class)) {
          if (lc == short.class && rc == Short.class) {
            return _unbox(v, Short.class);
          }
          if (lc == char.class && rc == Character.class) {
            return _unbox(v, Character.class);
          }
          if (rc == int.class && v.hasProperty(NodeProperties.VALUE)) {
            Number n = (Number)v.getProperty(NodeProperties.VALUE);
            if (n.intValue() == n.shortValue()) {
              return v;
            }
          }
          throw new ExecutionError("assignment.types", node);
        }
        else if (lc == int.class    &&
                 (rc != byte.class  &&
                  rc != short.class &&
                  rc != char.class  &&
                  rc != int.class)) {
          if (rc == Byte.class      || rc == Short.class ||
              rc == Character.class || rc == Integer.class) {
            return _unbox(v, rc);
          }
          throw new ExecutionError("assignment.types", node);
        }
        else if (lc == long.class   &&
                 (rc == null          ||
                  !rc.isPrimitive()   ||
                  rc == void.class    ||
                  rc == boolean.class ||
                  rc == float.class   ||
                  rc == double.class)) {
          if (TigerUtilities.isBoxingType(rc) && TigerUtilities.isIntegralType(rc)) {
            return _unbox(v, rc);
          }
          throw new ExecutionError("assignment.types", node);
        }
        else if (lc == float.class  &&
                 (rc == null          ||
                  !rc.isPrimitive()   ||
                  rc == void.class    ||
                  rc == boolean.class ||
                  rc == double.class)) {
          if (TigerUtilities.isBoxingType(rc) && rc != Boolean.class && rc != Double.class) {
            return _unbox(v, rc);
          }
          throw new ExecutionError("assignment.types", node);
        }
        else if (lc == double.class &&
                 (rc == null        ||
                  !rc.isPrimitive() ||
                  rc == void.class  ||
                  rc == boolean.class)) {
          if (TigerUtilities.isBoxingType(rc) && rc != Boolean.class) {
            return _unbox(v, rc);
          }
          throw new ExecutionError("assignment.types", node);
        }
      }
      else /* lc is not Primitive */
        if (rc != null) {
          if (rc.isPrimitive()) {
            Class<?> boxedRc = _correspondingRefClass(rc);
            if (lc.isAssignableFrom(boxedRc)) return _box(v,boxedRc);
            if (TigerUtilities.boxesTo(rc, lc)) return _box(v, lc); /* I think this statement is unnecessary.  Corky 6/19/04 */
            throw new ExecutionError("assignment.types", node);
          }
        if (!lc.isAssignableFrom(rc) && !rc.isAssignableFrom(lc)) { /* I don't know why the second clause appears in this test.  Corky 6/19/04 */
          throw new ExecutionError("assignment.types", node);
        }
      }
    }
    return v;
  }

  /**
   * Checks the typing rules in an equality operation
   * @param lc the class of the left operand
   * @param rc the class of the right operand
   * @param s  the error message
   * @param n  the current node
   */
  private void checkEqualityStaticRules(Class<?> lc, Class<?> rc, BinaryExpression n) {
    Expression leftExp = n.getLeftExpression();
    Expression rightExp = n.getRightExpression();

    // Auto-unbox, if necessary
    /**
     * We have not decided what the correct semantics
     * for the == operator is for boxed/primitive types
     */
    if (lc != null && rc != null) {
      if (TigerUtilities.isBoxingType(lc) && rc.isPrimitive()) {
        ObjectMethodCall methodCall = _unbox(leftExp, lc);
        n.setLeftExpression(methodCall);
        lc = (Class) methodCall.getProperty(NodeProperties.TYPE);
      }
      if (TigerUtilities.isBoxingType(rc) && lc.isPrimitive()) {
        ObjectMethodCall methodCall = _unbox(rightExp, rc);
        n.setRightExpression(methodCall);
        rc = (Class) methodCall.getProperty(NodeProperties.TYPE);
      }
    }

    if (lc != rc || lc == void.class) {
      if (lc == void.class    || rc == void.class ||
          lc == boolean.class || rc == boolean.class) {
        throw new ExecutionError("compare.type", n);
      } 
      else if ((lc == null && rc.isPrimitive()) ||
               (rc == null && lc.isPrimitive())) {
        throw new ExecutionError("compare.type", n);
      } 
      else if (lc != null && rc != null) {
        if (lc.isPrimitive() ^ rc.isPrimitive()) {
          throw new ExecutionError("compare.type", n);
        }
        else if (!lc.isPrimitive() && !rc.isPrimitive()) {  
          if (!lc.isAssignableFrom(rc) && !rc.isAssignableFrom(lc)) {
            throw new ExecutionError("compare.type", n);
          }
        }
      }
    }
  }

  /**
   * Visits a relational expression.  This simply makes sure
   * the types are numerical primitives/boxing types, 
   * performs the unboxing if necessary, and sets the type
   * of the overall expression to the boolean primitive type
   * @param node the relational expression: (> < >= <=)
   * @return the type of the expression: boolean
   */
  private Class<?> visitRelationalExpression(BinaryExpression node) {
    // Check the types
    Expression leftExp = node.getLeftExpression();
    Expression rightExp = node.getRightExpression();
    Class<?> lc = leftExp.acceptVisitor(this);
    Class<?> rc = rightExp.acceptVisitor(this);

    if (lc == null          || rc == null           ||
        lc == void.class    || rc == void.class     ||
        lc == boolean.class || rc == boolean.class  ||
        !(lc.isPrimitive()  || TigerUtilities.isBoxingType(lc))   || 
        !(rc.isPrimitive()  || TigerUtilities.isBoxingType(rc))) {
      throw new ExecutionError("relational.expression.type", node);
    }

    // Auto-unbox, if necessary
    if (TigerUtilities.isBoxingType(lc)) {
      node.setLeftExpression(_unbox(leftExp, lc));
    }
    if (TigerUtilities.isBoxingType(rc)) {
      node.setRightExpression(_unbox(rightExp, rc));
    }
    
    // The type of the expression is always boolean
    node.setProperty(NodeProperties.TYPE, boolean.class);
    return boolean.class;
  }

  /**
   * Visits a bitwise expression.
   * If either the left or right expression is an integral
   * boxing type, this method performs the unboxing procedure
   * and uses the primitive type of the ObjectMethodCall to 
   * perform the any numeric promotion necessary.
   * @param node the bitwise expression to be type checked.
   * @return the type of the expression after unboxing and 
   *         promotion has been executed
   */
  private Class<?> visitBitwiseExpression(BinaryExpression node) {
    // Check the types
    Expression leftExp = node.getLeftExpression();
    Expression rightExp = node.getRightExpression();
    Class<?> lc = leftExp.acceptVisitor(this);
    Class<?> rc = rightExp.acceptVisitor(this);
    Class<?> c = null;
    
    boolean intLeft   = TigerUtilities.isIntegralType(lc);
    boolean intRight  = TigerUtilities.isIntegralType(rc);
    boolean boolLeft  = (lc == boolean.class  || lc == Boolean.class);
    boolean boolRight = (rc == boolean.class  || rc == Boolean.class);
    
//    if (lc == null           || rc == null           ||
//        lc == void.class     || rc == void.class     ||
//        lc == float.class    || rc == float.class    ||
//        lc == Float.class    || rc == Float.class    ||
//        lc == double.class   || rc == double.class   ||
//        lc == Double.class   || rc == Double.class   ||
//        (lc == boolean.class ^ rc == boolean.class)  ||
//        !lc.isPrimitive()    || !rc.isPrimitive()) {
    if (!(  intLeft &&  intRight ) && 
        !( boolLeft && boolRight ) ){
      throw new ExecutionError("bitwise.expression.type", node);
    } 
    
    
    // Auto-unbox, if necessary
    if (TigerUtilities.isBoxingType(lc)) {
      node.setLeftExpression(_unbox(leftExp, lc));
    }
    if (TigerUtilities.isBoxingType(rc)) {
      node.setRightExpression(_unbox(rightExp, rc));
    }
    
    if (lc == long.class || rc == long.class ||
        lc == Long.class || rc == Long.class) {
      node.setProperty(NodeProperties.TYPE, c = long.class);
    }
    else if ( boolLeft ) {
      node.setProperty(NodeProperties.TYPE, c = boolean.class);
    }
    else {
      node.setProperty(NodeProperties.TYPE, c = int.class);
    }
    return c;
  }

  /**
   * Checks a bitwise expression
   */
  private Class<?> visitBitwiseAssign(BinaryExpression node) {
    // Check the types
    Node  ln = node.getLeftExpression();
    Class<?> lc = ln.acceptVisitor(this);
    Class<?> rc = node.getRightExpression().acceptVisitor(this);

    if (lc == null           || rc == null             ||
        lc == void.class     || rc == void.class       ||
        lc == float.class    || rc == float.class      ||
        lc == double.class   || rc == double.class     ||
        ((lc == boolean.class || lc == Boolean.class) ^ 
         (rc == boolean.class || rc == Boolean.class)) ||
        !(lc.isPrimitive() || TigerUtilities.isBoxingType(lc))       || 
        !(rc.isPrimitive() || TigerUtilities.isBoxingType(rc))) {
      throw new ExecutionError("bitwise.expression.type", node);
    }

    // The left subexpression must be a variable
    if (!ln.hasProperty(NodeProperties.MODIFIER)) {
      throw new ExecutionError("left.expression", node);
    }

    // Sets the type property of this node
    node.setProperty(NodeProperties.TYPE, lc);
    return lc;
  }

  /**
   * Visits a shift expression.
   * This method is responsible for unboxing the two operands
   * as necessary, performing any numeric promotions needed,
   * and checking that the types of the two operands are compatible
   * with the shift operations.
   * @param node the shifting expression to be type checked
   * @return the type of the expression after any unboxing or numeric
   * promotion has been successfully executed.
   */
  private Class<?> visitShiftExpression(BinaryExpression node) {
    // Check the types
    Expression leftExp = node.getLeftExpression();
    Expression rightExp = node.getRightExpression();
    Class<?> lc = leftExp.acceptVisitor(this);
    Class<?> rc = rightExp.acceptVisitor(this);
    Class<?> c  = null;

    if (lc == null          || rc == null          ||
        lc == boolean.class || rc == boolean.class ||
        lc == void.class    || rc == void.class    ||
        lc == float.class   || rc == float.class   ||
        lc == Float.class   || rc == Float.class   ||
        lc == double.class  || rc == double.class  ||
        lc == Double.class  || rc == Double.class  ||
        !(lc.isPrimitive()  || TigerUtilities.isBoxingType(lc))  || 
        !(rc.isPrimitive()  || TigerUtilities.isBoxingType(rc)) ) {
      throw new ExecutionError("shift.expression.type", node);
    } 
    
    // Auto-unbox, if necessary
    if (TigerUtilities.isBoxingType(lc) && !leftExp.hasProperty(NodeProperties.MODIFIER)) {
      node.setLeftExpression(_unbox(leftExp, lc));
    }
    if (TigerUtilities.isBoxingType(rc)) {
      node.setRightExpression(_unbox(rightExp, rc));
    }
    
    if (lc == long.class || lc == Long.class) {
      node.setProperty(NodeProperties.TYPE, c = long.class);
    } 
    else {
      node.setProperty(NodeProperties.TYPE, c = int.class);
    }
    return c;
  }

  /**
   * Checks the typing rules in a cast expression
   * @param tc the target class
   * @param ec the expression class
   * @param castExp the entire cast expression that is being type-checked
   */
  private void checkCastStaticRules(Class<?> tc, Class<?> ec, CastExpression castExp) {
    if (tc != ec) {
      if (tc.isPrimitive()) {
        boolean isBoxingType = TigerUtilities.isBoxingType(ec);
        if (ec == null          || 
            ec == boolean.class || 
            (tc == boolean.class && ec != Boolean.class) ||
            !(ec.isPrimitive()  || isBoxingType) ||
            ec == void.class) {
          throw new ExecutionError("cast", castExp);
        }
        
        if (isBoxingType) {
          castExp.setExpression(_unbox(castExp.getExpression(), ec));
        }        
      } 
      else if (ec != null) {
        if (ec.isArray()) {
          if (tc.isArray()) {
            Class<?> tec = tc.getComponentType();
            Class<?> eec = ec.getComponentType();
            if (tec.isPrimitive() && eec.isPrimitive()) {
              if (tec != eec) {
                throw new ExecutionError("cast", castExp);
              }
            } 
            else {
              checkCastStaticRules(tec, eec, castExp);
            }
          } 
          else if (tc.isInterface() && tc != Cloneable.class) {
            throw new ExecutionError("cast", castExp);
          }
          else if (tc != Object.class) {
            throw new ExecutionError("cast", castExp);
          }
        }
        else if (ec.isInterface()) {
           if (tc.isArray()) {
             if (!Cloneable.class.isAssignableFrom(ec)) {
               throw new ExecutionError("cast", castExp);
             }
           }
          else if (!tc.isInterface()) { // ec is an interface, tc is not
            if (isFinal(tc) && !ec.isAssignableFrom(tc)) {
                throw new ExecutionError("cast", castExp);
            }
          }
        } // ec is not an interface type
        else if (tc.isInterface()) { // tc is an interface, ec is not
          if (isFinal(ec) && !tc.isAssignableFrom(ec)) {
              throw new ExecutionError("cast", castExp);
            }
          } // both ec and tc are classes or primitives
        else if (ec.isPrimitive() && TigerUtilities.isBoxingType(tc) && ec != boolean.class) {
          castExp.setExpression(_box(castExp.getExpression(), tc));
        }
        else if (!ec.isAssignableFrom(tc) && !tc.isAssignableFrom(ec)) {
          throw new ExecutionError("cast", castExp);
        }
      }
    }
  }

  /**
   * Tests if the class/interface c is final
   */
  private static boolean isFinal(Class<?> c) { 
    return Modifier.isFinal(c.getModifiers());
  }
  
  /**
   * Check a list of node by running this type checker
   * on each node in the list.
   * @param the list of nodes to type check
   */
  private void checkList(List<? extends Node> l) {
    ListIterator<? extends Node> it = l.listIterator();
    while (it.hasNext()) {
      it.next().acceptVisitor(this);
    }
  }

  
  /**
   * Returns the primitive type that corresponds to the given reference class.
   * @param refType the reference class
   * @return the corresponding primitive type
   */
  protected static PrimitiveTypeName _correspondingPrimType(Class<?> refClass) {
    if (refClass == Boolean.class) {
      return new BooleanTypeName();
    }
    else if (refClass == Byte.class) {
      return new ByteTypeName();
    }
    else if (refClass == Character.class) {
      return new CharTypeName();
    }
    else if (refClass == Short.class) {
      return new ShortTypeName();
    }
    else if (refClass == Integer.class) {
      return new IntTypeName();
    }
    else if (refClass == Long.class) {
      return new LongTypeName();
    }
    else if (refClass == Float.class) {
      return new FloatTypeName();
    }
    else if (refClass == Double.class) {
      return new DoubleTypeName();
    }
    else {
      throw new RuntimeException("No corresponding primitive type for reference class " + 
                                 refClass + ".");
    }
  }
  
  /**
   * Returns the reference class that corresponds to the given primitive class.
   * @param primClass the primtive class
   * @return the corresponding reference class
   */
  protected static Class<?> _correspondingRefClass(Class<?> primClass) {
    if (primClass == boolean.class) {
      return Boolean.class;
    }
    else if (primClass == byte.class) {
      return Byte.class;
    }
    else if (primClass == char.class) {
      return Character.class;
    }
    else if (primClass == short.class) {
      return Short.class;
    }
    else if (primClass == int.class) {
      return Integer.class;
    }
    else if (primClass == long.class) {
      return Long.class;
    }
    else if (primClass == float.class) {
      return Float.class;
    }
    else if (primClass == double.class) {
      return Double.class;
    }
    else {
      throw new RuntimeException("No corresponding reference class for primitive class (?) " + 
                                 primClass + ".");
    }
  }
  
  
  /**
   * If autoboxing is supported
   * Boxes the given expression by returning the correct
   * <code>SimpleAllocation</code> corresponding to the given
   * primitive type. Throws an exception if 
   * @param exp the expression to box
   * @param refType the reference type to box the primitive type to
   * @return the <code>SimpleAllocation</code> that boxes the expression
   */
  protected abstract SimpleAllocation _box(Expression exp, Class<?> refType);
  
  /**
   * If Unboxing is supported, unboxes the given expression by returning the correct
   * <code>ObjectMethodCall</code> corresponding to the given type
   * Throws an exception if unboxing is not supported
   * @param child The expression to unbox
   * @param type The type of the evaluated expression
   * @return The <code>ObjectMethodCall</code> that unboxes the expression
   */
  protected abstract ObjectMethodCall _unbox(Expression child, Class<?> type);
}




See more files for this project here

DrJava

DrJava is a lightweight programming environment for Java designed to foster test-driven software development. It includes an intelligent program editor, an interactions pane for evaluating program text, a source level debugger, and a unit testing tool.

Project homepage: http://sourceforge.net/projects/drjava
Programming language(s): Java
License: other

  context/
    AbstractVariable.java
    Context.java
    GlobalContext.java
    MethodContext.java
    MethodModificationError.java
    NoSuchFunctionException.java
    NoSuchKeyException.java
    SimpleContext.java
    StaticContext.java
    VariableContext.java
    VariableContextTest.java
    package.html
  error/
    BreakException.java
    CatchedExceptionError.java
    ContinueException.java
    ExecutionError.java
    PossibleExecutionError.java
    ReturnException.java
    ThrownException.java
    WrongVersionException.java
    package.html
  modifier/
    ArrayModifier.java
    FinalVariableModifier.java
    InvalidModifier.java
    LeftHandSideModifier.java
    ObjectFieldModifier.java
    StaticFieldModifier.java
    SuperFieldModifier.java
    VariableModifier.java
    package.html
  resources/
    messages.properties
  AbstractTypeChecker.java
  ClassFactory.java
  ClassInfoCompiler.java
  ClassLoaderContainer.java
  ClassPool.java
  DynamicjavaTest.java
  EvaluationVisitor.java
  EvaluationVisitorTest.java
  ForEachNamingTest.java
  ForEachTypingTest.java
  Interpreter.java
  InterpreterException.java
  InterpreterUtilities.java
  Main.java
  NameVisitor.java
  NodeProperties.java
  TreeClassFinder.java
  TreeClassLoader.java
  TreeCompiler.java
  TreeInterpreter.java
  Type.ast
  TypeChecker14.java
  TypeChecker15.java
  TypeCheckerTest.java
  TypeVariable.java
  UninitializedObject.java
  package.html