// Transmogrify License
//
// Copyright (c) 2001, ThoughtWorks, Inc.
// 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 name of the ThoughtWorks, Inc. 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.
package com.puppycrawl.tools.checkstyle.checks.usage.transmogrify;
import java.util.Vector;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogConfigurationException;
import org.apache.commons.logging.LogFactory;
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
/**
* The resolver is responsible for traversing all the various
* definitions in a symbol table and resolving references in them.
*
* @see SymbolTable
*/
public class Resolver extends DefinitionTraverser {
/** true if the log factory has been initialized */
private boolean mInitialized = false;
/** Factory for creating org.apache.commons.logging.Log instances */
private LogFactory mLogFactory;
/**
* constructor with <code>SymbolTable</code> to be resolved
*/
public Resolver(SymbolTable symbolTable) {
super(symbolTable);
try {
mLogFactory = LogFactory.getFactory();
}
catch (LogConfigurationException e) {
System.out.println("log configuration exception" + e);
}
mInitialized = true;
}
/**
* resolves the symbol table
* @return <code>void</code>
* @see #traverse()
*/
public void resolve() {
traverse();
}
protected void handleSList(SymTabAST node, Scope scope) {
SymTabASTIterator iterator = node.getChildren();
while (iterator.hasNext()) {
SymTabAST current = iterator.nextChild();
resolveExpression(current, scope, null, true);
}
}
protected void handleAnonymousInnerClass(AnonymousInnerClass innerClass) {
SymTabAST objblock = innerClass.getTreeNode();
SymTabAST expression = (SymTabAST) objblock.getFirstChild();
while (expression != null) {
resolveExpression(expression, innerClass, null, true);
expression = (SymTabAST) expression.getNextSibling();
}
}
/**
* processes a <code>ClassDef</code> and resolves references in it
*
* @param classDef the <code>ClassDef</code> to process
*/
protected void handleClass(ClassDef classDef) {
SymTabAST node = classDef.getTreeNode();
if (node != null) {
SymTabAST nameNode = node.findFirstToken(TokenTypes.IDENT);
nameNode.setDefinition(classDef, classDef, true);
SymTabAST extendsClause =
node.findFirstToken(TokenTypes.EXTENDS_CLAUSE);
SymTabAST extendedClassNode =
(SymTabAST) extendsClause.getFirstChild();
while (extendedClassNode != null) {
IClass superClass =
resolveClass(extendedClassNode, classDef, null, true);
extendedClassNode.setDefinition(superClass, classDef, true);
extendedClassNode =
(SymTabAST) extendedClassNode.getNextSibling();
}
SymTabAST implementsNode =
node.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE);
if (implementsNode != null) {
SymTabAST interfaceNode =
(SymTabAST) (implementsNode.getFirstChild());
while (interfaceNode != null) {
resolveClass(interfaceNode, classDef, null, true);
interfaceNode =
(SymTabAST) (interfaceNode.getNextSibling());
}
}
}
}
/**
* processes a <code>MethodDef</code> and resolves references in it
*
* @param method the <code>MethodDef</code> to process
*/
protected void handleMethod(MethodDef method) {
SymTabAST node = method.getTreeNode();
SymTabAST nameNode = node.findFirstToken(TokenTypes.IDENT);
nameNode.setDefinition(method, method, true);
// references to classes in return type
SymTabAST returnTypeNode = node.findFirstToken(TokenTypes.TYPE);
if (returnTypeNode != null) {
// this is not a constructor
resolveExpression(returnTypeNode, method, null, true);
}
SymTabAST throwsNode =
node.findFirstToken(TokenTypes.LITERAL_THROWS);
if (throwsNode != null) {
SymTabAST exception = (SymTabAST) throwsNode.getFirstChild();
while (exception != null) {
// handle Checkstyle grammar
if (exception.getType() != TokenTypes.COMMA) {
resolveClass(exception, method, null, true);
}
exception = (SymTabAST) exception.getNextSibling();
}
}
// references to classes in parameters
// the body -- this would be better its own function
SymTabAST slist = node.findFirstToken(TokenTypes.SLIST);
if (slist != null) {
handleSList(slist, method);
}
}
/**
* processes a <code>BlockDef</code> and resolves references in it
*
* @param block the <code>BlockDef</code> to process
*/
protected void handleBlock(BlockDef block) {
SymTabAST node = block.getTreeNode();
switch (node.getType()) {
case TokenTypes.LITERAL_FOR :
handleFor(block);
break;
case TokenTypes.LITERAL_IF :
handleIf(block);
break;
case TokenTypes.LITERAL_WHILE :
handleWhileAndSynchronized(block);
break;
case TokenTypes.LITERAL_DO :
handleDoWhile(block);
break;
case TokenTypes.LITERAL_TRY :
case TokenTypes.LITERAL_FINALLY :
SymTabAST slist = node.findFirstToken(TokenTypes.SLIST);
handleSList(slist, block);
break;
case TokenTypes.LITERAL_CATCH :
handleCatch(block);
break;
case TokenTypes.LITERAL_SWITCH :
handleSwitch(block);
break;
case TokenTypes.SLIST :
handleSList(node, block);
break;
case TokenTypes.EXPR :
resolveExpression(node, block, null, true);
break;
case TokenTypes.INSTANCE_INIT :
case TokenTypes.STATIC_INIT :
handleSList((SymTabAST) node.getFirstChild(), block);
break;
case TokenTypes.LITERAL_SYNCHRONIZED :
handleWhileAndSynchronized(block);
break;
case TokenTypes.LITERAL_ASSERT :
handleAssert(block);
break;
default :
if (mInitialized) {
final Log log = mLogFactory.getInstance(this.getClass());
log.error(
"Unhandled block "
+ block
+ " of type "
+ node.getType());
}
}
}
/**
* @param block
*/
private void handleAssert(BlockDef block) {
SymTabAST node = block.getTreeNode();
SymTabAST conditional =
(node.findFirstToken(TokenTypes.EXPR));
resolveExpression(conditional, block, null, true);
SymTabAST message = (SymTabAST) conditional.getNextSibling();
while ((message != null) && (message.getType() != TokenTypes.EXPR)) {
message = (SymTabAST) message.getNextSibling();
}
if (message != null) {
resolveExpression(message, block, null, true);
}
}
/**
* processes a switch statement and resolves references in it
*
* @param block the <code>BlockDef</code> to process
*/
private void handleSwitch(BlockDef block) {
SymTabAST node = block.getTreeNode();
SymTabAST expr = node.findFirstToken(TokenTypes.EXPR);
resolveExpression(expr, block, null, true);
SymTabAST caseGroup = (SymTabAST) (expr.getNextSibling());
while (caseGroup != null
&& (caseGroup.getType() != TokenTypes.CASE_GROUP)) {
caseGroup = (SymTabAST) caseGroup.getNextSibling();
}
if (caseGroup != null) {
while (caseGroup.getType() == TokenTypes.CASE_GROUP) {
SymTabAST caseNode =
caseGroup.findFirstToken(TokenTypes.LITERAL_CASE);
while (caseNode != null
&& caseNode.getType() == TokenTypes.LITERAL_CASE) {
resolveExpression(
(SymTabAST) caseNode.getFirstChild(),
block,
null,
true);
caseNode = (SymTabAST) caseNode.getNextSibling();
}
SymTabAST caseSlist =
caseGroup.findFirstToken(TokenTypes.SLIST);
handleSList(caseSlist, block);
caseGroup = (SymTabAST) (caseGroup.getNextSibling());
}
}
}
/**
* processes a catch block and resolves references in it
*
* @param block the <code>BlockDef</code> to process
*/
private void handleCatch(BlockDef block) {
SymTabAST node = block.getTreeNode();
SymTabAST slist = node.findFirstToken(TokenTypes.SLIST);
handleSList(slist, block);
}
/**
* processes a for loop and resolves references in it
*
* @param block the <code>BlockDef</code> to process
*/
private void handleFor(BlockDef block) {
SymTabAST node = block.getTreeNode();
SymTabAST body;
SymTabAST forEach = node.findFirstToken(TokenTypes.FOR_EACH_CLAUSE);
if (forEach == null) {
SymTabAST init = node.findFirstToken(TokenTypes.FOR_INIT);
// only need to handle the elist case. if the init node is a variable
// definition, the variable def will be handled later on in the resolution
if (init.getFirstChild() != null) {
if (init.getFirstChild().getType() == TokenTypes.ELIST) {
resolveExpression(
(SymTabAST) (init.getFirstChild()),
block,
null,
true);
}
}
SymTabAST cond = node.findFirstToken(TokenTypes.FOR_CONDITION);
if (cond.getFirstChild() != null) {
resolveExpression(
(SymTabAST) (cond.getFirstChild()),
block,
null,
true);
}
SymTabAST iterator = node.findFirstToken(TokenTypes.FOR_ITERATOR);
if (iterator.getFirstChild() != null) {
resolveExpression(
(SymTabAST) (iterator.getFirstChild()),
block,
null,
true);
}
body = (SymTabAST) (iterator.getNextSibling());
}
else {
resolveExpression(
(forEach.findFirstToken(TokenTypes.EXPR)),
block,
null,
true);
body = (SymTabAST) (forEach.getNextSibling());
}
//could be an SLIST, EXPR or an EMPTY_STAT
if (body.getType() == TokenTypes.RPAREN) {
body = (SymTabAST) body.getNextSibling();
}
if (body.getType() == TokenTypes.SLIST) {
handleSList(body, block);
}
else {
resolveExpression(body, block, null, true);
}
}
/**
* processes an if statement and resolves references in it
*
* @param block the <code>BlockDef</code> to process
*/
private void handleIf(BlockDef block) {
SymTabAST node = block.getTreeNode();
SymTabAST conditional =
(node.findFirstToken(TokenTypes.EXPR));
resolveExpression(conditional, block, null, true);
SymTabAST body = (SymTabAST) conditional.getNextSibling();
// Handle Checkstyle grammar
if (body.getType() == TokenTypes.RPAREN) {
body = (SymTabAST) body.getNextSibling();
}
if (body != null) {
if (body.getType() == TokenTypes.SLIST) {
handleSList(body, block);
}
else {
resolveExpression(body, block, null, true);
}
SymTabAST elseBody = (SymTabAST) body.getNextSibling();
//handle Checkstyle grammar
while ((elseBody != null)
&& (elseBody.getType() != TokenTypes.LITERAL_ELSE)) {
elseBody = (SymTabAST) elseBody.getNextSibling();
}
/*
if (elseBody != null && elseBody.getType() == TokenTypes.SLIST) {
handleSList(elseBody, block);
}else{
resolveExpression(elseBody, block, null, true);
}
*/
if (elseBody != null) {
elseBody = (SymTabAST) elseBody.getFirstChild();
}
if (elseBody != null) {
resolveExpression(elseBody, block.getParentScope(), null, true);
}
}
}
/**
* processes a while loop and resolves references in it
*
* @param block the <code>BlockDef</code> to process
*/
private void handleWhileAndSynchronized(BlockDef block) {
SymTabAST node = block.getTreeNode();
SymTabAST condition =
(node.findFirstToken(TokenTypes.EXPR));
SymTabAST slist = (SymTabAST) (condition.getNextSibling());
// handle Checkstyle grammar
if (slist.getType() == TokenTypes.RPAREN) {
slist = (SymTabAST) slist.getNextSibling();
}
resolveExpression(condition, block, null, true);
handleSList(slist, block);
}
private void handleDoWhile(BlockDef block) {
SymTabAST node = block.getTreeNode();
SymTabAST slist = (SymTabAST) node.getFirstChild();
SymTabAST condition =
node.findFirstToken(TokenTypes.EXPR);
handleSList(slist, block);
resolveExpression(condition, block, null, true);
}
/**
* processes a variable definition and resolves references in it
*
* @param variable the <code>VariableDef</code> to process
*/
protected void handleVariable(VariableDef variable) {
SymTabAST node = variable.getTreeNode();
Scope location = variable.getParentScope();
SymTabAST nameNode = node.findFirstToken(TokenTypes.IDENT);
nameNode.setDefinition(variable, location, true);
SymTabAST typeNode = node.findFirstToken(TokenTypes.TYPE);
resolveType(typeNode, location, null, true);
SymTabAST assignmentNode = node.findFirstToken(TokenTypes.ASSIGN);
if (assignmentNode != null) {
resolveExpression(
(SymTabAST) (assignmentNode.getFirstChild()),
variable.getParentScope(),
null,
true);
}
}
/**
* processes a label and resolves references in it
*
* @param label the <code>LabelDef</code> to process
*/
protected void handleLabel(LabelDef label) {
SymTabAST node = label.getTreeNode();
((SymTabAST) node.getFirstChild()).setDefinition(
label,
label.getParentScope(),
true);
}
/**
* Resolves Java expressions, returning the type to which the expression
* evalutes. If this is the reference creation phase, any references found during resolution are created and
* resolved.
*
* @param expression the <code>SymTabAST</code> representing the expression
* @param location the <code>Scope</code> in which the expression occours.
* @param context the <code>Scope</code> in which the search for the
* definition will start
* @param referencePhase whether or not this is the reference phase of
* table construction
*
* @return the <code>ClassDef</code> representing the type to which the
* expression evalutes.
*/
public IClass resolveExpression(
SymTabAST expression,
Scope location,
IClass context,
boolean referencePhase) {
IClass result = null;
try {
switch (expression.getType()) {
case TokenTypes.TYPECAST :
result =
resolveTypecast(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.EXPR :
case TokenTypes.LITERAL_RETURN :
if (expression.getFirstChild() != null) {
result =
resolveExpression(
(SymTabAST) expression.getFirstChild(),
location,
context,
referencePhase);
}
else {
// YOU WRITE BAD CODE!
}
break;
case TokenTypes.ELIST :
SymTabAST child = (SymTabAST) (expression.getFirstChild());
while (child != null) {
if (child.getType() != TokenTypes.COMMA) {
resolveExpression(
child,
location,
context,
referencePhase);
}
child = (SymTabAST) (child.getNextSibling());
}
break;
case TokenTypes.IDENT :
result =
resolveIdent(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.TYPE :
result =
resolveType(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.METHOD_CALL :
//case TokenTypes.SUPER_CTOR_CALL :
result =
resolveMethod(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.LITERAL_THIS :
result = resolveLiteralThis(expression, location, context);
break;
case TokenTypes.LITERAL_SUPER :
result = resolveLiteralSuper(expression, location, context);
break;
case TokenTypes.DOT :
result =
resolveDottedName(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.LITERAL_NEW :
case TokenTypes.CTOR_CALL :
case TokenTypes.SUPER_CTOR_CALL :
result =
resolveNew(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.LITERAL_BOOLEAN :
case TokenTypes.LITERAL_DOUBLE :
case TokenTypes.LITERAL_FLOAT :
case TokenTypes.LITERAL_LONG :
case TokenTypes.LITERAL_INT :
case TokenTypes.LITERAL_SHORT :
case TokenTypes.LITERAL_BYTE :
case TokenTypes.LITERAL_CHAR :
result =
resolvePrimitiveType(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.NUM_INT :
case TokenTypes.NUM_LONG :
result = resolveNumInt(expression, location, context);
break;
case TokenTypes.NUM_FLOAT :
case TokenTypes.NUM_DOUBLE :
result = resolveNumFloat(expression, location, context);
break;
case TokenTypes.STRING_LITERAL :
result =
resolveStringLiteral(expression, location, context);
break;
case TokenTypes.CHAR_LITERAL :
result = resolveCharLiteral(expression, location, context);
break;
case TokenTypes.ASSIGN :
case TokenTypes.PLUS_ASSIGN :
case TokenTypes.MINUS_ASSIGN :
case TokenTypes.STAR_ASSIGN :
case TokenTypes.DIV_ASSIGN :
case TokenTypes.MOD_ASSIGN :
case TokenTypes.SR_ASSIGN :
case TokenTypes.BSR_ASSIGN :
case TokenTypes.SL_ASSIGN :
case TokenTypes.BAND_ASSIGN :
case TokenTypes.BXOR_ASSIGN :
case TokenTypes.BOR_ASSIGN :
resolveAssignment(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.LOR :
case TokenTypes.LAND :
case TokenTypes.NOT_EQUAL :
case TokenTypes.EQUAL :
case TokenTypes.LT :
case TokenTypes.GT :
case TokenTypes.LE :
case TokenTypes.GE :
result =
resolveBooleanExpression(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.LITERAL_INSTANCEOF :
result =
resolveInstanceOf(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.LITERAL_TRUE :
case TokenTypes.LITERAL_FALSE :
result =
resolveBooleanLiteral(expression, location, context);
break;
case TokenTypes.LNOT :
result =
resolveBooleanUnary(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.INC :
case TokenTypes.POST_INC :
case TokenTypes.DEC :
case TokenTypes.POST_DEC :
case TokenTypes.UNARY_PLUS :
case TokenTypes.UNARY_MINUS :
result =
resolveUnaryExpression(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.PLUS :
case TokenTypes.MINUS :
case TokenTypes.DIV :
case TokenTypes.STAR :
case TokenTypes.BAND :
case TokenTypes.BOR :
case TokenTypes.BXOR :
case TokenTypes.MOD :
result =
resolveArithmeticExpression(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.LITERAL_BREAK :
case TokenTypes.LITERAL_CONTINUE :
resolveGoto(expression, location, context, referencePhase);
break;
case TokenTypes.LPAREN :
result = resolveExpression(
//TODO: child || sibling?
(SymTabAST) (expression.getNextSibling()),
//(SymTabAST) (expression.getFirstChild()),
location, context, referencePhase);
break;
case TokenTypes.INDEX_OP :
result =
resolveArrayAccess(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.LITERAL_NULL :
result = new NullClass();
break;
case TokenTypes.QUESTION :
result =
resolveQuestion(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.LITERAL_CLASS :
result = resolveLiteralClass();
break;
case TokenTypes.ARRAY_INIT :
resolveArrayInitializer(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.LITERAL_THROW :
resolveThrowExpression(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.SL :
case TokenTypes.SR :
case TokenTypes.BSR :
result =
resolveShiftOperator(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.BNOT :
resolveBitwiseNot(
expression,
location,
context,
referencePhase);
break;
case TokenTypes.LITERAL_ASSERT :
// resolveAssert(
// expression,
// location,
// context,
// referencePhase);
break;
case TokenTypes.RPAREN :
case TokenTypes.EMPTY_STAT :
// case TokenTypes.ML_COMMENT:
// case TokenTypes.SL_COMMENT:
case TokenTypes.VARIABLE_DEF :
case TokenTypes.METHOD_DEF :
case TokenTypes.CLASS_DEF :
case TokenTypes.LITERAL_FOR :
case TokenTypes.LITERAL_WHILE :
case TokenTypes.LITERAL_IF :
case TokenTypes.LITERAL_VOID :
// case TokenTypes.LITERAL_INTERFACE:
case TokenTypes.LITERAL_DO :
case TokenTypes.LITERAL_SWITCH :
case TokenTypes.LITERAL_STATIC :
case TokenTypes.LITERAL_TRANSIENT :
case TokenTypes.LITERAL_NATIVE :
// case TokenTypes.LITERAL_threadsafe:
case TokenTypes.LITERAL_SYNCHRONIZED :
case TokenTypes.LITERAL_VOLATILE :
case TokenTypes.LITERAL_TRY :
case TokenTypes.LITERAL_CATCH :
case TokenTypes.LITERAL_FINALLY :
case TokenTypes.LABELED_STAT :
case TokenTypes.LCURLY :
case TokenTypes.RCURLY :
case TokenTypes.SLIST :
case TokenTypes.SEMI :
case TokenTypes.COMMA :
case TokenTypes.ARRAY_DECLARATOR :
break;
default :
//TODO: throw exception
if (mInitialized) {
final Log log =
mLogFactory.getInstance(this.getClass());
log.error(
"Unhandled expression type: "
+ expression.getType());
}
break;
}
}
catch (Exception e) {
result = new UnknownClass(expression.getText(), expression);
// TODO: This really should be logged
// if (mInitialized) {
// final Log log = mLogFactory.getInstance(this.getClass());
// log.error("Error resolving near " + expression);
// }
}
return result;
}
private IClass resolveTypecast(
SymTabAST node,
Scope location,
IClass context,
boolean referencePhase) {
SymTabAST typeNode = (SymTabAST) node.getFirstChild();
SymTabAST exprNode = (SymTabAST) typeNode.getNextSibling();
//handle Checkstyle grammar
if (exprNode.getType() == TokenTypes.RPAREN) {
exprNode = (SymTabAST) exprNode.getNextSibling();
}
IClass type = null;
final SymTabAST child = (SymTabAST) typeNode.getFirstChild();
// TODO: Checkstyle change.
// Do not create references from typecast.
// Original transmogrify code is equivalent to
// final boolean createReference = referencePhase;
// which creates non-existant references for variables.
final boolean createReference = false;
if (child.getType()
== TokenTypes.ARRAY_DECLARATOR) {
type =
new ArrayDef(
resolveType(
(SymTabAST) typeNode.getFirstChild(),
location,
context,
createReference));
}
else {
type = resolveType(typeNode, location, context, createReference);
}
resolveExpression(exprNode, location, context, referencePhase);
//TODO: Checkstyle change. Can this be ignored?
if (type != null) {
((SymTabAST) typeNode.getFirstChild()).setDefinition(
type,
location,
referencePhase);
}
return type;
}
private IClass resolveArrayAccess(
SymTabAST node,
Scope location,
IClass context,
boolean referencePhase) {
SymTabAST arrayNode = (SymTabAST) (node.getFirstChild());
SymTabAST exprNode = (SymTabAST) (arrayNode.getNextSibling());
//resolve index expressions
while (arrayNode.getType() == TokenTypes.INDEX_OP) {
resolveExpression(exprNode, location, context, referencePhase);
arrayNode = (SymTabAST) (arrayNode.getFirstChild());
exprNode = (SymTabAST) (arrayNode.getNextSibling());
}
ArrayDef array =
(ArrayDef) resolveExpression(arrayNode,
location,
context,
referencePhase);
resolveExpression(exprNode, location, context, referencePhase);
return array.getType();
}
private IClass resolveLiteralClass() {
return new ExternalClass(Class.class);
}
/**
* Resolves any dotted reference, returning the <code>Scope</code>
* identified by the reference.
*
* @param tree the root node of the dotted reference
* @param location the <code>Scope</code> in which the expression occours.
* @param context the <code>Scope</code> in which the search for the
* definition will start
* @return the <code>Scope</code> indentified by the reference
*/
private IClass resolveDottedName(
SymTabAST tree,
Scope location,
IClass context,
boolean referencePhase) {
IClass result = null;
IClass localContext = context;
String name = null;
DotIterator it = new DotIterator(tree);
while (it.hasNext()) {
SymTabAST node = it.nextNode();
if (node.getType() != TokenTypes.COMMA) {
localContext =
resolveExpression(
node,
location,
localContext,
referencePhase);
if (localContext == null) {
node.setMeaningfulness(false);
name = node.getText();
while (localContext == null && it.hasNext()) {
SymTabAST next = it.nextNode();
name = name + "." + next.getText();
localContext = location.getClassDefinition(name);
if (localContext != null && referencePhase) {
next.setDefinition(
localContext,
location,
referencePhase);
}
else {
next.setMeaningfulness(false);
}
}
}
}
}
if (localContext != null) {
result = localContext;
}
else {
result = new UnknownClass(name, tree);
}
return result;
}
/**
* Resolves a method call.
*
* @param methodNode the <code>SymTabAST</code> for the METHOD_CALL node
* @param location the <code>Scope</code> where the expression occurs
* @param context the <code>Scope</code> in which the expression occurs
* (where the search for a defintion begins)
* @param referencePhase whether or not this is the reference phase of
* table construction
*
* @return the <code>ClassDef</code> for the type returned by the method
*/
private IClass resolveMethod(
SymTabAST methodNode,
Scope location,
IClass context,
boolean referencePhase) {
IClass result = new UnknownClass(methodNode.getText(), methodNode);
IClass newContext = null;
if (context == null) {
newContext = location.getEnclosingClass();
}
else {
newContext = context;
}
String name = null;
boolean createReference = true;
SymTabAST nameNode = (SymTabAST) (methodNode.getFirstChild());
SymTabAST parametersNode = (SymTabAST) (nameNode.getNextSibling());
ISignature signature =
resolveParameters(
parametersNode,
location,
context,
referencePhase);
if (nameNode.getType() == TokenTypes.IDENT) {
name = nameNode.getText();
}
else if (
nameNode.getType() == TokenTypes.LITERAL_SUPER
|| (nameNode.getType() == TokenTypes.SUPER_CTOR_CALL)) {
IClass superclass = location.getEnclosingClass().getSuperclass();
newContext = superclass;
name = superclass.getName();
createReference = false;
}
else if (nameNode.getType() == TokenTypes.LITERAL_THIS) {
newContext = location.getEnclosingClass();
name = newContext.getName();
createReference = false;
}
else {
// REDTAG -- doing dotted name resolution on its own
SymTabAST contextNode = (SymTabAST) (nameNode.getFirstChild());
//TODO: handle Checkstyle grammar
nameNode = (SymTabAST) contextNode.getNextSibling();
//skip to IDENT
while (nameNode.getType() != TokenTypes.IDENT) {
nameNode = (SymTabAST) nameNode.getNextSibling();
}
name = nameNode.getText();
newContext =
resolveExpression(
contextNode,
location,
context,
referencePhase);
}
if (newContext != null) {
IMethod method = newContext.getMethodDefinition(name, signature);
if (method != null) {
if (createReference && referencePhase) {
nameNode.setDefinition(method, location, referencePhase);
}
result = method.getType();
}
}
if (result == null) {
result = new UnknownClass(methodNode.getText(), methodNode);
}
return result;
}
/**
* resolves a literal "this"
*
* @param expression the <code>SymTabAST</code> of the expression
* @param location the <code>Scope</code> where the expression occurs
* @param context the <code>Scope</code> in which the expression occurs
* (where the search for a defintion begins)
*
* @return the resulting scope of the expression (the type to which it evaluates)
*/
private IClass resolveLiteralThis(
SymTabAST thisNode,
Scope location,
IClass context) {
return location.getEnclosingClass();
}
/**
* resolves a literal "super"
*
* @param expression the <code>SymTabAST</code> of the expression
* @param location the <code>Scope</code> where the expression occurs
* @param context the <code>Scope</code> in which the expression occurs
* (where the search for a defintion begins)
*
* @return the resulting scope of the expression (the type to which it evaluates)
*/
private IClass resolveLiteralSuper(
SymTabAST superNode,
Scope location,
IClass context) {
return location.getEnclosingClass().getSuperclass();
}
private boolean newIsConstructor(SymTabAST newNode) {
boolean result = false;
SymTabAST typeNode =
(SymTabAST) (newNode.getFirstChild().getNextSibling());
//handle Checkstyle grammar
if (typeNode.getType() == TokenTypes.LPAREN) {
typeNode = (SymTabAST) typeNode.getNextSibling();
}
if (typeNode.getType() == TokenTypes.ELIST) {
result = true;
}
return result;
}
/**
* resolves and expression of type TokenTypes.TYPE
*
* @param expression the <code>SymTabAST</code> of the expression
* @param location the <code>Scope</code> where the expression occurs
* @param context the <code>Scope</code> in which the expression occurs
* (where the search for a defintion begins)
* @param referencePhase whether or not this is the reference phase of
* table construction
* @return the resulting scope of the expression (the type to which it evaluates)
* @see #resolveDottedName(SymTabAST, Scope, IClass, boolean)
* @see #resolveClassIdent(SymTabAST, Scope, IClass, boolean)
*/
public IClass resolveType(
SymTabAST expr,
Scope location,
IClass context,
boolean referencePhase) {
IClass result = null;
SymTabAST nameNode = (SymTabAST) expr.getFirstChild();
// TODO: Checkstyle change.
// Do not create references from typecast.
// Original transmogrify code is equivalent to
// final boolean createReference = referencePhase;
// which creates non-existant references for variables.
final boolean createReference = false;
if (nameNode.getType() == TokenTypes.DOT) {
result =
resolveDottedName(nameNode, location, context, createReference);
}
else {
result =
resolveClassIdent(nameNode, location, context, createReference);
}
return result;
}
/**
* resolves Class type expression
* @param expr node to be resolved
* @param location scope of the <code>expr</code>
* @param context context of the <code>expr</code> if exists
* @param referencePhase <code>true</code> if this method is used to during
* finding reference phase
* <code>false</code> otherwise
* @return <code>IClass</code> representing the type to which the
* expression evalutes.
* @see #resolveDottedName(SymTabAST, Scope, IClass, boolean)
*/
public IClass resolveClass(
SymTabAST expr,
Scope location,
IClass context,
boolean referencePhase) {
IClass result =
resolveDottedName(expr, location, context, referencePhase);
if (result != null && referencePhase) {
expr.setDefinition(result, location, referencePhase);
}
return result;
}
/**
* resolves expression with <code>JavaTokenTypes<code> other than <code>DOT</code>
* @param expr expression to be resolved
* @param location scope of the expression
* @param context context of the expression if any
* @param referencePhase <code>true</code> if this method is used to during
* finding reference phase
* <code>false</code> otherwise
* @return <code>IClass</code> representing the type to which the
* expression evalutes.
*/
public IClass resolveClassIdent(
SymTabAST expr,
Scope location,
IClass context,
boolean referencePhase) {
IClass result = location.getClassDefinition(expr.getText());
if (result != null) {
expr.setDefinition(result, location, referencePhase);
}
return result;
}
private IClass resolveNew(
SymTabAST newNode,
Scope location,
IClass context,
boolean referencePhase) {
IClass result;
if (newIsConstructor(newNode)) {
result =
resolveConstructor(newNode, location, context, referencePhase);
}
else {
result =
resolveNewArray(newNode, location, context, referencePhase);
}
return result;
}
private IClass resolveNewArray(
SymTabAST newNode,
Scope location,
IClass context,
boolean referencePhase) {
IClass arrayType;
SymTabAST typeNode = (SymTabAST) (newNode.getFirstChild());
SymTabAST declaratorNode = (SymTabAST) (typeNode.getNextSibling());
SymTabAST initializerNode =
(SymTabAST) (declaratorNode.getNextSibling());
arrayType = resolveClass(typeNode, location, context, referencePhase);
if (declaratorNode.getFirstChild() != null) {
resolveExpression(
((SymTabAST) declaratorNode.getFirstChild()),
location,
context,
referencePhase);
}
if (initializerNode != null) {
resolveArrayInitializer(
initializerNode,
location,
context,
referencePhase);
}
return new ArrayDef(arrayType);
}
private IClass resolveQuestion(
SymTabAST question,
Scope location,
IClass context,
boolean referencePhase) {
SymTabAST test = (SymTabAST) question.getFirstChild();
while (test.getType() == TokenTypes.LPAREN) {
test = (SymTabAST) test.getNextSibling();
}
SymTabAST leftBranch = (SymTabAST) test.getNextSibling();
while (leftBranch.getType() == TokenTypes.RPAREN) {
leftBranch = (SymTabAST) leftBranch.getNextSibling();
}
SymTabAST rightBranch = (SymTabAST) leftBranch.getNextSibling();
while (rightBranch.getType() != TokenTypes.COLON) {
rightBranch = (SymTabAST) rightBranch.getNextSibling();
}
rightBranch = (SymTabAST) rightBranch.getNextSibling();
resolveExpression(test, location, context, referencePhase);
IClass leftClass =
resolveExpression(leftBranch, location, context, referencePhase);
IClass rightClass =
resolveExpression(rightBranch, location, context, referencePhase);
return moreGeneral(leftClass, rightClass);
}
private IClass moreGeneral(IClass a, IClass b) {
return (a.isCompatibleWith(b)) ? b : a;
}
/**
* Resolves a constructor call.
*
* @param tree the root node of the constructor call
* @return the <code>ClassDef</code> for the class instantiated by the
* constructor
*/
private IClass resolveConstructor(
SymTabAST constructor,
Scope location,
IClass context,
boolean referencePhase) {
IClass classConstructed = null;
SymTabAST nameNode = (SymTabAST) (constructor.getFirstChild());
//SymTabAST parametersNode = (SymTabAST) (nameNode.getNextSibling());
SymTabAST parametersNode =
constructor.findFirstToken(TokenTypes.ELIST);
SymTabAST nameIdent = null;
if (nameNode.getType() == TokenTypes.IDENT) {
nameIdent = nameNode;
}
else {
nameIdent = (SymTabAST) nameNode.getFirstChild().getNextSibling();
}
classConstructed = resolveClass(nameNode, location, context, false);
if (classConstructed != null) {
MethodSignature signature =
resolveParameters(
parametersNode,
location,
context,
referencePhase);
IMethod constructorDef =
classConstructed.getMethodDefinition(
nameIdent.getText(),
signature);
if (constructorDef != null && referencePhase) {
nameIdent.setDefinition(
constructorDef,
location,
referencePhase);
}
}
return classConstructed;
}
/**
* Resolves the types found in a method call. Any references found
* in the process are created. Returns a <code>MethodSignature</code> for
* the types of the parameters.
*
* @param elist The <code>SymTabAST</code> for the list of parameters
* @return the signature of the parameters
*/
private MethodSignature resolveParameters(
SymTabAST elist,
Scope location,
IClass context,
boolean referencePhase) {
Vector parameters = new Vector();
SymTabAST expr = (SymTabAST) (elist.getFirstChild());
while (expr != null) {
if (expr.getType() != TokenTypes.COMMA) {
IClass parameter =
resolveExpression((SymTabAST) (expr
.getFirstChild()),
location,
context,
referencePhase);
parameters.add(parameter);
}
expr = (SymTabAST) (expr.getNextSibling());
}
return new MethodSignature(parameters);
}
/**
* Resolves an IDENT node of an AST, creating the appropriate reference and
* returning the scope of the identifer.
*
* @param ident the IDENT node
* @param location the <code>Scope</code> in which the IDENT is found
* @return the <code>Scope</code> the identifier identifies
*/
private IClass resolveIdent(
SymTabAST ident,
Scope location,
IClass context,
boolean referencePhase) {
IClass result = null;
IDefinition def = null;
String name = ident.getText();
// look for var
if (context != null) {
def = context.getVariableDefinition(name);
}
else {
def = location.getVariableDefinition(name);
}
if (def != null) {
result = ((IVariable) def).getType();
}
else {
// look for class
if (context != null) {
result = context.getClassDefinition(name);
}
else {
result = location.getClassDefinition(name);
}
def = result;
}
if (def != null) {
ident.setDefinition(def, location, referencePhase);
}
return result;
}
/**
* Resolves a (binary) boolean expression. The left and right sides of the
* expression
* are resolved in the process.
*
* @param expression the <code>SymTabAST</code> representing the boolean
* expression.
* @return the <code>Scope</code> for the boolean primitive type.
*/
private IClass resolveBooleanExpression(
SymTabAST expression,
Scope location,
IClass context,
boolean referencePhase) {
IClass result = null;
SymTabAST leftChild = findLeftChild(expression);
resolveExpression(leftChild, location, context, referencePhase);
SymTabAST rightChild = findRightSibling(leftChild);
resolveExpression(rightChild, location, context, referencePhase);
result = LiteralResolver.getDefinition(TokenTypes.LITERAL_BOOLEAN);
return result;
}
/**
* resolves references in an assignment expression
*
* @param expression the <code>SymTabAST</code> of the expression
* @param location the <code>Scope</code> where the expression occurs
* @param context the <code>Scope</code> in which the expression occurs
* (where the search for a defintion begins)
*
* @return the resulting scope of the expression (the type to which it evaluates)
*/
private IClass resolveAssignment(
SymTabAST expression,
Scope location,
IClass context,
boolean referencePhase) {
IClass result = null;
SymTabAST leftNode = (SymTabAST) (expression.getFirstChild());
SymTabAST rightNode = (SymTabAST) (leftNode.getNextSibling());
result = resolveExpression(leftNode, location, context, referencePhase);
resolveExpression(rightNode, location, context, referencePhase);
return result;
}
/**
* Resolves a unary expression. Returns the type of the expression,
* creating any references found along the way. Unary expressions are
* increment (x++), decrement (x--), unary plus (+x), and unary minus (-x)
*
* @param expression the <code>SymTabAST</code> of the unary expression.
* @return the <code>Scope</code> for the type to which the expression
* evalutes.
*/
private IClass resolveUnaryExpression(
SymTabAST expression,
Scope location,
IClass context,
boolean referencePhase) {
SymTabAST operatee = (SymTabAST) (expression.getFirstChild());
return resolveExpression(operatee, location, context, referencePhase);
}
/**
* Resolves an arithmetic expression. Returns the <code>Scope</code> for
* the type to which the expression resolves. Any references found during
* resolution are created and resolved.
*
* @param expression the <code>SymTabAST</code> representing the arithmetic
* expression.
*
* @return the <code>Scope</code> for the type to which the expression
* evaluates.
*/
private IClass resolveArithmeticExpression(
SymTabAST expression,
Scope location,
IClass context,
boolean referencePhase) {
IClass result = null;
SymTabAST leftChild = findLeftChild(expression);
IClass leftType =
(resolveExpression(leftChild,
location,
context,
referencePhase));
SymTabAST rightChild = findRightSibling(leftChild);
IClass rightType =
(resolveExpression(rightChild,
location,
context,
referencePhase));
result = binaryResultType(leftType, rightType);
return result;
}
/**
* Finds the left child of a binary operator, skipping parentheses.
* @param aExpression the node for the binary operator.
* @return the node for the left child.
*/
private SymTabAST findLeftChild(SymTabAST aExpression) {
SymTabAST leftChild = (SymTabAST) (aExpression.getFirstChild());
// handle Checkstyle grammar
while (leftChild.getType() == TokenTypes.LPAREN) {
leftChild = (SymTabAST) leftChild.getNextSibling();
}
return leftChild;
}
/**
* Finds the right sibling of the left child of a binary operator,
* skipping parentheses.
* @param aLeftChild the left child of a binary operator.
* @return the node of the right sibling.
*/
private SymTabAST findRightSibling(SymTabAST aLeftChild) {
SymTabAST rightChild = (SymTabAST) (aLeftChild.getNextSibling());
// handle Checkstyle grammar
while ((rightChild != null)
&& (rightChild.getType() == TokenTypes.RPAREN))
{
rightChild = (SymTabAST) rightChild.getNextSibling();
}
return rightChild;
}
/**
* Returns the <code>ClassDef</code> for the type to which arithmetic
* expressions evaluate.
*
* @param a the <code>ClassDef</code> of the first operand.
* @param b the <code>ClassDef</code> of the second operand.
*
* @return the <code>ClassDef</code> to which the expression evaluates.
*/
private IClass binaryResultType(IClass a, IClass b) {
IClass result = null;
// These may or may not be in line with the rules set forth in the java
// language specification. Not being in line would be a BadThing(r).
IClass string = new ExternalClass(java.lang.String.class);
if (a.equals(string) || b.equals(string)) {
result = string;
}
else if (a.equals(PrimitiveClasses.BOOLEAN)) {
result = PrimitiveClasses.BOOLEAN;
}
else {
result =
PrimitiveClasses.binaryPromote(
a,
b);
}
return result;
}
/**
* resolves references in an instanceof expression
*
* @param expression the <code>SymTabAST</code> of the expression
* @param location the <code>Scope</code> where the expression occurs
* @param context the <code>Scope</code> in which the expression occurs
* (where the search for a defintion begins)
*
* @return the resulting scope of the expression (the type to which it evaluates)
*/
private IClass resolveInstanceOf(
SymTabAST expression,
Scope location,
IClass context,
boolean referencePhase) {
SymTabAST leftNode = (SymTabAST) (expression.getFirstChild());
SymTabAST rightNode = (SymTabAST) (leftNode.getNextSibling());
resolveExpression(leftNode, location, context, referencePhase);
SymTabAST classNameNode = (SymTabAST) (rightNode.getFirstChild());
resolveClass(classNameNode, location, context, referencePhase);
return LiteralResolver.getDefinition(TokenTypes.LITERAL_BOOLEAN);
}
/**
* resolves references in a a break statement
*
* @param expression the <code>SymTabAST</code> for the expression
* @param location the <code>Scope</code> where the expression occurs
* @param context the <code>Scope</code> in which the expression occurs
* (where the search for a defintion begins)
*
* @return the <code>Scope</code> for the int primitive type
*/
private IClass resolveGoto(
SymTabAST expression,
Scope location,
IClass context,
boolean referencePhase) {
SymTabAST label = (SymTabAST) (expression.getFirstChild());
// handle Checkstyle grammar
if (label != null && (label.getType() != TokenTypes.SEMI)) {
LabelDef def = location.getLabelDefinition(label.getText());
if (def != null) {
label.setDefinition(def, location, referencePhase);
}
}
return null;
}
private IClass resolvePrimitiveType(
SymTabAST primitive,
Scope location,
IClass context,
boolean referencePhase) {
IClass result =
LiteralResolver.getDefinition(primitive.getType());
primitive.setDefinition(result, location, referencePhase);
return result;
}
/**
* Returns the <code>ClassDef</code> of the int primitive type. This may
* need to be amended, based on the Java Language spec, to return a long
* if the literal is larger than an int can hold.
*
* @param expression the <code>SymTabAST</code> for the integer literal
* @param location the <code>Scope</code> where the expression occurs
* @param context the <code>Scope</code> in which the expression occurs
* (where the search for a defintion begins)
*
* @return the <code>Scope</code> for the int primitive type
*/
private IClass resolveNumInt(
SymTabAST expression,
Scope location,
IClass context) {
return PrimitiveClasses.INT;
}
/**
* Returns the <code>ClassDef</code> type of the float primitive type.
* This may need to be amended, based on the Java Language spec, to return
* a double if the literal is larger than a float can hold.
*
* @param expression the <code>SymTabAST</code> for the floating point
literal
* @param location the <code>Scope</code> where the expression occurs
* @param context the <code>Scope</code> in which the expression occurs
* (where the search for a defintion begins)
*
* @return the <code>Scope</code> for the float primitive type
*/
private IClass resolveNumFloat(
SymTabAST expression,
Scope location,
IClass context) {
return PrimitiveClasses.DOUBLE;
}
/**
* Returns the <code>ClassDef</code> type of a string literal
*
* @param expression the <code>SymTabAST</code> for a string literal
* @param location the <code>Scope</code> where the expression occurs
* @param context the <code>Scope</code> in which the expression occurs
* (where the search for a defintion begins)
*
* @return the <code>Scope</code> type of a string literal
*/
private IClass resolveStringLiteral(
SymTabAST expression,
Scope location,
IClass context) {
return LiteralResolver.getDefinition(
TokenTypes.STRING_LITERAL);
}
/**
* Returns the <code>ClassDef</code> type of a character literal
*
* @param expression the <code>SymTabAST</code> for a string literal
* @param location the <code>Scope</code> where the expression occurs
* @param context the <code>Scope</code> in which the expression occurs
* (where the search for a defintion begins)
*
* @return the <code>Scope</code> type of a character literal
*/
private IClass resolveCharLiteral(
SymTabAST expression,
Scope location,
IClass context) {
return LiteralResolver.getDefinition(
TokenTypes.LITERAL_CHAR);
}
/**
* Describe <code>resolveBooleanLiteral</code> method here.
*
* @param expression the <code>SymTabAST</code> of the expression
* @param location the <code>Scope</code> where the expression occurs
* @param context the <code>Scope</code> in which the expression occurs
* (where the search for a defintion begins)
*
* @return the <code>Scope</code> for the boolean primitive.
*/
private IClass resolveBooleanLiteral(
SymTabAST expression,
Scope location,
IClass context) {
return LiteralResolver.getDefinition(TokenTypes.LITERAL_BOOLEAN);
}
private IClass resolveBooleanUnary(
SymTabAST expression,
Scope location,
IClass context,
boolean referencePhase) {
SymTabAST child = (SymTabAST) expression.getFirstChild();
resolveExpression(child, location, context, referencePhase);
return LiteralResolver.getDefinition(TokenTypes.LITERAL_BOOLEAN);
}
/**
* Resolves a constructor call.
*
* @param tree the root node of the constructor call
* @return the <code>ClassDef</code> for the class instantiated by the
* constructor
*/
private void resolveArrayInitializer(
SymTabAST initializerNode,
Scope location,
IClass context,
boolean referencePhase) {
SymTabAST child = (SymTabAST) (initializerNode.getFirstChild());
while (child != null) {
if (child.getType() != TokenTypes.COMMA) {
resolveExpression(child, location, context, referencePhase);
}
child = (SymTabAST) (child.getNextSibling());
}
}
/**
* Resolves a constructor call.
*
* @param tree the root node of the constructor call
* @return the <code>ClassDef</code> for the class instantiated by the
* constructor
*/
private void resolveThrowExpression(
SymTabAST throwNode,
Scope location,
IClass context,
boolean referencePhase) {
SymTabAST nameNode = (SymTabAST) (throwNode.getFirstChild());
resolveExpression(nameNode, location, context, referencePhase);
}
private IClass resolveShiftOperator(
SymTabAST expression,
Scope location,
IClass context,
boolean referencePhase) {
IClass result = null;
SymTabAST leftChild = findLeftChild(expression);
SymTabAST rightChild = findRightSibling(leftChild);
result =
resolveExpression(leftChild, location, context, referencePhase);
resolveExpression(rightChild, location, context, referencePhase);
result = PrimitiveClasses.unaryPromote(result);
return result;
}
private IClass resolveBitwiseNot(
SymTabAST expression,
Scope location,
IClass context,
boolean referencePhase) {
IClass result = null;
SymTabAST child = (SymTabAST) expression.getFirstChild();
result = resolveExpression(child, location, context, referencePhase);
result = PrimitiveClasses.unaryPromote(result);
return result;
}
}