| /******************************************************************************* |
| * Copyright (c) 2009, 2015 Google, Inc and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Sergey Prigogin (Google) - initial API and implementation |
| * Markus Schorn (Wind River Systems) |
| *******************************************************************************/ |
| package org.eclipse.cdt.internal.core.dom.parser.cpp.semantics; |
| |
| import org.eclipse.cdt.core.CCorePlugin; |
| import org.eclipse.cdt.core.dom.ast.DOMException; |
| import org.eclipse.cdt.core.dom.ast.IASTName; |
| import org.eclipse.cdt.core.dom.ast.IASTNode; |
| import org.eclipse.cdt.core.dom.ast.IBinding; |
| import org.eclipse.cdt.core.dom.ast.ICompositeType; |
| import org.eclipse.cdt.core.dom.ast.IProblemBinding; |
| import org.eclipse.cdt.core.dom.ast.IType; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPAliasTemplateInstance; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassScope; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassTemplatePartialSpecialization; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunction; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPMember; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPScope; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter; |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration; |
| import org.eclipse.cdt.core.parser.util.ArrayUtil; |
| import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClosureType; |
| import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper; |
| import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPDeferredClassInstance; |
| import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalUnknownScope; |
| import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownType; |
| |
| /** |
| * The context that determines access to private and protected class members. |
| */ |
| public class AccessContext { |
| private static final int v_private = ICPPMember.v_private; |
| private static final int v_protected = ICPPMember.v_protected; |
| public static final int v_public = ICPPMember.v_public; |
| |
| /** |
| * Checks if a binding is accessible from a given name. |
| * |
| * @param binding A binding to check access for. |
| * @param from A name corresponding to the binding. |
| * @return {@code true} if the binding is accessible. |
| */ |
| public static boolean isAccessible(IBinding binding, IASTName from) { |
| CPPSemantics.pushLookupPoint(from); |
| try { |
| return new AccessContext(from).isAccessible(binding); |
| } finally { |
| CPPSemantics.popLookupPoint(); |
| } |
| } |
| |
| /** |
| * Checks if a binding is accessible from a given name. |
| * |
| * @param binding A binding to check access for. |
| * @param bindingVisibility visibility of the binding in the containing composite type. |
| * Used instead of calling {@link ICPPMember#getVisibility()}. |
| * @param from A name corresponding to the binding. |
| * @return {@code true} if the binding is accessible. |
| */ |
| public static boolean isAccessible(IBinding binding, int bindingVisibility, IASTName from) { |
| CPPSemantics.pushLookupPoint(from); |
| try { |
| return new AccessContext(from).isAccessible(binding, bindingVisibility); |
| } finally { |
| CPPSemantics.popLookupPoint(); |
| } |
| } |
| |
| private final IASTName name; |
| /** |
| * A chain of nested classes or/and a function that determine accessibility of private/protected |
| * members by participating in friendship or class inheritance relationships. If both, classes |
| * and a function are present in the context, the outermost class has to be local to |
| * the function. |
| * {@link "http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#45"} |
| */ |
| private IBinding[] context; |
| /** |
| * A class through which the bindings are accessed (11.2.4). |
| */ |
| private boolean isUnqualifiedLookup; |
| private boolean isPrefixLookup; |
| private ICPPClassType namingClass; // Depends on the binding for which we check the access. |
| // The first candidate is independent of the binding for which we do the access-check. |
| private ICPPClassType firstCandidateForNamingClass; |
| private DOMException initializationException; |
| |
| public AccessContext(IASTName name) { |
| this(name, false); |
| } |
| |
| public AccessContext(IASTName name, boolean prefixLookup) { |
| this.name = name; |
| this.isPrefixLookup = prefixLookup; |
| } |
| |
| /** |
| * Checks if a binding is accessible in a given context. |
| * |
| * @param binding A binding to check access for. |
| * @return {@code true} if the binding is accessible. |
| */ |
| public boolean isAccessible(IBinding binding) { |
| if (binding instanceof ICPPTemplateParameter) |
| return true; |
| |
| int bindingVisibility; |
| if (binding instanceof ICPPMember) { |
| bindingVisibility = ((ICPPMember) binding).getVisibility(); |
| } else { |
| while (binding instanceof ICPPSpecialization) { |
| binding = ((ICPPSpecialization) binding).getSpecializedBinding(); |
| } |
| if (binding instanceof ICPPClassTemplatePartialSpecialization) { |
| // A class template partial specialization requires its primary |
| // template to be visible |
| if (!isAccessible( |
| ((ICPPClassTemplatePartialSpecialization) binding).getPrimaryClassTemplate())) |
| return false; |
| } |
| if (binding instanceof ICPPAliasTemplateInstance) { |
| binding = ((ICPPAliasTemplateInstance) binding).getTemplateDefinition(); |
| } |
| IBinding owner = binding.getOwner(); |
| if (owner instanceof CPPClosureType) |
| return true; |
| if (owner instanceof ICPPClassType) { |
| bindingVisibility = ((ICPPClassType) owner).getVisibility(binding); |
| } else { |
| bindingVisibility = v_public; |
| } |
| } |
| return isAccessible(binding, bindingVisibility); |
| } |
| |
| /** |
| * Checks if a binding is accessible in a given context. |
| * |
| * @param binding A binding to check access for. |
| * @param bindingVisibility visibility of the binding in the containing composite type. |
| * Used instead of calling {@link ICPPMember#getVisibility()}. |
| * @return {@code true} if the binding is accessible. |
| */ |
| public boolean isAccessible(IBinding binding, int bindingVisibility) { |
| IBinding owner; |
| while ((owner = binding.getOwner()) instanceof ICompositeType && |
| ((ICompositeType) owner).isAnonymous()) { |
| binding = owner; |
| } |
| if (!(owner instanceof ICPPClassType)) { |
| return true; // The binding is not a class member. |
| } |
| if (!initialize()) { |
| return true; // Assume visibility if anything goes wrong. |
| } |
| ICPPClassType accessOwner= (ICPPClassType) owner; |
| namingClass = getNamingClass(accessOwner); |
| if (namingClass == null) { |
| return true; |
| } |
| return isAccessible(binding, bindingVisibility, accessOwner, namingClass, |
| v_public, 0); |
| } |
| |
| /** |
| * @return {@code true} if initialization succeeded. |
| */ |
| private boolean initialize() { |
| if (context == null) { |
| if (initializationException != null) { |
| return false; |
| } |
| try { |
| context = getContext(name); |
| firstCandidateForNamingClass= getFirstCandidateForNamingClass(name); |
| } catch (DOMException e) { |
| CCorePlugin.log(e); |
| initializationException = e; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Return true if 'c' is the same type as 'target', or a specialization of 'target'. |
| private static boolean isSameTypeOrSpecialization(ICPPClassType c, ICPPClassType target) { |
| if (!(c instanceof ICPPSpecialization)) { |
| while (target instanceof ICPPSpecialization) { |
| IBinding specialized = ((ICPPSpecialization) target).getSpecializedBinding(); |
| if (specialized instanceof ICPPClassType) { |
| target = (ICPPClassType) specialized; |
| } |
| } |
| } |
| return c.isSameType(target); |
| } |
| |
| private boolean isAccessible(IBinding binding, int bindingVisibility, ICPPClassType owner, |
| ICPPClassType derivedClass, int accessLevel, int depth) { |
| if (depth > CPPSemantics.MAX_INHERITANCE_DEPTH) |
| return false; |
| |
| accessLevel = getMemberAccessLevel(derivedClass, accessLevel); |
| |
| if (isSameTypeOrSpecialization(owner, derivedClass)) { |
| return isAccessible(bindingVisibility, accessLevel); |
| } |
| |
| ICPPUsingDeclaration[] usingDecls = derivedClass.getUsingDeclarations(); |
| for (ICPPUsingDeclaration decl : usingDecls) { |
| for (IBinding delegate : decl.getDelegates()) { |
| if (delegate.equals(binding)) { |
| derivedClass.getVisibility(decl); |
| return isAccessible(derivedClass.getVisibility(decl), accessLevel); |
| } |
| } |
| } |
| |
| ICPPBase[] bases = derivedClass.getBases(); |
| if (bases != null) { |
| for (ICPPBase base : bases) { |
| IBinding baseBinding = base.getBaseClass(); |
| if (baseBinding instanceof ICPPDeferredClassInstance) { |
| // Support content assist for members of deferred instances. |
| baseBinding= ((ICPPDeferredClassInstance) baseBinding).getTemplateDefinition(); |
| } |
| if (!(baseBinding instanceof ICPPClassType)) { |
| continue; |
| } |
| if (!isAccessible(base.getVisibility(), accessLevel)) { |
| continue; |
| } |
| if (isAccessible(binding, bindingVisibility, owner, |
| (ICPPClassType) baseBinding, accessLevel == v_private ? v_protected : accessLevel, depth + 1)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns access level to the members of a class. |
| * |
| * @param classType A class |
| * @param inheritedAccessLevel Access level inherited from derived class. |
| * One of: v_public, v_protected, v_private. |
| * @return One of: v_public, v_protected, v_private. |
| */ |
| private int getMemberAccessLevel(ICPPClassType classType, int inheritedAccessLevel) { |
| int accessLevel = inheritedAccessLevel; |
| for (IBinding contextBinding : context) { |
| if (ClassTypeHelper.isFriend(contextBinding, classType)) |
| return v_private; |
| |
| if (accessLevel == v_public && contextBinding instanceof ICPPClassType) { |
| ICPPClassType contextClass = (ICPPClassType) contextBinding; |
| if (isAccessibleBaseClass(classType, contextClass, 0)) |
| accessLevel = v_protected; |
| } |
| } |
| return accessLevel; |
| } |
| |
| private boolean isAccessibleBaseClass(ICPPClassType classType, ICPPClassType derived, int depth) { |
| if (depth > CPPSemantics.MAX_INHERITANCE_DEPTH) |
| return false; |
| |
| if (derived.isSameType(classType)) |
| return true; |
| |
| ICPPBase[] bases = derived.getBases(); |
| if (bases != null) { |
| for (ICPPBase base : bases) { |
| IBinding baseClass = base.getBaseClass(); |
| if (baseClass instanceof ICPPDeferredClassInstance) { |
| baseClass = ((ICPPDeferredClassInstance) baseClass).getTemplateDefinition(); |
| } |
| if (!(baseClass instanceof ICPPClassType)) { |
| continue; |
| } |
| if (depth > 0 && !isAccessible(base.getVisibility(), v_protected)) { |
| continue; |
| } |
| if (isAccessibleBaseClass(classType, (ICPPClassType) baseClass, depth + 1)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private ICPPClassType getFirstCandidateForNamingClass(IASTName name) throws DOMException { |
| LookupData data = new LookupData(name); |
| isUnqualifiedLookup= !data.qualified; |
| |
| ICPPScope scope = CPPSemantics.getLookupScope(name); |
| while (scope != null && !(scope instanceof ICPPClassScope)) { |
| if (scope instanceof ICPPInternalUnknownScope) { |
| IType scopeType = ((ICPPInternalUnknownScope) scope).getScopeType(); |
| if (scopeType instanceof ICPPUnknownType && isPrefixLookup) { |
| scopeType = HeuristicResolver.resolveUnknownType((ICPPUnknownType) scopeType); |
| if (scopeType instanceof ICPPClassType) { |
| return (ICPPClassType) scopeType; |
| } |
| } |
| if (scopeType instanceof ICPPDeferredClassInstance) { |
| return ((ICPPDeferredClassInstance) scopeType).getClassTemplate(); |
| } |
| } |
| |
| scope = CPPSemantics.getParentScope(scope, data.getTranslationUnit()); |
| } |
| if (scope instanceof ICPPClassScope) { |
| return ((ICPPClassScope) scope).getClassType(); |
| } |
| return null; |
| } |
| |
| private ICPPClassType getNamingClass(ICPPClassType accessOwner) { |
| ICPPClassType classType = firstCandidateForNamingClass; |
| if (classType != null && isUnqualifiedLookup) { |
| IBinding owner = classType.getOwner(); |
| while (owner instanceof ICPPClassType && |
| !derivesFrom(classType, accessOwner, name, CPPSemantics.MAX_INHERITANCE_DEPTH)) { |
| classType= (ICPPClassType) owner; |
| owner= classType.getOwner(); |
| } |
| } |
| return classType; |
| } |
| |
| private static boolean derivesFrom(ICPPClassType derived, ICPPClassType target, IASTNode point, |
| int maxdepth) { |
| if (derived == target || derived.isSameType(target)) { |
| return true; |
| } |
| if (maxdepth > 0) { |
| for (ICPPBase cppBase : derived.getBases()) { |
| IBinding base = cppBase.getBaseClass(); |
| if (!(target instanceof ICPPSpecialization)) { |
| while (base instanceof ICPPSpecialization) { |
| base = ((ICPPSpecialization) base).getSpecializedBinding(); |
| } |
| } |
| if (base instanceof ICPPClassType) { |
| ICPPClassType tbase = (ICPPClassType) base; |
| if (tbase.isSameType(target)) { |
| return true; |
| } |
| if (derivesFrom(tbase, target, point, maxdepth - 1)) |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private static IBinding[] getContext(IASTName name) { |
| IBinding[] accessibilityContext = IBinding.EMPTY_BINDING_ARRAY; |
| for (IBinding binding = CPPVisitor.findEnclosingFunctionOrClass(name); |
| binding != null; binding = binding.getOwner()) { |
| if (binding instanceof ICPPMethod || |
| // Definition of an undeclared method. |
| binding instanceof IProblemBinding && |
| ((IProblemBinding) binding).getID() == IProblemBinding.SEMANTIC_MEMBER_DECLARATION_NOT_FOUND) { |
| continue; |
| } |
| if (binding instanceof ICPPFunction || binding instanceof ICPPClassType) { |
| accessibilityContext = ArrayUtil.append(accessibilityContext, binding); |
| } |
| } |
| return ArrayUtil.trim(accessibilityContext); |
| } |
| |
| /** |
| * Checks if objects with the given visibility are accessible at the given access level. |
| * |
| * @param visibility one of: v_public, v_protected, v_private. |
| * @param accessLevel one of: v_public, v_protected, v_private. |
| * @return {@code true} if the access level is sufficiently high. |
| */ |
| private static boolean isAccessible(int visibility, int accessLevel) { |
| // Note the ordering of numeric visibility values: v_public < v_protected < v_private. |
| return accessLevel >= visibility; |
| } |
| } |