| package org.eclipse.papyrus.designer.languages.cpp.reverse.reverse |
| |
| import java.util.logging.Level |
| import org.eclipse.cdt.core.dom.ast.ASTTypeUtil |
| import org.eclipse.cdt.core.dom.ast.ExpansionOverlapsBoundaryException |
| import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier |
| import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier |
| import org.eclipse.cdt.core.dom.ast.IASTEnumerationSpecifier |
| import org.eclipse.cdt.core.dom.ast.IASTFunctionDefinition |
| import org.eclipse.cdt.core.dom.ast.IASTName |
| import org.eclipse.cdt.core.dom.ast.IASTNamedTypeSpecifier |
| import org.eclipse.cdt.core.dom.ast.IASTNode |
| import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration |
| import org.eclipse.cdt.core.dom.ast.IASTStandardFunctionDeclarator |
| import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit |
| import org.eclipse.cdt.core.dom.ast.IBinding |
| import org.eclipse.cdt.core.dom.ast.IField |
| import org.eclipse.cdt.core.dom.ast.IProblemBinding |
| import org.eclipse.cdt.core.dom.ast.ITypedef |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTElaboratedTypeSpecifier |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTFunctionDefinition |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNamespaceDefinition |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateDeclaration |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTemplateId |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPBinding |
| import org.eclipse.cdt.core.model.ISourceRange |
| import org.eclipse.cdt.core.model.ISourceReference |
| import org.eclipse.cdt.core.model.ITranslationUnit |
| import org.eclipse.cdt.core.parser.ast.ASTAccessVisibility |
| import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPDeferredClassInstance |
| import org.eclipse.uml2.uml.VisibilityKind |
| import org.eclipse.uml2.uml.Namespace |
| import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier |
| import org.eclipse.cdt.core.dom.ast.ASTSignatureUtil |
| import org.eclipse.cdt.internal.core.model.ASTStringUtil |
| import org.eclipse.cdt.core.dom.ast.IASTElaboratedTypeSpecifier |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTNameSpecifier |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateInstance |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName |
| import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding |
| import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPUnknownMemberClass |
| import org.eclipse.cdt.core.dom.ast.cpp.ICPPSpecialization |
| |
| class ASTUtils { |
| /** |
| * Obtain a declSpecifier from a source reference |
| */ |
| static def IASTDeclSpecifier getDeclSpecifier(ISourceReference sourceRef) { |
| return sourceRef.findEnclosingNode.declSpecifier |
| } |
| |
| /** |
| * Obtain a declSpecifier from a node |
| */ |
| static def IASTDeclSpecifier getDeclSpecifier(IASTNode node) { |
| if (node instanceof IASTDeclSpecifier) { |
| // already a declSpecifier, return it directly |
| return node; |
| } else if (node instanceof IASTFunctionDefinition) { |
| return node.declSpecifier |
| } else if (node instanceof IASTSimpleDeclaration) { |
| return node.declSpecifier |
| } else if (node instanceof ICPPASTTemplateDeclaration) { |
| // check template declaration recursively |
| return node.declaration.getDeclSpecifier |
| } |
| return null |
| } |
| |
| /** |
| * Obtain a function declaration (typically for operation analysis) from a source reference |
| */ |
| static def IASTStandardFunctionDeclarator getDeclarator(ISourceReference sourceRef) { |
| var node = sourceRef.findEnclosingNode |
| if (node instanceof ICPPASTFunctionDefinition) { |
| return node.declarator as IASTStandardFunctionDeclarator |
| } else if (node instanceof IASTSimpleDeclaration) { |
| return node.declarators.filter(typeof(IASTStandardFunctionDeclarator)).head |
| } else if (node instanceof ICPPASTTemplateDeclaration) { |
| var declaration = node.declaration |
| if (declaration instanceof IASTSimpleDeclaration) { |
| return declaration.declarators.filter(typeof(IASTStandardFunctionDeclarator)).head |
| } |
| } |
| return null |
| } |
| |
| /** |
| * get a node-selector from a translation unit. The selector is cached to improve |
| * performance. It skips already indexed header files |
| */ |
| static def getSelector(ITranslationUnit unit) { |
| var IASTTranslationUnit ast = null |
| val tuToASTtuMap = ReverseData.current.tuToASTtuMap; |
| |
| if (unit !== null) { |
| if (tuToASTtuMap.get(unit) !== null) { |
| return tuToASTtuMap.get(unit).getNodeSelector(null) |
| } else { |
| // the program must already been indexed (i.e. not using CONFIGURE_USING_SOURCE_CONTEXT) |
| ast = unit.getAST(ReverseData.current.index, ITranslationUnit.AST_SKIP_INDEXED_HEADERS) |
| if (ast !== null) { |
| tuToASTtuMap.put(unit, ast) |
| return ast.getNodeSelector(null); |
| } |
| } |
| } |
| |
| return null |
| } |
| |
| /** |
| * get the AST node associated with a source reference |
| */ |
| static def findEnclosingNode(ISourceReference sourceRef) { |
| var selector = getSelector(sourceRef.translationUnit) |
| var ISourceRange range = sourceRef.getSourceRange() |
| if (selector !== null) { |
| return selector.findEnclosingNode(range.startPos, range.length) |
| } |
| return null |
| } |
| |
| /** |
| * Get a qualified name from a source reference via the AST node |
| * The function is based on the qualified names in bindings, obtained |
| * via decl-specifiers or AST names- |
| * In case of a function definition, the parent binding is used in order |
| * to obtain the type to which the method belongs. |
| * If no suitable name is obtained, null is returned. |
| */ |
| static def getQualifiedName(ISourceReference sourceRef) { |
| var node = sourceRef.findEnclosingNode |
| if (node instanceof IASTName) { |
| // can obtain name directly |
| return node.qualifiedName |
| } |
| if (node instanceof ICPPASTNamespaceDefinition) { |
| return node.name.qualifiedName |
| } |
| if (node instanceof ICPPASTTemplateDeclaration) { |
| node = node.declaration |
| } |
| if (node instanceof IASTFunctionDefinition) { |
| // in case of function/method bodies, we're interested in |
| // in the type owning it. Therefore, navigate to binding |
| // owner |
| val binding = node.declarator.name.resolveBinding.owner |
| if (binding instanceof ICPPBinding) { |
| return ASTTypeUtil.getQualifiedName(binding) |
| } |
| } |
| // now try to obtain a declSpecifier |
| val declSpecifier = node.declSpecifier |
| if (declSpecifier !== null) { |
| return declSpecifier.qualifiedName |
| } |
| return null; |
| } |
| |
| /** |
| * return the qualified name of a declaration based on its |
| * declaration specifier |
| */ |
| static def String getQualifiedName(IASTDeclSpecifier declSpecifier) { |
| if (declSpecifier instanceof IASTNamedTypeSpecifier) { |
| return declSpecifier.name.qualifiedName |
| } else if (declSpecifier instanceof IASTElaboratedTypeSpecifier) { |
| return declSpecifier.name.qualifiedName |
| } else if (declSpecifier instanceof IASTCompositeTypeSpecifier) { |
| return declSpecifier.name.qualifiedName |
| } else if (declSpecifier instanceof IASTEnumerationSpecifier) { |
| return declSpecifier.name.qualifiedName |
| } else { |
| // LOGGER.log(Level.WARNING, "declSpecifier is not a named type specifier") |
| // fallback to typename via toString (no ASTName, binding), typically for basic types |
| return ReverseUtils.getCppTypeName( |
| ASTStringUtil.getSignatureString(declSpecifier, null)) |
| // return ReverseUtils.getCppTypeName(declSpecifier.toString) |
| } |
| } |
| |
| /** |
| * CDT resolves bindings completely, i.e. references to "using" names are |
| * resolved to their right-hand definition |
| * Therefore recover the first segment |
| */ |
| static def String getQNameOfUsing(IASTName name) { |
| if (name instanceof ICPPASTQualifiedName) { |
| val segment = name.allSegments.get(0) |
| if (segment instanceof IASTName) { |
| val segLastName = segment.lastName |
| return segLastName.qualifiedName |
| } |
| } |
| return null |
| } |
| |
| /** |
| * get the qualified name from an AST name via a binding. |
| * If it is a template, get binding of template itself (no arguments) |
| */ |
| static def String getQualifiedName(IASTName name) { |
| val lastName = name.lastName |
| var IBinding binding |
| if (lastName instanceof ICPPASTTemplateId) { |
| binding = lastName.templateName.resolveBinding() |
| } else { |
| binding = name.resolveBinding() |
| } |
| if (binding instanceof ICPPUnknownMemberClass) { |
| // remove template signature (in which situations does this make sense?) |
| // (ICPPUnknownMemberClass represents a binding for a type found in a template |
| // definition, that can be determined only after the template is instantiated.) |
| binding = (binding as ICPPUnknownMemberClass).owner |
| } |
| return binding.qualifiedName |
| } |
| |
| /** |
| * Get a binding from a C++ name-specifier. If it is a template, get binding |
| * of template itself (no arguments). |
| * IASTName is no common supertype |
| */ |
| static def getQualifiedNameFromNSpec(ICPPASTNameSpecifier nameSpecifier) { |
| if (nameSpecifier instanceof ICPPASTTemplateId) { |
| return nameSpecifier.templateName.resolveBinding.qualifiedName |
| } |
| else if (nameSpecifier instanceof IASTName) { |
| return nameSpecifier.qualifiedName |
| } |
| else { |
| return nameSpecifier.resolveBinding.qualifiedName |
| } |
| } |
| |
| /** |
| * return a qualified name from a binding from. If the binding cannot |
| * be resolved, return the name as string |
| */ |
| static def String getQualifiedName(IBinding binding) { |
| if (binding instanceof IProblemBinding) { |
| ReverseUtils.LOGGER.log(Level.WARNING, String.format("binding <%s> is problematic", binding)) |
| if (binding.candidateBindings.size > 0) { |
| return binding.candidateBindings.get(0).qualifiedName |
| } |
| } |
| if (binding instanceof IField) { |
| return binding.compositeTypeOwner.qualifiedName |
| } |
| if (binding instanceof ICPPTemplateInstance) { |
| return (binding.templateDefinition as IBinding).qualifiedName |
| } |
| if (binding instanceof ICPPSpecialization) { |
| // remove template bindings |
| return (binding.specializedBinding).qualifiedName |
| } |
| if (binding instanceof ITypedef) { |
| //val tst = (binding as ICPPInternalBinding).declarations.get(0) |
| //if (tst instanceof IASTName) { |
| // return tst.qualifiedName |
| //} |
| } |
| if (binding instanceof ICPPBinding) { |
| return ASTTypeUtil.getQualifiedName(binding).stripFNHint |
| } |
| else { |
| // e.g. typedefs + fallback |
| return binding.name |
| } |
| } |
| |
| /** |
| * This functions removes a file-name hint that is eventually present in |
| * a qualified name. The ASTTypeUtil adds it in enclosed with { .. } to detect |
| * recursively calls to the same binding. |
| */ |
| static def String stripFNHint(String qName) { |
| if (qName !== null) { |
| var idx = qName.lastIndexOf("{") |
| if (idx != -1) { |
| if (idx > 2) idx-=2; // strip preceding "::", if not first entry |
| var endIdx = qName.indexOf("}", idx) |
| if (endIdx != -1) { |
| // strip preceding "::", if not last entry |
| if (endIdx < qName.length-2) endIdx+=2; |
| val strippedName = qName.substring(0, idx) + qName.substring(endIdx + 1) |
| return strippedName.stripFNHint |
| } |
| } |
| } |
| return qName |
| } |
| |
| /** |
| * get the target translation unit from a name |
| */ |
| static def getTargetTU(IASTName name) { |
| return ReverseUtils.getTranslationUnitFromPath(name.containingFilename, ReverseData.current.project) |
| } |
| |
| /** |
| * return the qualified name of a template type |
| */ |
| static def getTemplateType(IASTName astName, String name) { |
| var IASTNode parent = astName |
| while (parent !== null) { |
| if (parent instanceof ICPPASTTemplateDeclaration) { |
| for (parameter : parent.templateParameters) { |
| if (parameter.toString == name) { |
| val templateQN = (parent.declaration as IASTSimpleDeclaration).declSpecifier.qualifiedName |
| return templateQN + "::" + name |
| } |
| } |
| } |
| parent = parent.parent |
| } |
| return name |
| } |
| |
| /** |
| * Get the C++ typename from an AST decl specifier |
| * This includes all modifiers and eventual template parameters |
| */ |
| static def String getCppTypeName(IASTDeclSpecifier declarator) { |
| var String parameterTypeName = ""; // $NON-NLS-1$ |
| try { |
| var token = declarator.syntax |
| while (token !== null) { |
| var String tokenStr = token.toString() |
| if (tokenStr.equals("*")) { // $NON-NLS-1$ |
| // TODO: check, if this can be called (depending on |
| // * position with different semantics?) |
| // isPointer = true; |
| } else if (tokenStr.equals("&")) { // $NON-NLS-1$ |
| // isRef = true; |
| } else if (tokenStr.equals("const")) { // $NON-NLS-1$ |
| // do nothing (use isConst() operation of |
| // parameterType) |
| // is not part of parameter type |
| } else { |
| if (parameterTypeName.length() > 0) { |
| parameterTypeName += " "; // $NON-NLS-1$ |
| } |
| parameterTypeName += tokenStr; |
| } |
| |
| token = token.getNext(); |
| } |
| } catch (ExpansionOverlapsBoundaryException e) { |
| } |
| return parameterTypeName |
| } |
| |
| /** |
| * Convert AST visibilities ... |
| */ |
| static def convertVisibility(ASTAccessVisibility visibility) { |
| if(visibility == ASTAccessVisibility.PRIVATE) return VisibilityKind.PRIVATE_LITERAL |
| if(visibility == ASTAccessVisibility.PROTECTED) return VisibilityKind.PROTECTED_LITERAL |
| return VisibilityKind.PUBLIC_LITERAL |
| } |
| |
| /** |
| * Yes, there are two AST ways for visibilities ... |
| */ |
| static def convertVisibility(int visibility) { |
| if(visibility == ICPPASTVisibilityLabel.v_private) return VisibilityKind.PRIVATE_LITERAL |
| if(visibility == ICPPASTVisibilityLabel.v_protected) return VisibilityKind.PROTECTED_LITERAL |
| return VisibilityKind.PUBLIC_LITERAL |
| } |
| } |