blob: 7305abb44ead5921437f59fdf61bac5e8fd8b384 [file] [log] [blame]
/**********************************************************************
* This file is part of "Object Teams Development Tooling"-Software
*
* Copyright 2004, 2010 Fraunhofer Gesellschaft, Munich, Germany,
* for its Fraunhofer Institute for Computer Architecture and Software
* Technology (FIRST), Berlin, Germany and Technical University Berlin,
* Germany.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Please visit http://www.eclipse.org/objectteams for updates and contact.
*
* Contributors:
* Fraunhofer FIRST - Initial API and implementation
* Technical University Berlin - Initial API and implementation
**********************************************************************/
package org.eclipse.objectteams.otdt.internal.core.compiler.util;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.InvocationSite;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.objectteams.otdt.core.exceptions.InternalCompilerError;
import org.eclipse.objectteams.otdt.internal.core.compiler.ast.TSuperMessageSend;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.Dependencies;
import org.eclipse.objectteams.otdt.internal.core.compiler.control.ITranslationStates;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.RoleTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.MethodModel.ProblemDetail;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.RoleModel;
import org.eclipse.objectteams.otdt.internal.core.compiler.model.TeamModel;
/**
* Code dealing with modifiers and protection of team/role related elements.
*
* TODO (OTJLD): implement import restriction (1.2.3.(a))
*
* @author stephan
* @version $Id: Protections.java 23416 2010-02-03 19:59:31Z stephan $
*/
public class Protections implements ClassFileConstants, ExtraCompilerModifiers {
private static final int AccSynthIfc = (AccInterface|AccSynthetic);
/**
* Role classes cannot be private
* @param modifiers
* @return adjusted modifiers
*/
public static int checkRoleModifiers(int modifiers, TypeDeclaration type, Scope scope)
{
if (!type.isSourceRole())
return modifiers;
if ((modifiers & AccPrivate) != 0)
{
modifiers ^= AccPrivate;
if (!hasClassKindProblem(type.binding))
scope.problemReporter().illegalModifierForRole(type.binding);
} else if ((modifiers & (AccPublic | AccProtected)) == 0)
{
// default
modifiers |= AccProtected;
if (!hasClassKindProblem(type.binding))
scope.problemReporter().illegalModifierForRole(type.binding);
}
// TODO (SH): can we avoid AccTeam here?
if ((modifiers & ExtraCompilerModifiers.AccTeam) == 0)
{
if ( type.memberTypes != null
&& type.memberTypes.length > 0)
{
if (!hasClassKindProblem(type.binding))
scope.problemReporter().missingTeamForRoleWithMembers(type.binding, type.memberTypes[0]);
// avoid secondary errors
if (!type.isInterface()) { // setting AccTeam for interface would aggravate the situation
modifiers |= ExtraCompilerModifiers.AccTeam;
type.modifiers |= ExtraCompilerModifiers.AccTeam;
}
type.getTeamModel().tagBits |= TeamModel.HasClassKindProblem; // initialize
for (int i = 0; i < type.memberTypes.length; i++) {
type.memberTypes[i].modifiers |= AccRole;
type.memberTypes[i].enclosingType = type;
RoleModel roleModel = type.memberTypes[i].getRoleModel();
if (!type.memberTypes[i].isRoleFile())
roleModel.setState(ITranslationStates.STATE_ROLE_FILES_LINKED); // manually catch up this one CUD state, nothing to be done.
Dependencies.ensureRoleState(roleModel, ITranslationStates.STATE_ROLES_SPLIT);
}
}
}
if ((modifiers & AccStatic) != 0 && (modifiers & AccRole) != 0)
{
if (!hasClassKindProblem(type.binding))
scope.problemReporter().staticRole(type.binding);
}
return modifiers;
}
public static boolean hasClassKindProblem(ReferenceBinding binding) {
TeamModel model = binding._teamModel;
if (model != null && (model.tagBits & TeamModel.HasClassKindProblem) != 0)
return true;
if (binding.enclosingType() != null)
return hasClassKindProblem(binding.enclosingType());
return false;
}
public static boolean isRoleInterfaceMethod(MethodBinding inheritedMethod) {
int classModifiers = inheritedMethod.declaringClass.modifiers;
return ((classModifiers & AccSynthIfc) == AccSynthIfc);
}
/**
* @param modifiers
* @param abstractMethods
* @return are all abstract methods element of a synthetic role interface?
*/
public static boolean checkRoleIfcVisibility(int modifiers, MethodBinding[] abstractMethods) {
for (int i=0; i<abstractMethods.length; i++)
{
MethodModel model = abstractMethods[i].model;
if (model != null && model.problemDetail == ProblemDetail.RoleInheritsNonPublic)
continue; // these methods are not public either
ReferenceBinding ifc = abstractMethods[i].declaringClass;
if ((ifc.modifiers & AccSynthIfc) != AccSynthIfc)
return false;
}
return true;
}
public static boolean isAsVisible(int modifiers, int inheritedModifiers) {
// mainly stolen from MethodVerifier.isAsVisible
if ((inheritedModifiers & AccVisibilityMASK) ==
(modifiers & AccVisibilityMASK))
{
return true;
}
if ((modifiers & AccPublic) != 0) return true; // Covers everything
if ((inheritedModifiers & AccPublic) != 0) return false;
if ((modifiers & AccProtected) != 0) return true;
if ((inheritedModifiers & AccProtected) != 0) return false;
return (modifiers & AccPrivate) == 0; // The inherited thing cannot be private since it would not be visible
}
/**
* Printable representation of visibility modifiers
* @param modifiers
* @return the string representation of modifiers
*/
@SuppressWarnings("nls")
public static String toString(int modifiers) {
if ((modifiers & AccPublic) != 0) return "public";
if ((modifiers & AccProtected) != 0) return "protected";
if ((modifiers & AccPrivate) != 0) return "private";
return "<default visibility>";
}
/**
* Compute visibility of a feature from outside its class.
* Handles visibility and abstractness.
* @param classMod
* @param featureMod must not be private
* @return the combined bitset
*/
public static int combine(int classMod, int featureMod) {
int abstractness = (classMod & AccAbstract) | (featureMod & AccAbstract);
int visibility = 0;
if ((featureMod & AccPrivate) != 0) throw new InternalCompilerError("precond violated"); //$NON-NLS-1$
if ((classMod & AccPrivate) != 0) {
visibility = classMod & AccVisibilityMASK;
} else if ((classMod & (AccPublic|AccProtected|AccPrivate)) == 0) {
visibility = 0;
} else if ((classMod & AccProtected) != 0) {
if ((featureMod & (AccProtected|AccPublic)) != 0)
visibility = AccProtected;
else
visibility = 0;
} else { // class is public
visibility = featureMod & AccVisibilityMASK;
}
return visibility | abstractness;
}
/**
* For checking default visibility try to identify requiredType as a
* super-interface of startType, but never leaving requiredPackage.
* @param startType
* @param requiredType
* @param requiredPackage
* @return whether or not a supertype path could be found
*/
public static boolean findSuperIfcInPackage(
ReferenceBinding startType,
ReferenceBinding requiredType,
PackageBinding requiredPackage)
{
if (startType.fPackage != requiredPackage) return false;
if (TypeBinding.equalsEquals(startType, requiredType)) return true;
ReferenceBinding[] superIfcs = startType.superInterfaces();
if (superIfcs == null) return false;
for (int i=0; i<superIfcs.length; i++) {
if (findSuperIfcInPackage(superIfcs[i], requiredType, requiredPackage))
return true;
}
return false;
}
/**
* TODO (SH): find a better place: will we have a class Validity?
*
* Check whether other has no more enclosing types than binding.
* @param binding
* @param other
*/
public static boolean checkCompatibleEnclosingForRoles(
Scope scope,
TypeDeclaration clazz,
SourceTypeBinding binding,
ReferenceBinding other)
{
if (!binding.isRole())
return true;
if (binding.isTeam()) {
ReferenceBinding enclosing = binding.enclosingType();
while (enclosing != null) {
if (TypeBinding.equalsEquals(enclosing, other)) {
scope.problemReporter().teamExtendingEnclosing(clazz, other);
return false;
}
enclosing = enclosing.enclosingType();
}
}
ReferenceBinding otherEnclosing = other.original().enclosingType();
if (otherEnclosing == null)
return true;
ReferenceBinding thisEnclosing = binding.enclosingType();
while (thisEnclosing != null) {
if (thisEnclosing.isCompatibleWith(otherEnclosing))
return true; // good!
thisEnclosing = thisEnclosing.enclosingType();
}
scope.problemReporter().extendIncompatibleEnclosingTypes(
clazz, other, otherEnclosing);
return false;
}
/**
* Visibility control for role fields/methods. The public case is already handled outside.
*/
public static boolean canBeSeenBy(IProtectable binding, TypeBinding receiverType, InvocationSite invocationSite, Scope scope)
{
// never legal for non-public features:
if (RoleTypeBinding.isRoleWithExplicitAnchor(receiverType))
return false;
// callin methods are not subject to visibility control:
if ((binding.modifiers() & ExtraCompilerModifiers.AccCallin) != 0)
return true;
ReferenceBinding declaringClass = (ReferenceBinding) binding.getDeclaringClass().getRealClass().erasure();
ReferenceBinding invocationType = scope.enclosingSourceType();
// special privilege for tsuper calls:
if (invocationSite instanceof TSuperMessageSend && invocationType.isRole()) {
// check all tsupers, but not mixing with explicit supers
if (invocationType.roleModel.hasTSuperRole(declaringClass))
return true;
}
if (binding.isProtected()) {
// answer true if the invocationType is the declaringClass or they are in the same package
// OR the invocationType is a subclass of the declaringClass
// AND the receiverType is the invocationType or its subclass
// OR the method is a static method accessed directly through a type
// OR previous assertions are true for one of the enclosing type
if (TypeBinding.equalsEquals(invocationType, declaringClass)) return true;
// instead of package investigate the enclosing team.
if (receiverType instanceof ReferenceBinding) {
// strengthen all role types relative to the receiver type.
ReferenceBinding receiver = (ReferenceBinding)receiverType;
declaringClass = (ReferenceBinding)TeamModel.strengthenRoleType(receiver, declaringClass);
if (invocationType.isRole())
invocationType = (ReferenceBinding)TeamModel.strengthenRoleType(receiver, invocationType);
}
// START orig code from FieldBinding:
ReferenceBinding currentType = invocationType;
int depth = 0;
do {
/*OT:*/ if (TeamModel.isTeamContainingRole(currentType, declaringClass))
/*OT:*/ return true;
if (declaringClass.isSuperclassOf(currentType)) {
if (invocationSite.isSuperAccess()){
return true;
}
// receiverType can be an array binding in one case... see if you can change it
if (receiverType instanceof ArrayBinding){
return false;
}
if (binding.isStatic()){
if (depth > 0) invocationSite.setDepth(depth);
return true; // see 1FMEPDL - return invocationSite.isTypeAccess();
}
if (TypeBinding.equalsEquals(currentType, receiverType) || currentType.isSuperclassOf((ReferenceBinding)receiverType)){
if (depth > 0) invocationSite.setDepth(depth);
return true;
}
}
depth++;
currentType = currentType.enclosingType();
} while (currentType != null);
return false;
}
// END orig code
// default and private can be accessed from any nested class:
// only invocationType is allowed to be nested within declaringClass,
// not vice versa:
ReferenceBinding currentInvocationType = invocationType;
while(currentInvocationType != null) {
if (TypeBinding.equalsEquals(currentInvocationType, declaringClass))
return true;
currentInvocationType = currentInvocationType.enclosingType();
}
// no more hope for private
if (binding.isPrivate())
return false;
// default visibility (similar to FieldBinding.canBeSeenBy, whith less emphasis on packages):
// receiverType can be an array binding in one case... see if you can change it
if (receiverType instanceof ArrayBinding)
return false;
// have access to all super features:
// (implicit inheritance is already dealt with by copy inheritance ;-) )
if (invocationSite.isSuperAccess())
invocationType = invocationType.superclass();
ReferenceBinding currentType = (ReferenceBinding) (invocationType.isInterface()
?((ReferenceBinding)receiverType).getRealType().erasure()
:((ReferenceBinding)receiverType).getRealClass().erasure());
PackageBinding declaringPackage = declaringClass.fPackage;
do {
if (TypeBinding.equalsEquals(invocationType, currentType)) return true;
if ( !currentType.isRole() // when leaving team contexts ...
&& declaringPackage != currentType.fPackage) // ... compare the packages instead.
return false;
} while ((currentType = currentType.superclass()) != null);
return false;
}
}