CommentSema.h revision 0bd9838751384181ff387f2fb346896792b89617
13c827367444ee418f129b2c238299f49d3264554Jarkko Poyry//===--- CommentSema.h - Doxygen comment semantic analysis ------*- C++ -*-===// 23c827367444ee418f129b2c238299f49d3264554Jarkko Poyry// 33c827367444ee418f129b2c238299f49d3264554Jarkko Poyry// The LLVM Compiler Infrastructure 43c827367444ee418f129b2c238299f49d3264554Jarkko Poyry// 53c827367444ee418f129b2c238299f49d3264554Jarkko Poyry// This file is distributed under the University of Illinois Open Source 63c827367444ee418f129b2c238299f49d3264554Jarkko Poyry// License. See LICENSE.TXT for details. 73c827367444ee418f129b2c238299f49d3264554Jarkko Poyry// 83c827367444ee418f129b2c238299f49d3264554Jarkko Poyry//===----------------------------------------------------------------------===// 93c827367444ee418f129b2c238299f49d3264554Jarkko Poyry// 103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry// This file defines the semantic analysis class for Doxygen comments. 113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry// 123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry//===----------------------------------------------------------------------===// 133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#ifndef LLVM_CLANG_AST_COMMENT_SEMA_H 153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#define LLVM_CLANG_AST_COMMENT_SEMA_H 163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "clang/Basic/Diagnostic.h" 183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "clang/Basic/SourceLocation.h" 193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "clang/AST/Comment.h" 203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "llvm/ADT/ArrayRef.h" 213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "llvm/ADT/StringRef.h" 223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "llvm/ADT/StringMap.h" 233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#include "llvm/Support/Allocator.h" 243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 253c827367444ee418f129b2c238299f49d3264554Jarkko Poyrynamespace clang { 263c827367444ee418f129b2c238299f49d3264554Jarkko Poyryclass Decl; 273c827367444ee418f129b2c238299f49d3264554Jarkko Poyryclass SourceMgr; 283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 293c827367444ee418f129b2c238299f49d3264554Jarkko Poyrynamespace comments { 303c827367444ee418f129b2c238299f49d3264554Jarkko Poyryclass CommandTraits; 313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 323c827367444ee418f129b2c238299f49d3264554Jarkko Poyryclass Sema { 333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry Sema(const Sema &) LLVM_DELETED_FUNCTION; 343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void operator=(const Sema &) LLVM_DELETED_FUNCTION; 353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// Allocator for AST nodes. 373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry llvm::BumpPtrAllocator &Allocator; 383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// Source manager for the comment being parsed. 403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry const SourceManager &SourceMgr; 413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry DiagnosticsEngine &Diags; 433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry CommandTraits &Traits; 453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// Information about the declaration this comment is attached to. 473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry DeclInfo *ThisDeclInfo; 483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// Comment AST nodes that correspond to parameter names in 503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// \c TemplateParameters. 513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// 523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// Contains a valid value if \c DeclInfo->IsFilled is true. 533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry llvm::StringMap<TParamCommandComment *> TemplateParameterDocs; 543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// AST node for the \\brief command and its aliases. 563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry const BlockCommandComment *BriefCommand; 573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// AST node for the \\returns command and its aliases. 593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry const BlockCommandComment *ReturnsCommand; 603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID) { 623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return Diags.Report(Loc, DiagID); 633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 653c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// A stack of HTML tags that are currently open (not matched with closing 663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// tags). 673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SmallVector<HTMLStartTagComment *, 8> HTMLOpenTags; 683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 693c827367444ee418f129b2c238299f49d3264554Jarkko Poyrypublic: 703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr, 713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry DiagnosticsEngine &Diags, CommandTraits &Traits); 723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void setDecl(const Decl *D); 743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// Returns a copy of array, owned by Sema's allocator. 763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry template<typename T> 773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ArrayRef<T> copyArray(ArrayRef<T> Source) { 783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry size_t Size = Source.size(); 793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry if (Size != 0) { 803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry T *Mem = Allocator.Allocate<T>(Size); 813c827367444ee418f129b2c238299f49d3264554Jarkko Poyry std::uninitialized_copy(Source.begin(), Source.end(), Mem); 823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return llvm::makeArrayRef(Mem, Size); 833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } else 843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry return llvm::makeArrayRef(static_cast<T *>(NULL), 0); 853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry } 863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ParagraphComment *actOnParagraphComment( 883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ArrayRef<InlineContentComment *> Content); 893c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry BlockCommandComment *actOnBlockCommandStart(SourceLocation LocBegin, 913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation LocEnd, 923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry unsigned CommandID); 933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void actOnBlockCommandArgs(BlockCommandComment *Command, 953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ArrayRef<BlockCommandComment::Argument> Args); 963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void actOnBlockCommandFinish(BlockCommandComment *Command, 983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ParagraphComment *Paragraph); 993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ParamCommandComment *actOnParamCommandStart(SourceLocation LocBegin, 1013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation LocEnd, 1023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry unsigned CommandID); 1033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void actOnParamCommandDirectionArg(ParamCommandComment *Command, 1053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation ArgLocBegin, 1063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation ArgLocEnd, 1073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef Arg); 1083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void actOnParamCommandParamNameArg(ParamCommandComment *Command, 1103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation ArgLocBegin, 1113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation ArgLocEnd, 1123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef Arg); 1133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void actOnParamCommandFinish(ParamCommandComment *Command, 1153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ParagraphComment *Paragraph); 1163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry TParamCommandComment *actOnTParamCommandStart(SourceLocation LocBegin, 1183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation LocEnd, 1193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry unsigned CommandID); 1203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void actOnTParamCommandParamNameArg(TParamCommandComment *Command, 1223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation ArgLocBegin, 1233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation ArgLocEnd, 1243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef Arg); 1253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void actOnTParamCommandFinish(TParamCommandComment *Command, 1273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ParagraphComment *Paragraph); 1283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry InlineCommandComment *actOnInlineCommand(SourceLocation CommandLocBegin, 1303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation CommandLocEnd, 1313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry unsigned CommandID); 1323c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1333c827367444ee418f129b2c238299f49d3264554Jarkko Poyry InlineCommandComment *actOnInlineCommand(SourceLocation CommandLocBegin, 1343c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation CommandLocEnd, 1353c827367444ee418f129b2c238299f49d3264554Jarkko Poyry unsigned CommandID, 1363c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation ArgLocBegin, 1373c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation ArgLocEnd, 1383c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef Arg); 1393c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1403c827367444ee418f129b2c238299f49d3264554Jarkko Poyry InlineContentComment *actOnUnknownCommand(SourceLocation LocBegin, 1413c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation LocEnd, 1423c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef CommandName); 1433c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1443c827367444ee418f129b2c238299f49d3264554Jarkko Poyry InlineContentComment *actOnUnknownCommand(SourceLocation LocBegin, 1453c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation LocEnd, 1463c827367444ee418f129b2c238299f49d3264554Jarkko Poyry unsigned CommandID); 1473c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1483c827367444ee418f129b2c238299f49d3264554Jarkko Poyry TextComment *actOnText(SourceLocation LocBegin, 1493c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation LocEnd, 1503c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef Text); 1513c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1523c827367444ee418f129b2c238299f49d3264554Jarkko Poyry VerbatimBlockComment *actOnVerbatimBlockStart(SourceLocation Loc, 1533c827367444ee418f129b2c238299f49d3264554Jarkko Poyry unsigned CommandID); 1543c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1553c827367444ee418f129b2c238299f49d3264554Jarkko Poyry VerbatimBlockLineComment *actOnVerbatimBlockLine(SourceLocation Loc, 1563c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef Text); 1573c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1583c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void actOnVerbatimBlockFinish(VerbatimBlockComment *Block, 1593c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation CloseNameLocBegin, 1603c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef CloseName, 1613c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ArrayRef<VerbatimBlockLineComment *> Lines); 1623c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1633c827367444ee418f129b2c238299f49d3264554Jarkko Poyry VerbatimLineComment *actOnVerbatimLine(SourceLocation LocBegin, 1643c827367444ee418f129b2c238299f49d3264554Jarkko Poyry unsigned CommandID, 1653c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation TextBegin, 1663c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef Text); 1673c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1683c827367444ee418f129b2c238299f49d3264554Jarkko Poyry HTMLStartTagComment *actOnHTMLStartTagStart(SourceLocation LocBegin, 1693c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef TagName); 1703c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1713c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void actOnHTMLStartTagFinish(HTMLStartTagComment *Tag, 1723c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ArrayRef<HTMLStartTagComment::Attribute> Attrs, 1733c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation GreaterLoc, 1743c827367444ee418f129b2c238299f49d3264554Jarkko Poyry bool IsSelfClosing); 1753c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1763c827367444ee418f129b2c238299f49d3264554Jarkko Poyry HTMLEndTagComment *actOnHTMLEndTag(SourceLocation LocBegin, 1773c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SourceLocation LocEnd, 1783c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef TagName); 1793c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1803c827367444ee418f129b2c238299f49d3264554Jarkko Poyry FullComment *actOnFullComment(ArrayRef<BlockContentComment *> Blocks); 1813c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1823c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void checkBlockCommandEmptyParagraph(BlockCommandComment *Command); 1833c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1843c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void checkReturnsCommand(const BlockCommandComment *Command); 1853c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1863c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// Emit diagnostics about duplicate block commands that should be 1873c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// used only once per comment, e.g., \\brief and \\returns. 1883c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void checkBlockCommandDuplicate(const BlockCommandComment *Command); 1893c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1903c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void checkDeprecatedCommand(const BlockCommandComment *Comment); 1913c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1923c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// Resolve parameter names to parameter indexes in function declaration. 1933c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// Emit diagnostics about unknown parametrs. 1943c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void resolveParamCommandIndexes(const FullComment *FC); 1953c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1963c827367444ee418f129b2c238299f49d3264554Jarkko Poyry bool isFunctionDecl(); 1973c827367444ee418f129b2c238299f49d3264554Jarkko Poyry bool isTemplateOrSpecialization(); 1983c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 1993c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ArrayRef<const ParmVarDecl *> getParamVars(); 2003c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 2013c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// Extract all important semantic information from 2023c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// \c ThisDeclInfo->ThisDecl into \c ThisDeclInfo members. 2033c827367444ee418f129b2c238299f49d3264554Jarkko Poyry void inspectThisDecl(); 2043c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 2053c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// Returns index of a function parameter with a given name. 2063c827367444ee418f129b2c238299f49d3264554Jarkko Poyry unsigned resolveParmVarReference(StringRef Name, 2073c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ArrayRef<const ParmVarDecl *> ParamVars); 2083c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 2093c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// Returns index of a function parameter with the name closest to a given 2103c827367444ee418f129b2c238299f49d3264554Jarkko Poyry /// typo. 2113c827367444ee418f129b2c238299f49d3264554Jarkko Poyry unsigned correctTypoInParmVarReference(StringRef Typo, 2123c827367444ee418f129b2c238299f49d3264554Jarkko Poyry ArrayRef<const ParmVarDecl *> ParamVars); 2133c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 2143c827367444ee418f129b2c238299f49d3264554Jarkko Poyry bool resolveTParamReference(StringRef Name, 2153c827367444ee418f129b2c238299f49d3264554Jarkko Poyry const TemplateParameterList *TemplateParameters, 2163c827367444ee418f129b2c238299f49d3264554Jarkko Poyry SmallVectorImpl<unsigned> *Position); 2173c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 2183c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef correctTypoInTParamReference( 2193c827367444ee418f129b2c238299f49d3264554Jarkko Poyry StringRef Typo, 2203c827367444ee418f129b2c238299f49d3264554Jarkko Poyry const TemplateParameterList *TemplateParameters); 2213c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 2223c827367444ee418f129b2c238299f49d3264554Jarkko Poyry InlineCommandComment::RenderKind 2233c827367444ee418f129b2c238299f49d3264554Jarkko Poyry getInlineCommandRenderKind(StringRef Name) const; 2243c827367444ee418f129b2c238299f49d3264554Jarkko Poyry}; 2253c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 2263c827367444ee418f129b2c238299f49d3264554Jarkko Poyry} // end namespace comments 2273c827367444ee418f129b2c238299f49d3264554Jarkko Poyry} // end namespace clang 2283c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 2293c827367444ee418f129b2c238299f49d3264554Jarkko Poyry#endif 2303c827367444ee418f129b2c238299f49d3264554Jarkko Poyry 2313c827367444ee418f129b2c238299f49d3264554Jarkko Poyry