Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* SonarQube Java
* Copyright (C) 2012 SonarSource
* [email protected]
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.java.resolve;
import com.google.common.annotations.VisibleForTesting;
import java.util.List;
/**
* Routines for name resolution.
*
* Lookup by name and then filter by type is performant, because amount of symbols with same name are relatively small.
*
* Naming conventions:
* env - is the environment where the symbol was mentioned
* site - is the type of which symbol is a member
* name - is the symbol's name
*
* TODO site should be represented by class Type
*/
public class Resolve {
private final SymbolNotFound symbolNotFound = new SymbolNotFound();
private final Types types = new Types();
private final Symbols symbols;
public Resolve(Symbols symbols) {
this.symbols = symbols;
}
static class Env {
/**
* The next enclosing environment.
*/
Env next;
/**
* The environment enclosing the current class.
*/
Env outer;
Symbol.PackageSymbol packge;
Symbol.TypeSymbol enclosingClass;
Scope scope;
Env outer() {
return outer;
}
Symbol.TypeSymbol enclosingClass() {
return enclosingClass;
}
public Symbol.PackageSymbol packge() {
return packge;
}
Scope scope() {
return scope;
}
public Env dup() {
Env env = new Env();
env.next = this;
env.outer = this.outer;
env.packge = this.packge;
env.enclosingClass = this.enclosingClass;
env.scope = this.scope;
return env;
}
}
/**
* Finds field with given name.
*/
private Symbol findField(Env env, Symbol.TypeSymbol site, String name, Symbol.TypeSymbol c) {
Symbol bestSoFar = symbolNotFound;
for (Symbol symbol : c.members().lookup(name)) {
if (symbol.kind == Symbol.VAR) {
return isAccessible(env, site, symbol)
? symbol
: new AccessErrorSymbol(symbol);
}
}
Symbol symbol;
if (c.getSuperclass() != null) {
symbol = findField(env, site, name, c.getSuperclass().symbol);
if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
for (Type interfaceType : c.getInterfaces()) {
symbol = findField(env, site, name, interfaceType.symbol);
if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
return bestSoFar;
}
/**
* Finds variable or field with given name.
*/
private Symbol findVar(Env env, String name) {
Symbol bestSoFar = symbolNotFound;
Env env1 = env;
while (env1.outer() != null) {
Symbol sym = null;
for (Symbol symbol : env1.scope().lookup(name)) {
if (symbol.kind == Symbol.VAR) {
sym = symbol;
}
}
if (sym == null) {
sym = findField(env1, env1.enclosingClass(), name, env1.enclosingClass());
}
if (sym.kind < Symbol.ERRONEOUS) {
// symbol exists
return sym;
} else if (sym.kind < bestSoFar.kind) {
bestSoFar = sym;
}
env1 = env1.outer();
}
// TODO imports
return bestSoFar;
}
public Symbol findMemberType(Env env, Symbol.TypeSymbol site, String name, Symbol.TypeSymbol c) {
Symbol bestSoFar = symbolNotFound;
for (Symbol symbol : c.members().lookup(name)) {
if (symbol.kind == Symbol.TYP) {
return isAccessible(env, site, symbol)
? symbol
: new AccessErrorSymbol(symbol);
}
}
if (c.getSuperclass() != null) {
Symbol symbol = findMemberType(env, site, name, c.getSuperclass().symbol);
if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
for (Type interfaceType : c.getInterfaces()) {
Symbol symbol = findMemberType(env, site, name, interfaceType.symbol);
if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
return bestSoFar;
}
/**
* Finds type with given name.
*/
private Symbol findType(Env env, String name) {
Symbol bestSoFar = symbolNotFound;
for (Env env1 = env; env1.outer() != null; env1 = env1.outer()) {
for (Symbol symbol : env1.scope().lookup(name)) {
if (symbol.kind == Symbol.TYP) {
return symbol;
}
}
Symbol symbol = findMemberType(env1, env1.enclosingClass(), name, env1.enclosingClass());
if (symbol.kind < Symbol.ERRONEOUS) {
// symbol exists
return symbol;
} else if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
//checks predefined types
Symbol predefinedSymbol = findField(env, symbols.predefClass, name, symbols.predefClass);
if (predefinedSymbol.kind < bestSoFar.kind) {
return predefinedSymbol;
}
// TODO imports
for (Symbol symbol : env.packge().members.lookup(name)) {
if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
return bestSoFar;
}
/**
* @param kind subset of {@link Symbol#VAR}, {@link Symbol#TYP}, {@link Symbol#PCK}
*/
public Symbol findIdent(Env env, String name, int kind) {
Symbol bestSoFar = symbolNotFound;
Symbol symbol;
if ((kind & Symbol.VAR) != 0) {
symbol = findVar(env, name);
if (symbol.kind < Symbol.ERRONEOUS) {
// symbol exists
return symbol;
} else if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
if ((kind & Symbol.TYP) != 0) {
symbol = findType(env, name);
if (symbol.kind < Symbol.ERRONEOUS) {
// symbol exists
return symbol;
} else if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
if ((kind & Symbol.PCK) != 0) {
// TODO read package
}
return bestSoFar;
}
/**
* @param kind subset of {@link Symbol#TYP}, {@link Symbol#PCK}
*/
public Symbol findIdentInPackage(Env env, Symbol site, String name, int kind) {
// TODO implement me
return symbolNotFound;
}
/**
* @param kind subset of {@link Symbol#VAR}, {@link Symbol#TYP}
*/
public Symbol findIdentInType(Env env, Symbol.TypeSymbol site, String name, int kind) {
Symbol bestSoFar = symbolNotFound;
Symbol symbol;
if ((kind & Symbol.VAR) != 0) {
symbol = findField(env, site, name, site);
if (symbol.kind < Symbol.ERRONEOUS) {
// symbol exists
return symbol;
} else if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
if ((kind & Symbol.TYP) != 0) {
symbol = findMemberType(env, site, name, site);
if (symbol.kind < Symbol.ERRONEOUS) {
// symbol exists
return symbol;
} else if (symbol.kind < bestSoFar.kind) {
bestSoFar = symbol;
}
}
return bestSoFar;
}
/**
* Finds method matching given name and types of arguments.
*/
public Symbol findMethod(Env env, String name, List argTypes) {
Symbol bestSoFar = symbolNotFound;
Env env1 = env;
while (env1.outer() != null) {
Symbol sym = findMethod(env1, env1.enclosingClass(), name, argTypes);
if (sym.kind < Symbol.ERRONEOUS) {
// symbol exists
return sym;
} else if (sym.kind < bestSoFar.kind) {
bestSoFar = sym;
}
env1 = env1.outer;
}
// TODO imports
return bestSoFar;
}
public Symbol findMethod(Env env, Symbol.TypeSymbol site, String name, List argTypes) {
Symbol bestSoFar = symbolNotFound;
// TODO search in supertypes
for (Symbol symbol : site.members().lookup(name)) {
if (symbol.kind == Symbol.MTH) {
bestSoFar = selectBest(env, site, argTypes, symbol, bestSoFar);
}
}
// best guess: method with unique name
// TODO remove, when search will be improved
if (bestSoFar.kind < Symbol.ERRONEOUS) {
return bestSoFar;
}
for (Symbol symbol : site.enclosingClass().members().lookup(name)) {
if ((symbol.kind == Symbol.MTH) && isAccessible(env, site, symbol)) {
if (bestSoFar.kind < Symbol.ERRONEOUS) {
return new AmbiguityErrorSymbol();
}
bestSoFar = symbol;
}
}
return bestSoFar;
}
/**
* @param symbol candidate
* @param bestSoFar previously found best match
*/
private Symbol selectBest(Env env, Symbol.TypeSymbol site, List argTypes, Symbol symbol, Symbol bestSoFar) {
if (!isInheritedIn(symbol, site)) {
return bestSoFar;
}
// TODO get rid of null check
if (symbol.type == null) {
return bestSoFar;
}
if (!isArgumentsAcceptable(argTypes, ((Type.MethodType) symbol.type).argTypes)) {
return bestSoFar;
}
// TODO ambiguity, errors, ...
if (!isAccessible(env, site, symbol)) {
return new AccessErrorSymbol(symbol);
}
return selectMostSpecific(symbol, bestSoFar);
}
/**
* @param argTypes types of arguments
* @param formals types of formal parameters of method
*/
private boolean isArgumentsAcceptable(List argTypes, List formals) {
// TODO varargs
if (argTypes.size() != formals.size()) {
return false;
}
for (int i = 0; i < argTypes.size(); i++) {
if (!types.isSubtype(argTypes.get(i), formals.get(i))) {
return false;
}
}
return true;
}
/**
* JLS7 15.12.2.5. Choosing the Most Specific Method
*/
private Symbol selectMostSpecific(Symbol m1, Symbol m2) {
// FIXME get rig of null check
if (m2.type == null) {
return m1;
}
boolean m1SignatureMoreSpecific = isSignatureMoreSpecific(m1, m2);
boolean m2SignatureMoreSpecific = isSignatureMoreSpecific(m2, m1);
if (m1SignatureMoreSpecific && m2SignatureMoreSpecific) {
// TODO handle this case
return new AmbiguityErrorSymbol();
} else if (m1SignatureMoreSpecific) {
return m1;
} else if (m2SignatureMoreSpecific) {
return m2;
} else {
return new AmbiguityErrorSymbol();
}
}
/**
* @return true, if signature of m1 is more specific than signature of m2
*/
private boolean isSignatureMoreSpecific(Symbol m1, Symbol m2) {
return isArgumentsAcceptable(((Type.MethodType) m1.type).argTypes, ((Type.MethodType) m2.type).argTypes);
}
/**
* Is class accessible in given environment?
*/
@VisibleForTesting
boolean isAccessible(Env env, Symbol.TypeSymbol c) {
final boolean result;
switch (c.flags() & Flags.ACCESS_FLAGS) {
case Flags.PRIVATE:
result = (env.enclosingClass().outermostClass() == c.owner().outermostClass());
break;
case 0:
result = (env.packge() == c.packge());
break;
case Flags.PUBLIC:
result = true;
break;
case Flags.PROTECTED:
result = (env.packge() == c.packge()) || isInnerSubClass(env.enclosingClass(), c.owner());
break;
default:
throw new IllegalStateException();
}
// TODO check accessibility of enclosing type: isAccessible(env, c.type.getEnclosingType())
return result;
}
/**
* Is given class a subclass of given base class, or an inner class of a subclass?
*/
private boolean isInnerSubClass(Symbol.TypeSymbol c, Symbol base) {
while (c != null && isSubClass(c, base)) {
c = c.owner().enclosingClass();
}
return c != null;
}
/**
* Is given class a subclass of given base class?
*/
@VisibleForTesting
boolean isSubClass(Symbol.TypeSymbol c, Symbol base) {
// TODO get rid of null check
if (c == null) {
return false;
}
// TODO see Javac
if (c == base) {
// same class
return true;
} else if ((base.flags() & Flags.INTERFACE) != 0) {
// check if class implements base
for (Type interfaceType : c.getInterfaces()) {
if (isSubClass(interfaceType.symbol, base)) {
return true;
}
}
// check if superclass implements base
return isSubClass(superclassSymbol(c), base);
} else {
// check if class extends base or its superclass extends base
return isSubClass(superclassSymbol(c), base);
}
}
/**
* Is symbol accessible as a member of given class in given environment?
*
* Symbol is accessible only if not overridden by another symbol. If overridden, then strictly speaking it is not a member.
*/
private boolean isAccessible(Env env, Symbol.TypeSymbol site, Symbol symbol) {
switch (symbol.flags() & Flags.ACCESS_FLAGS) {
case Flags.PRIVATE:
// no check of overriding, because private members cannot be overridden
return (env.enclosingClass().outermostClass() == symbol.owner().outermostClass())
&& isInheritedIn(symbol, site);
case 0:
return (env.packge() == symbol.packge())
&& isAccessible(env, site)
&& isInheritedIn(symbol, site)
&& notOverriddenIn(site, symbol);
case Flags.PUBLIC:
return isAccessible(env, site)
&& notOverriddenIn(site, symbol);
case Flags.PROTECTED:
return ((env.packge() == symbol.packge()) || isProtectedAccessible(symbol, env.enclosingClass, site))
&& isAccessible(env, site)
&& notOverriddenIn(site, symbol);
default:
throw new IllegalStateException();
}
}
private boolean notOverriddenIn(Symbol.TypeSymbol site, Symbol symbol) {
// TODO see Javac
return true;
}
/**
* Is symbol inherited in given class?
*/
@VisibleForTesting
boolean isInheritedIn(Symbol symbol, Symbol.TypeSymbol clazz) {
switch (symbol.flags() & Flags.ACCESS_FLAGS) {
case Flags.PUBLIC:
return true;
case Flags.PRIVATE:
return symbol.owner() == clazz;
case Flags.PROTECTED:
// TODO see Javac
return true;
case 0:
// TODO see Javac
Symbol.PackageSymbol thisPackage = symbol.packge();
for (Symbol.TypeSymbol sup = clazz; sup != null && sup != symbol.owner(); sup = superclassSymbol(sup)) {
if (sup.packge() != thisPackage) {
return false;
}
}
return true;
default:
throw new IllegalStateException();
}
}
private boolean isProtectedAccessible(Symbol symbol, Symbol.TypeSymbol c, Symbol.TypeSymbol site) {
// TODO see Javac
return true;
}
public static class SymbolNotFound extends Symbol {
public SymbolNotFound() {
super(Symbol.ABSENT, 0, null, null);
}
}
public static class AmbiguityErrorSymbol extends Symbol {
public AmbiguityErrorSymbol() {
super(Symbol.AMBIGUOUS, 0, null, null);
}
}
public static class AccessErrorSymbol extends Symbol {
/**
* The invalid symbol found during resolution.
*/
Symbol symbol;
public AccessErrorSymbol(Symbol symbol) {
super(Symbol.ERRONEOUS, 0, null, null);
this.symbol = symbol;
}
}
private static Symbol.TypeSymbol superclassSymbol(Symbol.TypeSymbol c) {
Type supertype = c.getSuperclass();
return supertype == null ? null : supertype.symbol;
}
}