/******************************************************************************* * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v0.5 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v05.html * * Contributors: * IBM Corporation - initial API and implementation ******************************************************************************/ package net.sourceforge.phpdt.internal.compiler.lookup; 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.CharOperation; 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; /* 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) verifyTypeStructure | hasHierarchyProblem superclass current names interfaces interfacesByIndentity duplicateExists invalidType | (type basicModifiers anyMask: AccModifierProblem | AccAlternateModifierProblem) ifTrue: [ self reportModifierProblemsOnType: type]. type controller isJavaDefaultPackage ifFalse: [ (nameEnvironment class doesPackageExistNamed: type javaQualifiedName) ifTrue: [ problemSummary reportVerificationProblem: #CollidesWithPackage args: (Array with: type javaQualifiedName) severity: nil forType: type]]. hasHierarchyProblem := false. type isJavaClass ifTrue: [ (superclass := self superclassFor: type) ~~ nil ifTrue: [ superclass isBuilderClass ifTrue: [ superclass := superclass newClass]. superclass isJavaMissing ifTrue: [ hasHierarchyProblem := true. type javaSuperclassIsMissing ifTrue: [ problemSummary reportVerificationProblem: #MissingSuperclass args: (Array with: superclass javaQualifiedName with: superclass unmatchedDescriptor) severity: nil forType: type]. type javaSuperclassCreatesCycle ifTrue: [ problemSummary reportVerificationProblem: #CyclicSuperclass args: (Array with: superclass javaQualifiedName) severity: nil forType: type]. type javaSuperclassIsInterface ifTrue: [ problemSummary reportVerificationProblem: #ClassCannotExtendAnInterface args: (Array with: superclass javaQualifiedName) severity: nil forType: type]] ifFalse: [ "NOTE: If type is a Java class and its superclass is a valid descriptor then it should NEVER be an interface." superclass isJavaFinal ifTrue: [ problemSummary reportVerificationProblem: #ClassCannotExtendFinalClass args: nil severity: nil forType: type]]]] ifFalse: [ type isJavaLocalType ifTrue: [ problemSummary reportVerificationProblem: #CannotDefineLocalInterface args: nil severity: nil forType: type]]. type isJavaNestedType ifTrue: [ (current := type) sourceName notEmpty ifTrue: [ names := Set new. [(current := current enclosingType) ~~ nil] whileTrue: [ names add: current sourceName]. (names includes: type sourceName) ifTrue: [ problemSummary reportVerificationProblem: #NestedTypeCannotShadowTypeName args: nil severity: nil forType: type]]. (type enclosingType isJavaNestedType and: [type enclosingType isJavaClass]) ifTrue: [ type enclosingType isJavaStatic ifFalse: [ type isJavaClass ifTrue: [ type isJavaStatic ifTrue: [ problemSummary reportVerificationProblem: #StaticClassCannotExistInNestedClass args: nil severity: nil forType: type]] ifFalse: [ problemSummary reportVerificationProblem: #InterfaceCannotExistInNestedClass args: nil severity: nil forType: type]]]]. (interfaces := newClass superinterfaces) notEmpty ifTrue: [ interfacesByIndentity := interfaces asSet. duplicateExists := interfaces size ~~ interfacesByIndentity size. interfacesByIndentity do: [:interface | duplicateExists ifTrue: [ (interfaces occurrencesOf: interface) > 1 ifTrue: [ problemSummary reportVerificationProblem: #InterfaceIsSpecifiedMoreThanOnce args: (Array with: interface javaQualifiedName) severity: nil forType: type]]. interface isJavaMissing ifTrue: [ hasHierarchyProblem := true. interface basicClass == JavaInterfaceIsClass basicClass ifTrue: [ problemSummary reportVerificationProblem: #UsingClassWhereInterfaceIsRequired args: (Array with: interface javaQualifiedName) severity: nil forType: type] ifFalse: [ interface basicClass == JavaMissingInterface basicClass ifTrue: [ problemSummary reportVerificationProblem: #MissingInterface args: (Array with: interface javaQualifiedName with: interface unmatchedDescriptor) severity: nil forType: type] ifFalse: [ problemSummary reportVerificationProblem: #CyclicSuperinterface args: (Array with: interface javaQualifiedName) severity: nil forType: type]]]]]. hasHierarchyProblem ifFalse: [ "Search up the type's hierarchy for 1. missing superclass, 2. superclass cycle, or 3. superclass is interface." (invalidType := newClass findFirstInvalidSupertypeSkipping: EsIdentitySet new) ~~ nil ifTrue: [ problemSummary reportVerificationProblem: #HasHierarchyProblem args: (Array with: invalidType javaReadableName) severity: nil forType: type]] reportModifierProblemsOnType: aType (type basicModifiers anyMask: AccAlternateModifierProblem) ifTrue: [ (type basicModifiers anyMask: AccModifierProblem) ifTrue: [ ^problemSummary reportVerificationProblem: #OnlyOneVisibilityModifierAllowed args: nil severity: nil forType: aType] ifFalse: [ ^problemSummary reportVerificationProblem: #DuplicateModifier args: nil severity: nil forType: aType]]. type isJavaInterface ifTrue: [ ^problemSummary reportVerificationProblem: #IllegalModifierForInterface args: nil severity: nil forType: aType]. (type basicModifiers allMask: AccAbstract | AccFinal) ifTrue: [ ^problemSummary reportVerificationProblem: #IllegalModifierCombinationAbstractFinal args: nil severity: nil forType: aType]. ^problemSummary reportVerificationProblem: #IllegalModifierForClass args: nil severity: nil forType: aType void reportModifierProblems() { if (this.type.isAbstract() && this.type.isFinal()) this.problemReporter.illegalModifierCombinationAbstractFinal(this.type); // Should be able to detect all 3 problems NOT just 1 if ((type.modifiers() & Modifiers.AccAlternateModifierProblem) == 0) { if (this.type.isInterface()) this.problemReporter.illegalModifierForInterface(this.type); else this.problemReporter.illegalModifier(this.type); } else { if ((type.modifiers() & Modifiers.AccModifierProblem) != 0) this.problemReporter.onlyOneVisibilityModifierAllowed(this.type); else this.problemReporter.duplicateModifier(this.type); } } */ 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; } private void checkAgainstInheritedMethods(MethodBinding currentMethod, MethodBinding[] methods, int length) { for (int i = length; --i >= 0;) { MethodBinding inheritedMethod = methods[i]; 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()) 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; // in this case, we 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]); } } } } } } } /* 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; if (this.type.isClass()) { superType = this.type.superclass(); } else { // check interface methods against Object superType = this.type.scope.getJavaLangObject(); } 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) if (method.areParametersEqual(existingMethods[i])) continue nextMethod; if (nonVisibleDefaultMethods != null) for (int i = 0; i < nonVisibleCount; i++) if (method.returnType == nonVisibleDefaultMethods[i].returnType) if (CharOperation.equals(method.selector, nonVisibleDefaultMethods[i].selector)) if (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) { if (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]; 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]; for (int j = 0, length = interfaces.length; j < length; j++) interfaces[j].tagBits &= ~InterfaceVisited; } } /* computeInheritedMethodMembers "8.4.6.4" "Compute all of the members for the type that are inherited from its supertypes. This includes: All of the methods implemented in the supertype hierarchy that are not overridden. PROBLEM: Currently we do not remove overridden methods in the interface hierarchy. This could cause a non-existent exception error to be detected." | supertype allSuperinterfaces methodsSeen interfacesSeen | inheritedMethodMembers := LookupTable new: 50. allSuperinterfaces := OrderedCollection new. type isJavaClass ifTrue: [ supertype := type. methodsSeen := EsIdentitySet new: 20. [(supertype := self superclassFor: supertype) == nil] whileFalse: [ (supertype isBuilderClass or: [supertype isValidDescriptor]) ifTrue: [ allSuperinterfaces addAll: (self superinterfacesFor: supertype). supertype javaUserDefinedMethodsDo: [:method | (method isJavaPrivate or: [method isJavaConstructor]) ifFalse: [ (method isJavaDefault and: [method declaringClass package symbol ~= type package symbol]) ifFalse: [ (methodsSeen includes: method selector) ifFalse: [ methodsSeen add: method selector. (inheritedMethodMembers at: (self methodSignatureFor: method selector) ifAbsentPut: [OrderedCollection new: 3]) add: method]]]]]]]. allSuperinterfaces addAll: (self superinterfacesFor: type). interfacesSeen := EsIdentitySet new: allSuperinterfaces size * 2. [allSuperinterfaces notEmpty] whileTrue: [ supertype := allSuperinterfaces removeFirst. (interfacesSeen includes: supertype) ifFalse: [ interfacesSeen add: supertype. (supertype isBuilderClass or: [supertype isValidDescriptor]) ifTrue: [ allSuperinterfaces addAll: (self superinterfacesFor: supertype). supertype javaUserDefinedMethodsDo: [:method | "Interface methods are all abstract public." (inheritedMethodMembers at: (self methodSignatureFor: method selector) ifAbsentPut: [OrderedCollection new: 3]) add: method]]]] */ private void computeMethods() { MethodBinding[] methods = type.methods(); 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)) return !this.type.isAbstract(); 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(); } private void zzFieldProblems() { } /* Binding creation is responsible for reporting all problems with fields: - all modifier problems (duplicates & multiple visibility modifiers + incompatible combinations - final/volatile) - plus invalid modifiers given the context (the verifier did not do this before) - include initializers in the modifier checks even though bindings are not created - collisions... 2 fields with same name - interfaces cannot define initializers - nested types cannot define static fields - with the type of the field: - void is not a valid type (or for an array) - an ambiguous, invisible or missing type verifyFields | toSearch | (toSearch := newClass fields) notEmpty ifTrue: [ newClass fromJavaClassFile ifTrue: [ toSearch do: [:field | field isJavaInitializer ifFalse: [ self verifyFieldType: field]]] ifFalse: [ toSearch do: [:field | field isJavaInitializer ifTrue: [self verifyFieldInitializer: field] ifFalse: [self verifyField: field]]]] verifyFieldInitializer: field type isJavaInterface ifTrue: [ problemSummary reportVerificationProblem: #InterfacesCannotHaveInitializers args: #() severity: nil forField: field] ifFalse: [ field isJavaStatic ifTrue: [ field modifiers == AccStatic ifFalse: [ problemSummary reportVerificationProblem: #IllegalModifierForStaticInitializer args: #() severity: nil forField: field]] ifFalse: [ field modifiers == 0 ifFalse: [ problemSummary reportVerificationProblem: #IllegalModifierForInitializer args: #() severity: nil forField: field]]] verifyField: field (field basicModifiers anyMask: AccAlternateModifierProblem | AccModifierProblem) ifTrue: [ self reportModifierProblemsOnField: field]. field isJavaStatic ifTrue: [ type isJavaStatic ifFalse: [ (type isJavaNestedType and: [type isJavaClass]) ifTrue: [ problemSummary reportVerificationProblem: #NestedClassCannotHaveStaticField args: #() severity: nil forField: field]]]. self verifyFieldType: field verifyFieldType: field | descriptor fieldType | "8.3 (Class) 9.3 (Interface)" "Optimize the base type case" field typeIsBaseType ifTrue: [ field typeName = 'V' ifTrue: [ "$NON-NLS$" problemSummary reportVerificationProblem: #IllegalTypeForField args: (Array with: JavaVoid) severity: nil forField: field]] ifFalse: [ descriptor := field asDescriptorIn: nameEnvironment. (fieldType := descriptor type) isValidDescriptor ifTrue: [ (fieldType isArrayType and: [fieldType leafComponentType isVoidType]) ifTrue: [ problemSummary reportVerificationProblem: #InvalidArrayType args: (Array with: fieldType javaReadableName) severity: nil forField: field]] ifFalse: [ problemSummary reportVerificationProblem: #UnboundTypeForField args: (Array with: fieldType javaReadableName with: fieldType leafComponentType) severity: nil forField: field]]. reportModifierProblemsOnField: field (field basicModifiers anyMask: AccAlternateModifierProblem) ifTrue: [ (field basicModifiers anyMask: AccModifierProblem) ifTrue: [ ^problemSummary reportVerificationProblem: #OnlyOneVisibilityModifierAllowed args: #() severity: ErrorInfo::ConflictingModifier forField: field] ifFalse: [ ^problemSummary reportVerificationProblem: #DuplicateModifier args: #() severity: ErrorInfo::ConflictingModifier forField: field]]. type isJavaInterface ifTrue: [ ^problemSummary reportVerificationProblem: #IllegalModifierForInterfaceField args: #() severity: nil forField: field]. (field basicModifiers allMask: AccFinal | AccVolatile) ifTrue: [ ^problemSummary reportVerificationProblem: #IllegalModifierCombinationFinalVolatile args: #() severity: nil forField: field]. ^problemSummary reportVerificationProblem: #IllegalModifierForField args: #() severity: nil forField: field void reportModifierProblems(FieldBinding field) { if (field.isFinal() && field.isVolatile()) this.problemReporter.illegalModifierCombinationFinalVolatile(field); // Should be able to detect all 3 problems NOT just 1 if ((type.modifiers() & Modifiers.AccAlternateModifierProblem) == 0) { if (this.type.isInterface()) this.problemReporter.illegalModifierForInterfaceField(field); else this.problemReporter.illegalModifier(field); } else { if ((field.modifiers & Modifiers.AccModifierProblem) != 0) this.problemReporter.onlyOneVisibilityModifierAllowed(field); else this.problemReporter.duplicateModifier(field); } } */ private void zzImportProblems() { } /* Binding creation is responsible for reporting all problems with imports: - on demand imports which refer to missing packages - with single type imports: - resolves to an ambiguous, invisible or missing type - conflicts with the type's source name - has the same simple name as another import Note: VAJ ignored duplicate imports (only one was kept) verifyImports | importsBySimpleName nameEnvClass imports cl first | importsBySimpleName := LookupTable new. nameEnvClass := nameEnvironment class. "7.5.2" type imports do: [:import | import isOnDemand ifTrue: [ (nameEnvClass doesPackageExistNamed: import javaPackageName) ifFalse: [ (nameEnvClass findJavaClassNamedFrom: import javaPackageName) == nil ifTrue: [ problemSummary reportVerificationProblem: #OnDemandImportRefersToMissingPackage args: (Array with: import asString) severity: ErrorInfo::ImportVerification forType: type]]] ifFalse: [ (imports := importsBySimpleName at: import javaSimpleName ifAbsent: []) == nil ifTrue: [ importsBySimpleName at: import javaSimpleName put: (Array with: import)] ifFalse: [ (imports includes: import) ifFalse: [ importsBySimpleName at: import javaSimpleName put: imports, (Array with: import)]]. "Ignore any imports which are simple names - we will treat these as no-ops." import javaPackageName notEmpty ifTrue: [ cl := nameEnvClass findJavaClassNamedFrom: import asString. (cl ~~ nil and: [cl isJavaPublic or: [cl controller symbol == type controller symbol]]) ifFalse: [ problemSummary reportVerificationProblem: #SingleTypeImportRefersToInvisibleType args: (Array with: import asString) severity: ErrorInfo::ImportVerification forType: type]]]]. importsBySimpleName notEmpty ifTrue: [ importsBySimpleName keysAndValuesDo: [:simpleName :matching | matching size == 1 ifTrue: [ simpleName = type sourceName ifTrue: [ matching first javaReadableName = type javaReadableName ifFalse: [ problemSummary reportVerificationProblem: #SingleTypeImportConflictsWithType args: #() severity: nil forType: type]]] ifFalse: [ problemSummary reportVerificationProblem: #SingleTypeImportsHaveSameSimpleName args: (Array with: simpleName) severity: nil forType: type]]] */ private void zzTypeProblems() { } }