MethodVerifier.java
| Index Score | ||
|---|---|---|
![]() |
![]() |
net.sourceforge.phpdt.internal.compiler.lookup |
![]() |
![]() |
PHPeclipse |
View: Reasons, Metrics, Source Code
These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package net.sourceforge.phpdt.internal.compiler.lookup;
import net.sourceforge.phpdt.core.compiler.CharOperation;
import net.sourceforge.phpdt.internal.compiler.ast.MethodDeclaration;
import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
import net.sourceforge.phpdt.internal.compiler.util.HashtableOfObject;
public final class MethodVerifier implements TagBits, TypeConstants {
SourceTypeBinding type;
HashtableOfObject inheritedMethods;
HashtableOfObject currentMethods;
ReferenceBinding runtimeException;
ReferenceBinding errorException;
LookupEnvironment environment;
/*
* Binding creation is responsible for reporting all problems with types: -
* all modifier problems (duplicates & multiple visibility modifiers +
* incompatible combinations - abstract/final) - plus invalid modifiers
* given the context (the verifier did not do this before) - qualified name
* collisions between a type and a package (types in default packages are
* excluded) - all type hierarchy problems: - cycles in the superclass or
* superinterface hierarchy - an ambiguous, invisible or missing superclass
* or superinterface - extending a final class - extending an interface
* instead of a class - implementing a class instead of an interface -
* implementing the same interface more than once (ie. duplicate interfaces) -
* with nested types: - shadowing an enclosing type's source name - defining
* a static class or interface inside a non-static nested class - defining
* an interface as a local type (local types can only be classes)
*/
public MethodVerifier(LookupEnvironment environment) {
this.type = null; // Initialized with the public method
// verify(SourceTypeBinding)
this.inheritedMethods = null;
this.currentMethods = null;
this.runtimeException = null;
this.errorException = null;
this.environment = environment;
}
private void checkAgainstInheritedMethods(MethodBinding currentMethod,
MethodBinding[] methods, int length) {
currentMethod.modifiers |= CompilerModifiers.AccOverriding;
for (int i = length; --i >= 0;) {
MethodBinding inheritedMethod = methods[i];
if (!currentMethod.isAbstract() && inheritedMethod.isAbstract())
currentMethod.modifiers |= CompilerModifiers.AccImplementing;
if (currentMethod.returnType != inheritedMethod.returnType) {
this.problemReporter(currentMethod).incompatibleReturnType(
currentMethod, inheritedMethod);
} else if (currentMethod.isStatic() != inheritedMethod.isStatic()) { // Cannot
// override
// a
// static
// method
// or
// hide
// an
// instance
// method
this.problemReporter(currentMethod).staticAndInstanceConflict(
currentMethod, inheritedMethod);
} else {
if (currentMethod.thrownExceptions != NoExceptions)
this.checkExceptions(currentMethod, inheritedMethod);
if (inheritedMethod.isFinal())
this.problemReporter(currentMethod)
.finalMethodCannotBeOverridden(currentMethod,
inheritedMethod);
if (!this.isAsVisible(currentMethod, inheritedMethod))
this.problemReporter(currentMethod).visibilityConflict(
currentMethod, inheritedMethod);
// if (inheritedMethod.isViewedAsDeprecated())
// if (!currentMethod.isViewedAsDeprecated() ||
// environment.options.reportDeprecationInsideDeprecatedCode)
// this.problemReporter(currentMethod).overridesDeprecatedMethod(currentMethod,
// inheritedMethod);
}
}
}
/*
* "8.4.4" Verify that newExceptions are all included in
* inheritedExceptions. Assumes all exceptions are valid and throwable.
* Unchecked exceptions (compatible with runtime & error) are ignored (see
* the spec on pg. 203).
*/
private void checkExceptions(MethodBinding newMethod,
MethodBinding inheritedMethod) {
ReferenceBinding[] newExceptions = newMethod.thrownExceptions;
ReferenceBinding[] inheritedExceptions = inheritedMethod.thrownExceptions;
for (int i = newExceptions.length; --i >= 0;) {
ReferenceBinding newException = newExceptions[i];
int j = inheritedExceptions.length;
while (--j > -1
&& !this.isSameClassOrSubclassOf(newException,
inheritedExceptions[j]))
;
if (j == -1)
if (!(newException.isCompatibleWith(this.runtimeException()) || newException
.isCompatibleWith(this.errorException())))
this.problemReporter(newMethod)
.incompatibleExceptionInThrowsClause(this.type,
newMethod, inheritedMethod, newException);
}
}
private void checkInheritedMethods(MethodBinding[] methods, int length) {
TypeBinding returnType = methods[0].returnType;
int index = length;
while (--index > 0 && returnType == methods[index].returnType)
;
if (index > 0) { // All inherited methods do NOT have the same
// vmSignature
this.problemReporter().inheritedMethodsHaveIncompatibleReturnTypes(
this.type, methods, length);
return;
}
MethodBinding concreteMethod = null;
if (!type.isInterface()) { // ignore concrete methods for interfaces
for (int i = length; --i >= 0;) { // Remember that only one of the
// methods can be non-abstract
if (!methods[i].isAbstract()) {
concreteMethod = methods[i];
break;
}
}
}
if (concreteMethod == null) {
if (this.type.isClass() && !this.type.isAbstract()) {
for (int i = length; --i >= 0;)
if (!mustImplementAbstractMethod(methods[i]))
return; // have already reported problem against the
// concrete superclass
TypeDeclaration typeDeclaration = this.type.scope.referenceContext;
if (typeDeclaration != null) {
MethodDeclaration missingAbstractMethod = typeDeclaration
.addMissingAbstractMethodFor(methods[0]);
missingAbstractMethod.scope.problemReporter()
.abstractMethodMustBeImplemented(this.type,
methods[0]);
} else {
this.problemReporter().abstractMethodMustBeImplemented(
this.type, methods[0]);
}
}
return;
}
MethodBinding[] abstractMethods = new MethodBinding[length - 1];
index = 0;
for (int i = length; --i >= 0;)
if (methods[i] != concreteMethod)
abstractMethods[index++] = methods[i];
// Remember that interfaces can only define public instance methods
if (concreteMethod.isStatic())
// Cannot inherit a static method which is specified as an instance
// method by an interface
this.problemReporter().staticInheritedMethodConflicts(type,
concreteMethod, abstractMethods);
if (!concreteMethod.isPublic())
// Cannot reduce visibility of a public method specified by an
// interface
this.problemReporter().inheritedMethodReducesVisibility(type,
concreteMethod, abstractMethods);
if (concreteMethod.thrownExceptions != NoExceptions)
for (int i = abstractMethods.length; --i >= 0;)
this.checkExceptions(concreteMethod, abstractMethods[i]);
}
/*
* For each inherited method identifier (message pattern - vm signature
* minus the return type) if current method exists if current's vm signature
* does not match an inherited signature then complain else compare
* current's exceptions & visibility against each inherited method else if
* inherited methods = 1 if inherited is abstract && type is NOT an
* interface or abstract, complain else if vm signatures do not match
* complain else find the concrete implementation amongst the abstract
* methods (can only be 1) if one exists then it must be a public instance
* method compare concrete's exceptions against each abstract method else
* complain about missing implementation only if type is NOT an interface or
* abstract
*/
private void checkMethods() {
boolean mustImplementAbstractMethods = this.type.isClass()
&& !this.type.isAbstract();
char[][] methodSelectors = this.inheritedMethods.keyTable;
for (int s = methodSelectors.length; --s >= 0;) {
if (methodSelectors[s] != null) {
MethodBinding[] current = (MethodBinding[]) this.currentMethods
.get(methodSelectors[s]);
MethodBinding[] inherited = (MethodBinding[]) this.inheritedMethods.valueTable[s];
int index = -1;
MethodBinding[] matchingInherited = new MethodBinding[inherited.length];
if (current != null) {
for (int i = 0, length1 = current.length; i < length1; i++) {
while (index >= 0)
matchingInherited[index--] = null; // clear the
// previous
// contents of
// the matching
// methods
MethodBinding currentMethod = current[i];
for (int j = 0, length2 = inherited.length; j < length2; j++) {
if (inherited[j] != null
&& currentMethod
.areParametersEqual(inherited[j])) {
matchingInherited[++index] = inherited[j];
inherited[j] = null; // do not want to find
// it again
}
}
if (index >= 0)
this.checkAgainstInheritedMethods(currentMethod,
matchingInherited, index + 1); // pass in
// the
// length of
// matching
}
}
for (int i = 0, length = inherited.length; i < length; i++) {
while (index >= 0)
matchingInherited[index--] = null; // clear the
// previous contents
// of the matching
// methods
if (inherited[i] != null) {
matchingInherited[++index] = inherited[i];
for (int j = i + 1; j < length; j++) {
if (inherited[j] != null
&& inherited[i]
.areParametersEqual(inherited[j])) {
matchingInherited[++index] = inherited[j];
inherited[j] = null; // do not want to find
// it again
}
}
}
if (index > 0) {
this
.checkInheritedMethods(matchingInherited,
index + 1); // pass in the length of
// matching
} else if (mustImplementAbstractMethods && index == 0
&& matchingInherited[0].isAbstract()) {
if (mustImplementAbstractMethod(matchingInherited[0])) {
TypeDeclaration typeDeclaration = this.type.scope.referenceContext;
if (typeDeclaration != null) {
MethodDeclaration missingAbstractMethod = typeDeclaration
.addMissingAbstractMethodFor(matchingInherited[0]);
missingAbstractMethod.scope
.problemReporter()
.abstractMethodMustBeImplemented(
this.type, matchingInherited[0]);
} else {
this
.problemReporter()
.abstractMethodMustBeImplemented(
this.type, matchingInherited[0]);
}
}
}
}
}
}
}
private void checkPackagePrivateAbstractMethod(MethodBinding abstractMethod) {
ReferenceBinding superType = this.type.superclass();
char[] selector = abstractMethod.selector;
do {
if (!superType.isValidBinding())
return;
if (!superType.isAbstract())
return; // closer non abstract super type will be flagged
// instead
MethodBinding[] methods = superType.getMethods(selector);
nextMethod: for (int m = methods.length; --m >= 0;) {
MethodBinding method = methods[m];
if (method.returnType != abstractMethod.returnType
|| !method.areParametersEqual(abstractMethod))
continue nextMethod;
if (method.isPrivate() || method.isConstructor()
|| method.isDefaultAbstract())
continue nextMethod;
if (superType.fPackage == abstractMethod.declaringClass.fPackage)
return; // found concrete implementation of abstract method
// in same package
}
} while ((superType = superType.superclass()) != abstractMethod.declaringClass);
// non visible abstract methods cannot be overridden so the type must be
// defined abstract
this.problemReporter().abstractMethodCannotBeOverridden(this.type,
abstractMethod);
}
/*
* Binding creation is responsible for reporting: - all modifier problems
* (duplicates & multiple visibility modifiers + incompatible combinations) -
* plus invalid modifiers given the context... examples: - interface methods
* can only be public - abstract methods can only be defined by abstract
* classes - collisions... 2 methods with identical vmSelectors - multiple
* methods with the same message pattern but different return types -
* ambiguous, invisible or missing return/argument/exception types - check
* the type of any array is not void - check that each exception type is
* Throwable or a subclass of it
*/
private void computeInheritedMethods() {
this.inheritedMethods = new HashtableOfObject(51); // maps method
// selectors to an
// array of
// methods... must
// search to match
// paramaters &
// return type
ReferenceBinding[][] interfacesToVisit = new ReferenceBinding[5][];
int lastPosition = 0;
interfacesToVisit[lastPosition] = type.superInterfaces();
ReferenceBinding superType = this.type.isClass() ? this.type
.superclass() : this.type.scope.getJavaLangObject(); // check
// interface
// methods
// against
// Object
MethodBinding[] nonVisibleDefaultMethods = null;
int nonVisibleCount = 0;
while (superType != null) {
if (superType.isValidBinding()) {
ReferenceBinding[] itsInterfaces = superType.superInterfaces();
if (itsInterfaces != NoSuperInterfaces) {
if (++lastPosition == interfacesToVisit.length)
System
.arraycopy(
interfacesToVisit,
0,
interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
0, lastPosition);
interfacesToVisit[lastPosition] = itsInterfaces;
}
MethodBinding[] methods = superType.methods();
nextMethod: for (int m = methods.length; --m >= 0;) {
MethodBinding method = methods[m];
if (!(method.isPrivate() || method.isConstructor() || method
.isDefaultAbstract())) { // look at all methods
// which are NOT private
// or constructors or
// default abstract
MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods
.get(method.selector);
if (existingMethods != null) {
for (int i = 0, length = existingMethods.length; i < length; i++) {
if (method.returnType == existingMethods[i].returnType
&& method
.areParametersEqual(existingMethods[i])) {
if (method.isDefault()
&& method.isAbstract()
&& method.declaringClass.fPackage != type.fPackage)
checkPackagePrivateAbstractMethod(method);
continue nextMethod;
}
}
}
if (nonVisibleDefaultMethods != null)
for (int i = 0; i < nonVisibleCount; i++)
if (method.returnType == nonVisibleDefaultMethods[i].returnType
&& CharOperation
.equals(
method.selector,
nonVisibleDefaultMethods[i].selector)
&& method
.areParametersEqual(nonVisibleDefaultMethods[i]))
continue nextMethod;
if (!(method.isDefault() && method.declaringClass.fPackage != type.fPackage)) { // ignore
// methods
// which
// have
// default
// visibility
// and
// are
// NOT
// defined
// in
// another
// package
if (existingMethods == null)
existingMethods = new MethodBinding[1];
else
System
.arraycopy(
existingMethods,
0,
(existingMethods = new MethodBinding[existingMethods.length + 1]),
0, existingMethods.length - 1);
existingMethods[existingMethods.length - 1] = method;
this.inheritedMethods.put(method.selector,
existingMethods);
} else {
if (nonVisibleDefaultMethods == null)
nonVisibleDefaultMethods = new MethodBinding[10];
else if (nonVisibleCount == nonVisibleDefaultMethods.length)
System
.arraycopy(
nonVisibleDefaultMethods,
0,
(nonVisibleDefaultMethods = new MethodBinding[nonVisibleCount * 2]),
0, nonVisibleCount);
nonVisibleDefaultMethods[nonVisibleCount++] = method;
if (method.isAbstract() && !this.type.isAbstract()) // non
// visible
// abstract
// methods
// cannot
// be
// overridden
// so
// the
// type
// must
// be
// defined
// abstract
this.problemReporter()
.abstractMethodCannotBeOverridden(
this.type, method);
MethodBinding[] current = (MethodBinding[]) this.currentMethods
.get(method.selector);
if (current != null) { // non visible methods
// cannot be overridden so a
// warning is issued
foundMatch: for (int i = 0, length = current.length; i < length; i++) {
if (method.returnType == current[i].returnType
&& method
.areParametersEqual(current[i])) {
this.problemReporter()
.overridesPackageDefaultMethod(
current[i], method);
break foundMatch;
}
}
}
}
}
}
superType = superType.superclass();
}
}
for (int i = 0; i <= lastPosition; i++) {
ReferenceBinding[] interfaces = interfacesToVisit[i];
if (interfaces == null) {
interfaces = new ReferenceBinding[0];
}
for (int j = 0, length = interfaces.length; j < length; j++) {
superType = interfaces[j];
if ((superType.tagBits & InterfaceVisited) == 0) {
superType.tagBits |= InterfaceVisited;
if (superType.isValidBinding()) {
ReferenceBinding[] itsInterfaces = superType
.superInterfaces();
if (itsInterfaces != NoSuperInterfaces) {
if (++lastPosition == interfacesToVisit.length)
System
.arraycopy(
interfacesToVisit,
0,
interfacesToVisit = new ReferenceBinding[lastPosition * 2][],
0, lastPosition);
interfacesToVisit[lastPosition] = itsInterfaces;
}
MethodBinding[] methods = superType.methods();
for (int m = methods.length; --m >= 0;) { // Interface
// methods
// are all
// abstract
// public
MethodBinding method = methods[m];
MethodBinding[] existingMethods = (MethodBinding[]) this.inheritedMethods
.get(method.selector);
if (existingMethods == null)
existingMethods = new MethodBinding[1];
else
System
.arraycopy(
existingMethods,
0,
(existingMethods = new MethodBinding[existingMethods.length + 1]),
0, existingMethods.length - 1);
existingMethods[existingMethods.length - 1] = method;
this.inheritedMethods.put(method.selector,
existingMethods);
}
}
}
}
}
// bit reinitialization
for (int i = 0; i <= lastPosition; i++) {
ReferenceBinding[] interfaces = interfacesToVisit[i];
if (interfaces == null) {
interfaces = new ReferenceBinding[0];
}
for (int j = 0, length = interfaces.length; j < length; j++)
interfaces[j].tagBits &= ~InterfaceVisited;
}
}
private void computeMethods() {
MethodBinding[] methods = type.methods();
if (methods == null) {
methods = new MethodBinding[0];
}
int size = methods.length;
this.currentMethods = new HashtableOfObject(size == 0 ? 1 : size); // maps
// method
// selectors
// to
// an
// array
// of
// methods...
// must
// search
// to
// match
// paramaters
// &
// return
// type
for (int m = size; --m >= 0;) {
MethodBinding method = methods[m];
if (!(method.isConstructor() || method.isDefaultAbstract())) { // keep
// all
// methods
// which
// are
// NOT
// constructors
// or
// default
// abstract
MethodBinding[] existingMethods = (MethodBinding[]) this.currentMethods
.get(method.selector);
if (existingMethods == null)
existingMethods = new MethodBinding[1];
else
System
.arraycopy(
existingMethods,
0,
(existingMethods = new MethodBinding[existingMethods.length + 1]),
0, existingMethods.length - 1);
existingMethods[existingMethods.length - 1] = method;
this.currentMethods.put(method.selector, existingMethods);
}
}
}
private ReferenceBinding errorException() {
if (errorException == null)
this.errorException = this.type.scope.getJavaLangError();
return errorException;
}
private boolean isAsVisible(MethodBinding newMethod,
MethodBinding inheritedMethod) {
if (inheritedMethod.modifiers == newMethod.modifiers)
return true;
if (newMethod.isPublic())
return true; // Covers everything
if (inheritedMethod.isPublic())
return false;
if (newMethod.isProtected())
return true;
if (inheritedMethod.isProtected())
return false;
return !newMethod.isPrivate(); // The inheritedMethod cannot be private
// since it would not be visible
}
private boolean isSameClassOrSubclassOf(ReferenceBinding testClass,
ReferenceBinding superclass) {
do {
if (testClass == superclass)
return true;
} while ((testClass = testClass.superclass()) != null);
return false;
}
private boolean mustImplementAbstractMethod(MethodBinding abstractMethod) {
// if the type's superclass is an abstract class, then all abstract
// methods must be implemented
// otherwise, skip it if the type's superclass must implement any of the
// inherited methods
ReferenceBinding superclass = this.type.superclass();
ReferenceBinding declaringClass = abstractMethod.declaringClass;
if (declaringClass.isClass()) {
while (superclass.isAbstract() && superclass != declaringClass)
superclass = superclass.superclass(); // find the first
// concrete superclass
// or the abstract
// declaringClass
} else {
if (this.type.implementsInterface(declaringClass, false)) {
if (this.type.isAbstract())
return false; // leave it for the subclasses
if (!superclass.implementsInterface(declaringClass, true)) // only
// if a
// superclass
// does
// not
// also
// implement
// the
// interface
return true;
}
while (superclass.isAbstract()
&& !superclass.implementsInterface(declaringClass, false))
superclass = superclass.superclass(); // find the first
// concrete superclass
// or the superclass
// which implements the
// interface
}
return superclass.isAbstract(); // if it is a concrete class then we
// have already reported problem against
// it
}
private ProblemReporter problemReporter() {
return this.type.scope.problemReporter();
}
private ProblemReporter problemReporter(MethodBinding currentMethod) {
ProblemReporter reporter = problemReporter();
if (currentMethod.declaringClass == type) // only report against the
// currentMethod if its
// implemented by the type
reporter.referenceContext = currentMethod.sourceMethod();
return reporter;
}
private ReferenceBinding runtimeException() {
if (runtimeException == null)
this.runtimeException = this.type.scope
.getJavaLangRuntimeException();
return runtimeException;
}
public void verify(SourceTypeBinding type) {
this.type = type;
this.computeMethods();
this.computeInheritedMethods();
this.checkMethods();
}
public String toString() {
StringBuffer buffer = new StringBuffer(10);
buffer.append("MethodVerifier for type: "); //$NON-NLS-1$
buffer.append(type.readableName());
buffer.append('\n');
buffer.append("\t-inherited methods: "); //$NON-NLS-1$
buffer.append(this.inheritedMethods);
return buffer.toString();
}
}
The table below shows all metrics for MethodVerifier.java.




