/******************************************************************************* * Copyright (c) 2005, 2015 IBM Corporation 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: * IBM - Initial API and implementation * Markus Schorn (Wind River Systems) * Bryan Wilkinson (QNX) * Andrew Ferguson (Symbian) * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.dom.parser.cpp; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.dom.IName; import org.eclipse.cdt.core.dom.ast.DOMException; import org.eclipse.cdt.core.dom.ast.EScopeKind; import org.eclipse.cdt.core.dom.ast.IASTName; import org.eclipse.cdt.core.dom.ast.IASTNode; import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IProblemBinding; import org.eclipse.cdt.core.dom.ast.IScope; import org.eclipse.cdt.core.dom.ast.IType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase; import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassSpecialization; 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.ICPPConstructor; import org.eclipse.cdt.core.dom.ast.cpp.ICPPField; import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace; import org.eclipse.cdt.core.dom.ast.cpp.ICPPParameter; import org.eclipse.cdt.core.dom.ast.cpp.ICPPParameterPackType; import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameterMap; import org.eclipse.cdt.core.index.IIndexFileSet; import org.eclipse.cdt.core.parser.util.ArrayUtil; import org.eclipse.cdt.core.parser.util.CharArrayUtils; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPTemplates; import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.SemanticUtil; /** * Base class for all specialization scopes * For safe usage in index bindings, all fields need to be final or volatile. */ public class AbstractCPPClassSpecializationScope implements ICPPClassSpecializationScope { private final ICPPClassSpecialization specialClass; // The following fields are used by the PDOM bindings and need to be volatile. private volatile ICPPBase[] fBases; private volatile ICPPMethod[] ownInheritedConstructors; private final ThreadLocal<Boolean> fComputingBases = new ThreadLocal<Boolean>() { @Override protected Boolean initialValue() { return false; } }; public AbstractCPPClassSpecializationScope(ICPPClassSpecialization specialization) { this.specialClass= specialization; } @Override public ICPPClassType getOriginalClassType() { return specialClass.getSpecializedBinding(); } @Override public final IBinding getBinding(IASTName name, boolean resolve) { return getBinding(name, resolve, IIndexFileSet.EMPTY); } @Override public final IBinding[] getBindings(IASTName name, boolean resolve, boolean prefix) { return getBindings(new ScopeLookupData(name, resolve, prefix)); } @Override public IBinding getBinding(IASTName name, boolean resolve, IIndexFileSet fileSet) { char[] c = name.getLookupKey(); if (CharArrayUtils.equals(c, specialClass.getNameCharArray()) && !CPPClassScope.shallReturnConstructors(name, false)) { return specialClass; } ICPPClassType specialized = specialClass.getSpecializedBinding(); IScope classScope = specialized.getCompositeScope(); IBinding[] bindings = classScope != null ? classScope.getBindings(new ScopeLookupData(name, resolve, false)) : null; if (bindings == null) return null; IBinding[] specs = IBinding.EMPTY_BINDING_ARRAY; for (IBinding binding : bindings) { specs = ArrayUtil.append(specs, specialClass.specializeMember(binding, name)); } specs = ArrayUtil.trim(specs); return CPPSemantics.resolveAmbiguities(name, specs); } @Deprecated @Override final public IBinding[] getBindings(IASTName name, boolean resolve, boolean prefixLookup, IIndexFileSet fileSet) { return getBindings(new ScopeLookupData(name, resolve, prefixLookup)); } @Override final public IBinding[] getBindings(ScopeLookupData lookup) { ICPPClassType specialized = specialClass.getSpecializedBinding(); IScope classScope = specialized.getCompositeScope(); if (classScope == null) return IBinding.EMPTY_BINDING_ARRAY; IBinding[] bindings= classScope.getBindings(lookup); IBinding[] result= IBinding.EMPTY_BINDING_ARRAY; int n = 0; for (IBinding binding : bindings) { if (binding == specialized || (binding instanceof ICPPClassType && areSameTypesModuloPartialSpecialization(specialized, (IType) binding))) { binding= specialClass; } else { binding= specialClass.specializeMember(binding, lookup.getLookupPoint()); } if (binding != null) result = ArrayUtil.appendAt(result, n++, binding); } return ArrayUtil.trim(result, n); } private static boolean areSameTypesModuloPartialSpecialization(IType type1, IType type2) { while (type1 instanceof ICPPClassTemplatePartialSpecialization) { type1 = ((ICPPClassTemplatePartialSpecialization) type1).getPrimaryClassTemplate(); } while (type2 instanceof ICPPClassTemplatePartialSpecialization) { type2 = ((ICPPClassTemplatePartialSpecialization) type2).getPrimaryClassTemplate(); } return type1.isSameType(type2); } @Override public ICPPClassSpecialization getClassType() { return specialClass; } @Override public ICPPBase[] getBases(IASTNode point) { if (fBases == null) { if (fComputingBases.get()) { return ICPPBase.EMPTY_BASE_ARRAY; // avoid recursion } fComputingBases.set(true); try { ICPPBase[] result = ICPPBase.EMPTY_BASE_ARRAY; ICPPBase[] bases = ClassTypeHelper.getBases(specialClass.getSpecializedBinding(), point); if (bases.length == 0) { fBases= bases; } else { final ICPPTemplateParameterMap tpmap = specialClass.getTemplateParameterMap(); for (ICPPBase base : bases) { IType baseType = base.getBaseClassType(); if (baseType instanceof ICPPParameterPackType) { IType[] specClasses= CPPTemplates.instantiateTypes(new IType[] { baseType }, new InstantiationContext(tpmap, specialClass, point)); if (specClasses.length == 1 && specClasses[0] instanceof ICPPParameterPackType) { result= ArrayUtil.append(result, base); } else { for (IType specClass : specClasses) { ICPPBase specBase = base.clone(); specClass = SemanticUtil.getUltimateType(specClass, false); if (specClass instanceof IBinding && !(specClass instanceof IProblemBinding)) { specBase.setBaseClass((IBinding) specClass); result = ArrayUtil.append(result, specBase); } } } } else if (baseType != null) { ICPPBase specBase = base.clone(); ICPPClassSpecialization specializationContext = specialClass; IBinding owner = specialClass.getOwner(); if (owner instanceof ICPPClassSpecialization) { specializationContext = (ICPPClassSpecialization) owner; } IType specClass= CPPTemplates.instantiateType(baseType, new InstantiationContext(tpmap, specializationContext, point)); specClass = SemanticUtil.getUltimateType(specClass, false); if (specClass instanceof IBinding && !(specClass instanceof IProblemBinding)) { specBase.setBaseClass((IBinding) specClass); } result = ArrayUtil.append(result, specBase); } } result= ArrayUtil.trim(result); fBases= result; return result; } } finally { fComputingBases.set(false); } } return fBases; } @SuppressWarnings("unchecked") private <T extends IBinding> T[] specializeMembers(T[] array, IASTNode point) { if (array == null || array.length == 0) return array; T[] newArray= array.clone(); for (int i = 0; i < newArray.length; i++) { IBinding specializedMember = specialClass.specializeMember(array[i], point); newArray[i]= (T) specializedMember; } return newArray; } @Override public ICPPField[] getDeclaredFields(IASTNode point) { ICPPField[] fields= ClassTypeHelper.getDeclaredFields(specialClass.getSpecializedBinding(), point); return specializeMembers(fields, point); } @Override public ICPPMethod[] getImplicitMethods() { CCorePlugin.log(new Exception("Unsafe method call. Instantiation of dependent expressions may not work.")); //$NON-NLS-1$ return getImplicitMethods(null); } @Override public ICPPMethod[] getImplicitMethods(IASTNode point) { ICPPClassType origClass = specialClass.getSpecializedBinding(); ICPPMethod[] methods= ClassTypeHelper.getImplicitMethods(origClass, point); ICPPMethod[] specializedMembers = specializeMembers(methods, point); // Add inherited constructors. ICPPMethod[] inheritedConstructors = getOwnInheritedConstructors(point); return ArrayUtil.addAll(specializedMembers, inheritedConstructors); } @Override public IName getScopeName() { if (specialClass instanceof ICPPInternalBinding) return (IASTName) ((ICPPInternalBinding) specialClass).getDefinition(); return null; } @Override public ICPPConstructor[] getConstructors() { CCorePlugin.log(new Exception("Unsafe method call. Instantiation of dependent expressions may not work.")); //$NON-NLS-1$ return getConstructors(null); } @Override public ICPPConstructor[] getConstructors(IASTNode point) { ICPPConstructor[] ctors= ClassTypeHelper.getConstructors(specialClass.getSpecializedBinding(), point); ICPPConstructor[] specializedCtors = specializeMembers(ctors, point); // Add inherited constructors. ICPPMethod[] inheritedConstructors = getOwnInheritedConstructors(specializedCtors, point); return ArrayUtil.addAll(specializedCtors, inheritedConstructors); } /** * Returns the inherited constructors that are not specializations of the inherited constructors * of the specialized class. */ private ICPPMethod[] getOwnInheritedConstructors(ICPPConstructor[] existingConstructors, IASTNode point) { if (ownInheritedConstructors == null) { if (!hasInheritedConstructorsSources(point)) return ICPPMethod.EMPTY_CPPMETHOD_ARRAY; IType[][] existingConstructorParamTypes = new IType[existingConstructors.length][]; for (int i = 0; i < existingConstructors.length; i++) { ICPPParameter[] params = existingConstructors[i].getParameters(); IType[] types = new IType[params.length]; for (int j = 0; j < params.length; j++) { types[j] = params[j].getType(); } existingConstructorParamTypes[i] = types; } ICPPMethod[] constructors = ClassTypeHelper.getInheritedConstructors(this, getBases(point), existingConstructorParamTypes, point); ownInheritedConstructors = constructors; } return ownInheritedConstructors; } private ICPPMethod[] getOwnInheritedConstructors(IASTNode point) { if (ownInheritedConstructors != null) return ownInheritedConstructors; ICPPConstructor[] ctors= ClassTypeHelper.getConstructors(specialClass.getSpecializedBinding(), point); ICPPConstructor[] specializedCtors = specializeMembers(ctors, point); return getOwnInheritedConstructors(specializedCtors, point); } private boolean hasInheritedConstructorsSources(IASTNode point) { for (ICPPBase base : getBases(point)) { if (base.isInheritedConstructorsSource()) return true; } return false; } @Override public ICPPMethod[] getDeclaredMethods(IASTNode point) { ICPPMethod[] bindings = ClassTypeHelper.getDeclaredMethods(specialClass.getSpecializedBinding(), point); return specializeMembers(bindings, point); } @Override public ICPPClassType[] getNestedClasses(IASTNode point) { ICPPClassType[] bindings = ClassTypeHelper.getNestedClasses(specialClass.getSpecializedBinding(), point); return specializeMembers(bindings, point); } @Override public IBinding[] getFriends(IASTNode point) { IBinding[] friends = ClassTypeHelper.getFriends(specialClass.getSpecializedBinding(), point); return specializeMembers(friends, point); } @Override public IScope getParent() throws DOMException { IBinding binding= specialClass.getOwner(); if (binding instanceof ICPPClassType) { return ((ICPPClassType) binding).getCompositeScope(); } if (binding instanceof ICPPNamespace) { return ((ICPPNamespace) binding).getNamespaceScope(); } return getOriginalClassType().getScope(); } @Override public IBinding[] find(String name, IASTTranslationUnit tu) { return find(name); } @Override public IBinding[] find(String name) { return CPPSemantics.findBindings(this, name, false); } @Override public EScopeKind getKind() { return EScopeKind.eClassType; } @Override public String toString() { IName name = getScopeName(); return name != null ? name.toString() : String.valueOf(specialClass); } // Note: equals() and hashCode() are overridden because multiple instances // of this class representing the same class specialization scope // may be created, but scopes are sometimes stored in hash maps // under the assumption that two objects representing the same // scope will compare equal(). @Override public boolean equals(Object other) { if (other instanceof ICPPClassSpecializationScope) { return getClassType().equals(((ICPPClassSpecializationScope) other).getClassType()); } return false; } @Override public int hashCode() { return specialClass.hashCode(); } }