blob: 9e9364d6c56d088ed0f30898d6190225b4970706 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2015 QNX Software Systems and others.
*
* 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
*
* Contributors:
* QNX - Initial API and implementation
* Andrew Ferguson (Symbian)
* Markus Schorn (Wind River Systems)
* Sergey Prigogin (Google)
* Thomas Corbat (IFS)
*******************************************************************************/
package org.eclipse.cdt.internal.core.pdom.dom.cpp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IPDOMVisitor;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.ITypedef;
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.ICPPClassSpecialization;
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.ICPPSpecialization;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPUsingDeclaration;
import org.eclipse.cdt.core.parser.util.ObjectMap;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClassSpecialization;
import org.eclipse.cdt.internal.core.dom.parser.cpp.CPPClassSpecialization.RecursionResolvingBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ClassTypeHelper;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPClassSpecializationScope;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPTemplates;
import org.eclipse.cdt.internal.core.index.IIndexCPPBindingConstants;
import org.eclipse.cdt.internal.core.pdom.dom.IPDOMMemberOwner;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode;
import org.eclipse.core.runtime.CoreException;
/**
* @author Bryan Wilkinson
*/
class PDOMCPPClassSpecialization extends PDOMCPPSpecialization
implements ICPPClassSpecialization, IPDOMMemberOwner, IPDOMCPPClassType {
private static final int FIRST_BASE = PDOMCPPSpecialization.RECORD_SIZE + 0;
private static final int MEMBERLIST = FIRST_BASE + 4;
private static final int FLAGS = MEMBERLIST + PDOMCPPMemberBlock.RECORD_SIZE; // byte
/**
* The size in bytes of a PDOMCPPClassSpecialization record in the database.
*/
@SuppressWarnings("hiding")
protected static final int RECORD_SIZE = FLAGS + 1;
private static final byte FLAGS_FINAL = 0x01;
private static final byte FLAGS_HAS_OWN_SCOPE = 0x02;
private static final byte FLAGS_NODISCARD = 0x03;
private volatile ICPPClassScope fScope;
private ObjectMap specializationMap; // Obtained from the synchronized PDOM cache.
private final ThreadLocal<Set<IBinding>> fInProgress = new ThreadLocal<>() {
@Override
protected Set<IBinding> initialValue() {
return new HashSet<>();
}
};
public PDOMCPPClassSpecialization(PDOMCPPLinkage linkage, PDOMNode parent, ICPPClassType classType,
PDOMBinding specialized) throws CoreException {
super(linkage, parent, (ICPPSpecialization) classType, specialized);
setFlags(classType);
}
public PDOMCPPClassSpecialization(PDOMLinkage linkage, long bindingRecord) {
super(linkage, bindingRecord);
}
@Override
public void update(PDOMLinkage linkage, IBinding newBinding) throws CoreException {
if (newBinding instanceof ICPPClassType) {
ICPPClassType classType = (ICPPClassType) newBinding;
setFlags(classType);
super.update(linkage, newBinding);
}
}
@Override
protected int getRecordSize() {
return RECORD_SIZE;
}
@Override
public int getNodeType() {
return IIndexCPPBindingConstants.CPP_CLASS_SPECIALIZATION;
}
@Override
public ICPPClassType getSpecializedBinding() {
return (ICPPClassType) super.getSpecializedBinding();
}
@Override
public IBinding specializeMember(IBinding original) {
if (specializationMap == null) {
final Long key = record + PDOMCPPLinkage.CACHE_INSTANCE_SCOPE;
Object cached = getPDOM().getCachedResult(key);
if (cached != null) {
specializationMap = (ObjectMap) cached;
} else {
final ObjectMap newMap = new ObjectMap(2);
try {
PDOMClassUtil.NestedClassCollector visitor = new PDOMClassUtil.NestedClassCollector();
PDOMCPPClassScope.acceptViaCache(this, visitor, false);
final ICPPClassType[] nested = visitor.getNestedClasses();
for (ICPPClassType classType : nested) {
if (classType instanceof ICPPSpecialization) {
newMap.put(((ICPPSpecialization) classType).getSpecializedBinding(), classType);
}
}
} catch (CoreException e) {
CCorePlugin.log(e);
}
specializationMap = (ObjectMap) getPDOM().putCachedResult(key, newMap, false);
}
}
synchronized (specializationMap) {
IBinding result = (IBinding) specializationMap.get(original);
if (result != null)
return result;
}
IBinding newSpec;
Set<IBinding> recursionProtectionSet = fInProgress.get();
if (!recursionProtectionSet.add(original))
return RecursionResolvingBinding.createFor(original);
try {
newSpec = CPPTemplates.createSpecialization(this, original);
} finally {
recursionProtectionSet.remove(original);
}
synchronized (specializationMap) {
IBinding oldSpec = (IBinding) specializationMap.put(original, newSpec);
if (oldSpec != null) {
specializationMap.put(original, oldSpec);
return oldSpec;
}
}
return newSpec;
}
@Override
@Deprecated
public IBinding specializeMember(IBinding original, IASTNode point) {
return specializeMember(original);
}
@Override
public ICPPClassScope getCompositeScope() {
if (fScope == null) {
try {
if (hasOwnScope()) {
fScope = new PDOMCPPClassScope(this);
return fScope;
}
} catch (CoreException e) {
}
fScope = new PDOMCPPClassSpecializationScope(this);
}
return fScope;
}
public PDOMCPPBase getFirstBase() throws CoreException {
long rec = getDB().getRecPtr(record + FIRST_BASE);
return rec != 0 ? new PDOMCPPBase(getLinkage(), rec) : null;
}
private void setFirstBase(PDOMCPPBase base) throws CoreException {
long rec = base != null ? base.getRecord() : 0;
getDB().putRecPtr(record + FIRST_BASE, rec);
}
public void addBases(PDOMName classDefName, ICPPBase[] bases) throws CoreException {
getPDOM().removeCachedResult(record + PDOMCPPLinkage.CACHE_BASES);
final PDOMLinkage linkage = getLinkage();
PDOMCPPBase firstBase = getFirstBase();
for (ICPPBase base : bases) {
PDOMCPPBase nextBase = new PDOMCPPBase(linkage, base, classDefName);
nextBase.setNextBase(firstBase);
firstBase = nextBase;
}
setFirstBase(firstBase);
}
public void removeBases(PDOMName classDefName) throws CoreException {
getPDOM().removeCachedResult(record + PDOMCPPLinkage.CACHE_BASES);
PDOMCPPBase base = getFirstBase();
PDOMCPPBase predecessor = null;
long nameRec = classDefName.getRecord();
boolean deleted = false;
while (base != null) {
PDOMCPPBase nextBase = base.getNextBase();
long classDefRec = getDB().getRecPtr(base.getRecord() + PDOMCPPBase.CLASS_DEFINITION);
if (classDefRec == nameRec) {
deleted = true;
base.delete();
} else if (deleted) {
deleted = false;
if (predecessor == null) {
setFirstBase(base);
} else {
predecessor.setNextBase(base);
}
predecessor = base;
}
base = nextBase;
}
if (deleted) {
if (predecessor == null) {
setFirstBase(null);
} else {
predecessor.setNextBase(null);
}
}
}
@Override
public ICPPBase[] getBases() {
IScope scope = getCompositeScope();
if (scope instanceof ICPPClassSpecializationScope) {
return ((ICPPClassSpecializationScope) scope).getBases();
}
// This is an explicit specialization.
Long key = record + PDOMCPPLinkage.CACHE_BASES;
ICPPBase[] bases = (ICPPBase[]) getPDOM().getCachedResult(key);
if (bases != null)
return bases;
try {
List<PDOMCPPBase> list = new ArrayList<>();
for (PDOMCPPBase base = getFirstBase(); base != null; base = base.getNextBase()) {
list.add(base);
}
Collections.reverse(list);
bases = list.toArray(new ICPPBase[list.size()]);
getPDOM().putCachedResult(key, bases);
return bases;
} catch (CoreException e) {
CCorePlugin.log(e);
}
return ICPPBase.EMPTY_BASE_ARRAY;
}
@Override
@Deprecated
public ICPPBase[] getBases(IASTNode point) {
return getBases();
}
@Override
public ICPPConstructor[] getConstructors() {
IScope scope = getCompositeScope();
if (scope instanceof ICPPClassSpecializationScope) {
ICPPConstructor[] constructors = ((ICPPClassSpecializationScope) scope).getConstructors();
return ClassTypeHelper.getAllConstructors(this, constructors);
}
try {
PDOMClassUtil.ConstructorCollector visitor = new PDOMClassUtil.ConstructorCollector();
PDOMCPPClassScope.acceptViaCache(this, visitor, false);
ICPPConstructor[] constructors = visitor.getConstructors();
return ClassTypeHelper.getAllConstructors(this, constructors);
} catch (CoreException e) {
CCorePlugin.log(e);
return ICPPConstructor.EMPTY_CONSTRUCTOR_ARRAY;
}
}
@Override
@Deprecated
public ICPPConstructor[] getConstructors(IASTNode point) {
return getConstructors();
}
@Override
public ICPPMethod[] getDeclaredMethods() {
IScope scope = getCompositeScope();
if (scope instanceof ICPPClassSpecializationScope) {
return ((ICPPClassSpecializationScope) scope).getDeclaredMethods();
}
try {
PDOMClassUtil.MethodCollector methods = new PDOMClassUtil.MethodCollector(false);
PDOMCPPClassScope.acceptViaCache(this, methods, false);
return methods.getMethods();
} catch (CoreException e) {
CCorePlugin.log(e);
return ICPPMethod.EMPTY_CPPMETHOD_ARRAY;
}
}
@Override
@Deprecated
public ICPPMethod[] getDeclaredMethods(IASTNode point) {
return getDeclaredMethods();
}
@Override
public ICPPField[] getDeclaredFields() {
IScope scope = getCompositeScope();
if (scope instanceof ICPPClassSpecializationScope) {
return ((ICPPClassSpecializationScope) scope).getDeclaredFields();
}
try {
PDOMClassUtil.FieldCollector visitor = new PDOMClassUtil.FieldCollector();
PDOMCPPClassScope.acceptViaCache(this, visitor, false);
return visitor.getFields();
} catch (CoreException e) {
CCorePlugin.log(e);
return ICPPField.EMPTY_CPPFIELD_ARRAY;
}
}
@Override
@Deprecated
public ICPPField[] getDeclaredFields(IASTNode point) {
return getDeclaredFields();
}
@Override
public ICPPClassType[] getNestedClasses() {
IScope scope = getCompositeScope();
if (scope instanceof ICPPClassSpecializationScope) {
return ((ICPPClassSpecializationScope) scope).getNestedClasses();
}
try {
PDOMClassUtil.NestedClassCollector visitor = new PDOMClassUtil.NestedClassCollector();
PDOMCPPClassScope.acceptViaCache(this, visitor, false);
return visitor.getNestedClasses();
} catch (CoreException e) {
CCorePlugin.log(e);
return ICPPClassType.EMPTY_CLASS_ARRAY;
}
}
@Override
@Deprecated
public ICPPClassType[] getNestedClasses(IASTNode point) {
return getNestedClasses();
}
@Override
public ICPPUsingDeclaration[] getUsingDeclarations() {
IScope scope = getCompositeScope();
if (scope instanceof ICPPClassSpecializationScope) {
return ((ICPPClassSpecializationScope) scope).getUsingDeclarations();
}
try {
PDOMClassUtil.UsingDeclarationCollector visitor = new PDOMClassUtil.UsingDeclarationCollector();
PDOMCPPClassScope.acceptViaCache(this, visitor, false);
return visitor.getUsingDeclarations();
} catch (CoreException e) {
CCorePlugin.log(e);
return ICPPUsingDeclaration.EMPTY_USING_DECL_ARRAY;
}
}
@Override
@Deprecated
public ICPPUsingDeclaration[] getUsingDeclarations(IASTNode point) {
return getUsingDeclarations();
}
@Override
public IBinding[] getFriends() {
ICPPClassScope scope = getCompositeScope();
if (scope instanceof ICPPClassSpecializationScope)
return ((ICPPClassSpecializationScope) scope).getFriends();
return IBinding.EMPTY_BINDING_ARRAY;
}
@Override
@Deprecated
public IBinding[] getFriends(IASTNode point) {
return getFriends();
}
@Override
public ICPPMethod[] getMethods() {
return ClassTypeHelper.getMethods(this);
}
@Override
@Deprecated
public ICPPMethod[] getMethods(IASTNode point) {
return getMethods();
}
@Override
public ICPPMethod[] getAllDeclaredMethods() {
return ClassTypeHelper.getAllDeclaredMethods(this);
}
@Override
@Deprecated
public ICPPMethod[] getAllDeclaredMethods(IASTNode point) {
return getAllDeclaredMethods();
}
@Override
public IField[] getFields() {
return ClassTypeHelper.getFields(this);
}
@Override
@Deprecated
public IField[] getFields(IASTNode point) {
return getFields();
}
@Override
public IField findField(String name) {
return ClassTypeHelper.findField(this, name);
}
@Override
public int getKey() {
return getSpecializedBinding().getKey();
}
@Override
public boolean isSameType(IType type) {
if (type == this)
return true;
if (type instanceof ITypedef)
return type.isSameType(this);
if (type instanceof PDOMNode) {
PDOMNode node = (PDOMNode) type;
if (node.getPDOM() == getPDOM()) {
return node.getRecord() == getRecord();
}
}
// require a class specialization
if (!(type instanceof ICPPClassSpecialization))
return false;
return CPPClassSpecialization.isSameClassSpecialization(this, (ICPPClassSpecialization) type);
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
}
return null;
}
@Override
public void addChild(PDOMNode member) throws CoreException {
throw new UnsupportedOperationException("addMember method should be called instead."); //$NON-NLS-1$
}
@Override
public void acceptUncached(IPDOMVisitor visitor) throws CoreException {
PDOMCPPMemberBlock members = new PDOMCPPMemberBlock(getLinkage(), record + MEMBERLIST);
members.accept(visitor);
}
@Override
public void accept(IPDOMVisitor visitor) throws CoreException {
PDOMCPPClassScope.acceptViaCache(this, visitor, false);
}
@Override
public boolean isAnonymous() {
return false;
}
@Override
public boolean isFinal() {
try {
return (getFlags() & FLAGS_FINAL) != 0;
} catch (CoreException e) {
CCorePlugin.log(e);
return false;
}
}
@Override
public boolean isNoDiscard() {
try {
return (getFlags() & FLAGS_NODISCARD) != 0;
} catch (CoreException e) {
CCorePlugin.log(e);
return false;
}
}
private boolean hasOwnScope() throws CoreException {
return (getFlags() & FLAGS_HAS_OWN_SCOPE) != 0;
}
private byte getFlags() throws CoreException {
return getDB().getByte(record + FLAGS);
}
private void setFlags(ICPPClassType classType) throws CoreException {
byte flags = (byte) ((classType.isFinal() ? FLAGS_FINAL : 0)
| (hasOwnScope(classType) ? FLAGS_HAS_OWN_SCOPE : 0));
getDB().putByte(record + FLAGS, flags);
}
/**
* Returns true if the given class is an explicit template specialization that has its own definition.
*/
private static boolean hasOwnScope(ICPPClassType classType) {
if (!(classType instanceof ICPPInternalBinding))
return false;
ICPPInternalBinding binding = (ICPPInternalBinding) classType;
if (binding.getDefinition() != null)
return true;
return false;
}
@Override
public void addMember(PDOMNode member, int visibility) {
try {
PDOMCPPMemberBlock members = new PDOMCPPMemberBlock(getLinkage(), record + MEMBERLIST);
members.addMember(member, visibility);
} catch (CoreException e) {
CCorePlugin.log(e);
}
}
@Override
public int getVisibility(IBinding member) {
try {
PDOMCPPMemberBlock members = new PDOMCPPMemberBlock(getLinkage(), record + MEMBERLIST);
int visibility = members.getVisibility(member);
if (visibility < 0) {
if (member instanceof ICPPSpecialization) {
return getSpecializedBinding().getVisibility(((ICPPSpecialization) member).getSpecializedBinding());
}
throw new IllegalArgumentException(member.getName() + " is not a member of " + getName()); //$NON-NLS-1$
}
return visibility;
} catch (CoreException e) {
CCorePlugin.log(e);
return v_private; // Fallback visibility
}
}
// Class specializations do not need to be marked "visible to ADL only"
// independent of their specialized class types, so they do not need
// to implement these methods.
@Override
public boolean isVisibleToAdlOnly() {
return false;
}
@Override
public void setVisibleToAdlOnly(boolean visibleToAdlOnly) throws CoreException {
}
}