/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// Creation of temporaries with one argument are represented by // CXXFunctionalCastExpr, while any other number of arguments are // represented by CXXTemporaryObjectExpr: bool VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr const * expr)
{ return visitTemporaryObjectExpr(expr); } bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr)
{ return visitTemporaryObjectExpr(expr); }
bool containsOWeakObjectSubclass(const clang::Type* pType0) { if (!pType0) returnfalse; if (pType0->isDependentType()) { returnfalse;
} const clang::Type* pType = pType0->getUnqualifiedDesugaredType(); if (!pType) returnfalse; const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl(); if (pRecordDecl) { // because dbaccess just has to be special...
loplugin::DeclCheck dc(pRecordDecl); if (dc.Class("DocumentEvents").Namespace("dbaccess")
.GlobalNamespace() ||
dc.Class("OBookmarkContainer").Namespace("dbaccess")
.GlobalNamespace()) returnfalse; // TODO not sure about these ones, just avoiding dbaccess in general for now if (dc.Class("SbaXPropertiesChangeMultiplexer").Namespace("dbaui").GlobalNamespace() ||
dc.Class("SbaXSubmitMultiplexer").Namespace("dbaui").GlobalNamespace() ||
dc.Class("SbaXResetMultiplexer").Namespace("dbaui").GlobalNamespace() ||
dc.Class("SbaXPropertyChangeMultiplexer").Namespace("dbaui").GlobalNamespace() ||
dc.Class("SbaXSQLErrorMultiplexer").Namespace("dbaui").GlobalNamespace() ||
dc.Class("SbaXParameterMultiplexer").Namespace("dbaui").GlobalNamespace() ||
dc.Class("SbaXRowSetApproveMultiplexer").Namespace("dbaui").GlobalNamespace() ||
dc.Class("SbaXRowSetMultiplexer").Namespace("dbaui").GlobalNamespace() ||
dc.Class("SbaXLoadMultiplexer").Namespace("dbaui").GlobalNamespace() ||
dc.Class("SbaXVetoableChangeMultiplexer").Namespace("dbaui").GlobalNamespace()) returnfalse; // slideshow playing games here if (dc.Class("SlideView").AnonymousNamespace().Namespace("internal").Namespace("slideshow").GlobalNamespace()) returnfalse; // svx playing acquire/release games here in OWeakSubObject if (dc.Class("FmXUpdateMultiplexer").GlobalNamespace() ||
dc.Class("FmXContainerMultiplexer").GlobalNamespace() ||
dc.Class("FmXSelectionMultiplexer").GlobalNamespace() ||
dc.Class("FmXGridControlMultiplexer").GlobalNamespace() ||
dc.Class("FmXModifyMultiplexer").GlobalNamespace()) returnfalse;
} if (pType->isPointerType()) { // ignore returnfalse;
} elseif (pType->isArrayType()) { const clang::ArrayType* pArrayType = dyn_cast<clang::ArrayType>(pType);
QualType elementType = pArrayType->getElementType(); return containsOWeakObjectSubclass(elementType);
} else { return loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { returnbool(loplugin::DeclCheck(decl).Class("OWeakObject").Namespace("cppu").GlobalNamespace()); });
}
}
bool containsSalhelperReferenceObjectSubclass(const clang::Type* pType0) { if (!pType0) returnfalse; const clang::Type* pType = pType0->getUnqualifiedDesugaredType(); if (!pType) returnfalse; const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl(); if (pRecordDecl) {
pRecordDecl = pRecordDecl->getCanonicalDecl();
} if (pRecordDecl) { // for performance reasons we sometimes allocate temporaries on the stack if (loplugin::DeclCheck(pRecordDecl).Struct("ScSheetLimits").GlobalNamespace()) returnfalse;
// the calc excel filter likes storing lots of classes either by reference or by value if (loplugin::isDerivedFrom(pRecordDecl,
[](Decl const * decl) -> bool
{ return bool(loplugin::DeclCheck(decl).Class("XclExpRecordBase").GlobalNamespace())
|| bool(loplugin::DeclCheck(decl).Class("XclImpChLineFormat").GlobalNamespace());
})) returnfalse;
staticbool containsStaticTypeMethod(const CXXRecordDecl* x)
{ for (auto it = x->method_begin(); it != x->method_end(); ++it) { auto i = *it; if ( !i->isStatic() ) continue; auto ident = i->getIdentifier(); if ( ident && ident->isStr("static_type") ) { returntrue;
}
} returnfalse;
}
void RefCounting::checkUnoReference(QualType qt, const Decl* decl, const RecordDecl* parent, const std::string& rDeclName)
{ if (!loplugin::TypeCheck(qt).Class("Reference").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace()) return; const CXXRecordDecl* pRecordDecl = qt->getAsCXXRecordDecl(); const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl); const TemplateArgument& rArg = pTemplate->getTemplateArgs()[0]; const CXXRecordDecl* templateParam = rArg.getAsType()->getAsCXXRecordDecl()->getDefinition(); if (!templateParam) return; // SwXText is a special case. It is a mixin class that does not inherit from OWeakObject, so // we cannot use rtl::Reference. if (loplugin::DeclCheck(templateParam).Class("SwXText")) return; if (containsStaticTypeMethod(templateParam)) return;
report(
DiagnosticsEngine::Warning,
("uno::Reference %0 with template parameter that does not" " contain ::static_type() %1%select{|, parent is %3,}2 should" " probably be using rtl::Reference instead"),
decl->getLocation())
<< rDeclName << qt << (parent != nullptr)
<< (parent != nullptr
? parent->getQualifiedNameAsString() : std::string())
<< decl->getSourceRange();
}
bool RefCounting::visitTemporaryObjectExpr(Expr const * expr) { if (ignoreLocation(expr)) { returntrue;
} auto t = expr->getType(); if (containsSvRefBaseSubclass(t.getTypePtr())) {
report(
DiagnosticsEngine::Warning,
("Temporary object of SvRefBase subclass %0 being directly stack" " managed, should be managed via tools::SvRef"),
expr->getBeginLoc())
<< t.getUnqualifiedType() << expr->getSourceRange();
} elseif (containsSalhelperReferenceObjectSubclass(t.getTypePtr())) {
report(
DiagnosticsEngine::Warning,
("Temporary object of salhelper::SimpleReferenceObject subclass %0" " being directly stack managed, should be managed via" " rtl::Reference"),
expr->getBeginLoc())
<< t.getUnqualifiedType() << expr->getSourceRange();
} elseif (containsXInterfaceSubclass(t)) {
report(
DiagnosticsEngine::Warning,
("Temporary object of css::uno::XInterface subclass %0 being" " directly stack managed, should be managed via" " css::uno::Reference"),
expr->getBeginLoc())
<< t.getUnqualifiedType() << expr->getSourceRange();
} elseif (containsOWeakObjectSubclass(t)) {
report(
DiagnosticsEngine::Warning,
("Temporary object of cppu::OWeakObject subclass %0 being" " directly stack managed, should be managed via" " css::uno::Reference"),
expr->getBeginLoc())
<< t.getUnqualifiedType() << expr->getSourceRange();
} returntrue;
}
// check for dodgy code managing ref-counted stuff with shared_ptr or unique_ptr or similar stuff bool RefCounting::VisitTypeLoc(clang::TypeLoc typeLoc)
{
QualType firstTemplateParamType; if (auto recordType = typeLoc.getType()->getUnqualifiedDesugaredType()->getAs<RecordType>()) { autoconst tc = loplugin::TypeCheck(recordType); if (tc.ClassOrStruct("unique_ptr").StdNamespace()
|| tc.ClassOrStruct("weak_ptr").StdNamespace()
|| tc.ClassOrStruct("shared_ptr").StdNamespace()
|| tc.ClassOrStruct("intrusive_ptr").Namespace("boost").GlobalNamespace())
{ auto templateDecl = dyn_cast<ClassTemplateSpecializationDecl>(recordType->getDecl()); if (templateDecl && templateDecl->getTemplateArgs().size() > 0)
firstTemplateParamType = templateDecl->getTemplateArgs()[0].getAsType();
}
} if (firstTemplateParamType.isNull()) returntrue; if (containsSvRefBaseSubclass(firstTemplateParamType.getTypePtr()))
{
report(
DiagnosticsEngine::Warning, "SvRefBase subclass %0 being managed via smart pointer, should be managed via tools::SvRef",
typeLoc.getBeginLoc())
<< firstTemplateParamType
<< typeLoc.getSourceRange();
} if (containsSalhelperReferenceObjectSubclass(firstTemplateParamType.getTypePtr()))
{
report(
DiagnosticsEngine::Warning, "salhelper::SimpleReferenceObject subclass %0 being managed via smart pointer, should be managed via rtl::Reference",
typeLoc.getBeginLoc())
<< firstTemplateParamType
<< typeLoc.getSourceRange();
} // Not in general (dbaccess::DocumentEvents, dbaccess/source/core/dataaccess/databasedocument.hxx): #if 0 if (containsXInterfaceSubclass(firstTemplateParamType))
{
report(
DiagnosticsEngine::Warning, "XInterface subclass %0 being managed via smart pointer, should be managed via uno::Reference",
typeLoc.getBeginLoc())
<< firstTemplateParamType
<< typeLoc.getSourceRange();
} #endif if (containsOWeakObjectSubclass(firstTemplateParamType.getTypePtr()))
{
report(
DiagnosticsEngine::Warning, "cppu::OWeakObject subclass %0 being managed via smart pointer, should be managed via rtl::Reference",
typeLoc.getBeginLoc())
<< firstTemplateParamType
<< typeLoc.getSourceRange();
} returntrue;
}
bool RefCounting::VisitCXXDeleteExpr(const CXXDeleteExpr * cxxDeleteExpr)
{ if (ignoreLocation(cxxDeleteExpr)) returntrue;
StringRef aFileName = getFilenameOfLocation(
compiler.getSourceManager().getSpellingLoc(cxxDeleteExpr->getBeginLoc())); if (loplugin::isSamePathname(aFileName, SRCDIR "/cppuhelper/source/weak.cxx")) returntrue; if (loplugin::isSamePathname(aFileName, SRCDIR "/include/svx/svdobj.hxx")) returntrue; if (loplugin::isSamePathname(aFileName, SRCDIR "/svx/source/svdraw/svdobj.cxx")) returntrue;
if (!cxxDeleteExpr->getArgument()) returntrue; auto argType = cxxDeleteExpr->getArgument()->getType(); if (argType.isNull() || !argType->isPointerType()) returntrue; auto pointeeType = argType->getPointeeType(); if (containsOWeakObjectSubclass(pointeeType))
{
report(
DiagnosticsEngine::Warning, "cppu::OWeakObject subclass %0 being deleted via delete, should be managed via rtl::Reference",
cxxDeleteExpr->getBeginLoc())
<< pointeeType
<< cxxDeleteExpr->getSourceRange();
} returntrue;
}
bool RefCounting::VisitFieldDecl(const FieldDecl * fieldDecl) { if (ignoreLocation(fieldDecl)) { returntrue;
} if (fieldDecl->isBitField()) { returntrue;
}
// We can't call FieldDecl::getParent, which triggers an assertion at least with // current trunk towards Clang 3.7 when the FieldDecl is actually an // ObjCIvarDecl. if (isa<ObjCIvarDecl>(fieldDecl)) { returntrue;
}
if (containsSvRefBaseSubclass(fieldDecl->getType().getTypePtr())) {
report(
DiagnosticsEngine::Warning, "SvRefBase subclass %0 being directly heap managed, should be managed via tools::SvRef, " ", parent is %1",
fieldDecl->getLocation())
<< fieldDecl->getType()
<< fieldDecl->getParent()
<< fieldDecl->getSourceRange();
}
if (containsSalhelperReferenceObjectSubclass(fieldDecl->getType().getTypePtr())) {
report(
DiagnosticsEngine::Warning, "salhelper::SimpleReferenceObject subclass %0 being directly heap managed, should be managed via rtl::Reference, " "parent is %1",
fieldDecl->getLocation())
<< fieldDecl->getType()
<< fieldDecl->getParent()
<< fieldDecl->getSourceRange();
}
autoconst dc = loplugin::DeclCheck(fieldDecl->getParent()); if ( (dc.Class("BaseReference").Namespace("uno").Namespace("star")
.Namespace("sun").Namespace("com").GlobalNamespace())
|| (dc.Class("Reference").Namespace("rtl").GlobalNamespace())
|| (dc.Union("element_alias").Namespace("detail").Namespace("cppu")
.GlobalNamespace()) // this is playing some kind of game to avoid circular references
|| (dc.Class("ResultSetDataSupplier").Namespace("ucbhelper")
.GlobalNamespace()))
{ returntrue;
}
if (containsXInterfaceSubclass(fieldDecl->getType())) {
report(
DiagnosticsEngine::Warning, "XInterface subclass %0 being directly heap managed, should be managed via uno::Reference, " "parent is %1",
fieldDecl->getLocation())
<< fieldDecl->getType()
<< fieldDecl->getParent()
<< fieldDecl->getSourceRange();
}
if (containsOWeakObjectSubclass(fieldDecl->getType())) {
report(
DiagnosticsEngine::Warning, "cppu::OWeakObject subclass %0 being directly heap managed, should be managed via rtl::Reference, " "parent is %1",
fieldDecl->getLocation())
<< fieldDecl->getType()
<< fieldDecl->getParent()
<< fieldDecl->getSourceRange();
}
if (!returnStmt->getRetValue()) returntrue; auto cxxNewExpr = dyn_cast<CXXNewExpr>(returnStmt->getRetValue()->IgnoreImplicit()); if (!cxxNewExpr) returntrue;
auto qt = returnStmt->getRetValue()->getType(); if (!qt->isPointerType()) returnfalse;
qt = qt->getPointeeType();
if (containsOWeakObjectSubclass(qt)) {
report(
DiagnosticsEngine::Warning, "new object of cppu::OWeakObject subclass %0 being returned via raw pointer, should be returned by via rtl::Reference",
returnStmt->getBeginLoc())
<< qt
<< returnStmt->getSourceRange();
}
returntrue;
}
bool RefCounting::VisitVarDecl(const VarDecl * varDecl) { if (ignoreLocation(varDecl)) returntrue;
if (containsSvRefBaseSubclass(varDecl->getType().getTypePtr())) {
report(
DiagnosticsEngine::Warning, "SvRefBase subclass being directly stack managed, should be managed via tools::SvRef, "
+ varDecl->getType().getAsString(),
varDecl->getLocation())
<< varDecl->getSourceRange();
} if (containsSalhelperReferenceObjectSubclass(varDecl->getType().getTypePtr())) {
StringRef name { getFilenameOfLocation(
compiler.getSourceManager().getSpellingLoc(varDecl->getLocation())) }; // this is playing games that it believes is safe if (loplugin::isSamePathname(name, SRCDIR "/stoc/source/security/permissions.cxx")) returntrue;
report(
DiagnosticsEngine::Warning, "salhelper::SimpleReferenceObject subclass being directly stack managed, should be managed via rtl::Reference, "
+ varDecl->getType().getAsString(),
varDecl->getLocation())
<< varDecl->getSourceRange();
} if (containsXInterfaceSubclass(varDecl->getType())) {
report(
DiagnosticsEngine::Warning, "XInterface subclass being directly stack managed, should be managed via uno::Reference, "
+ varDecl->getType().getAsString(),
varDecl->getLocation())
<< varDecl->getSourceRange();
} if (containsOWeakObjectSubclass(varDecl->getType())) {
report(
DiagnosticsEngine::Warning, "cppu::OWeakObject subclass being directly stack managed, should be managed via uno::Reference, "
+ varDecl->getType().getAsString(),
varDecl->getLocation())
<< varDecl->getSourceRange();
}
if (varDecl->getType()->isPointerType() && varDecl->getInit())
{ auto newExpr = dyn_cast<CXXNewExpr>(varDecl->getInit()->IgnoreImplicit()); if (newExpr)
{
StringRef fileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(varDecl->getBeginLoc())); if (loplugin::isSamePathname(fileName, SRCDIR "/cppuhelper/source/component_context.cxx")) returntrue; auto pointeeType = varDecl->getType()->getPointeeType(); if (containsOWeakObjectSubclass(pointeeType))
report(
DiagnosticsEngine::Warning, "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
varDecl->getLocation())
<< pointeeType
<< varDecl->getSourceRange();
} if (isCastingReference(varDecl->getInit()))
{ // TODO false+ code
StringRef fileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(varDecl->getBeginLoc())); if (loplugin::isSamePathname(fileName, SRCDIR "/sw/source/core/unocore/unotbl.cxx")) returntrue; auto pointeeType = varDecl->getType()->getPointeeType(); if (containsOWeakObjectSubclass(pointeeType))
report(
DiagnosticsEngine::Warning, "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
varDecl->getLocation())
<< pointeeType
<< varDecl->getSourceRange();
} if (isCallingGetOnWeakRef(varDecl->getInit()))
{ auto pointeeType = varDecl->getType()->getPointeeType(); if (containsOWeakObjectSubclass(pointeeType))
report(
DiagnosticsEngine::Warning, "weak object being converted to strong, and then the reference dropped, and managed via raw pointer, should be managed via rtl::Reference",
varDecl->getLocation())
<< pointeeType
<< varDecl->getSourceRange();
}
} returntrue;
}
/** Look for code like static_cast<FooChild*>(makeFoo().get()); where makeFoo() returns a Reference<Foo>
*/ bool RefCounting::isCastingReference(const Expr* expr)
{
expr = expr->IgnoreImplicit(); auto castExpr = dyn_cast<CastExpr>(expr); if (!castExpr) returnfalse; auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(castExpr->getSubExpr()); if (!memberCallExpr) returnfalse; if (!memberCallExpr->getMethodDecl()->getIdentifier() || memberCallExpr->getMethodDecl()->getName() != "get") returnfalse;
QualType objectType = memberCallExpr->getImplicitObjectArgument()->getType(); if (!loplugin::TypeCheck(objectType).Class("Reference")) returnfalse; // ignore "x.get()" where x is a var auto obj = memberCallExpr->getImplicitObjectArgument()->IgnoreImplicit(); if (isa<DeclRefExpr>(obj) || isa<MemberExpr>(obj)) returnfalse; // if the foo in foo().get() returns "rtl::Reference<T>&" then the variable // we are assigning to does not __have__ to be Reference, since the method called // must already be holding a reference. if (auto callExpr = dyn_cast<CallExpr>(obj))
{ if (auto callMethod = callExpr->getDirectCallee()) if (callMethod->getReturnType()->isReferenceType()) returnfalse;
} // Ignore // WeakReference x; // if (x.get.get()) // and similar stuff if (auto memberCall2 = dyn_cast<CXXMemberCallExpr>(obj))
{ if (loplugin::TypeCheck(memberCall2->getImplicitObjectArgument()->getType()).Class("WeakReference")) returnfalse;
} returntrue;
}
/** Look for code like makeFoo().get(); or cast<T*>(makeFoo().get().get()); or foo.get(); where makeFoo() returns a unotools::WeakReference<Foo> and foo is a unotools::WeakReference<Foo> var.
*/ bool RefCounting::isCallingGetOnWeakRef(const Expr* expr)
{
expr = expr->IgnoreImplicit(); // unwrap the cast (if any) if (auto castExpr = dyn_cast<CastExpr>(expr))
expr = castExpr->getSubExpr()->IgnoreImplicit(); // unwrap outer get (if any) if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(expr))
{ auto methodDecl = memberCallExpr->getMethodDecl(); if (methodDecl && methodDecl->getIdentifier() && methodDecl->getName() == "get")
{
QualType objectType = memberCallExpr->getImplicitObjectArgument()->getType(); if (loplugin::TypeCheck(objectType).Class("Reference").Namespace("rtl"))
expr = memberCallExpr->getImplicitObjectArgument()->IgnoreImplicit();
}
} // check for converting a WeakReference to a strong reference via get() if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(expr))
{ auto methodDecl = memberCallExpr->getMethodDecl(); if (methodDecl && methodDecl->getIdentifier() && methodDecl->getName() == "get")
{
QualType objectType = memberCallExpr->getImplicitObjectArgument()->getType(); if (loplugin::TypeCheck(objectType).Class("WeakReference").Namespace("unotools")) returntrue;
}
} returnfalse;
}
bool RefCounting::VisitBinaryOperator(const BinaryOperator * binaryOperator)
{ if (ignoreLocation(binaryOperator)) returntrue; if (binaryOperator->getOpcode() != BO_Assign) returntrue; if (!binaryOperator->getLHS()->getType()->isPointerType()) returntrue;
auto newExpr = dyn_cast<CXXNewExpr>(binaryOperator->getRHS()->IgnoreImplicit()); if (newExpr)
{ // deliberately does not want to keep track at the allocation site
StringRef fileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(binaryOperator->getBeginLoc())); if (loplugin::isSamePathname(fileName, SRCDIR "/vcl/unx/generic/dtrans/X11_selection.cxx")) returntrue;
auto pointeeType = binaryOperator->getLHS()->getType()->getPointeeType(); if (containsOWeakObjectSubclass(pointeeType))
{
report(
DiagnosticsEngine::Warning, "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
binaryOperator->getBeginLoc())
<< pointeeType
<< binaryOperator->getSourceRange();
}
} if (isCastingReference(binaryOperator->getRHS()))
{ auto pointeeType = binaryOperator->getLHS()->getType()->getPointeeType(); if (containsOWeakObjectSubclass(pointeeType))
report(
DiagnosticsEngine::Warning, "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
binaryOperator->getBeginLoc())
<< pointeeType
<< binaryOperator->getSourceRange();
} if (isCallingGetOnWeakRef(binaryOperator->getRHS()))
{ // TODO Very dodgy code, but I see no simple way of fixing it
StringRef fileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(binaryOperator->getBeginLoc())); if (loplugin::isSamePathname(fileName, SRCDIR "/sd/source/ui/view/Outliner.cxx")) returntrue; auto pointeeType = binaryOperator->getLHS()->getType()->getPointeeType(); if (containsOWeakObjectSubclass(pointeeType))
report(
DiagnosticsEngine::Warning, "weak object being converted to strong, and then the reference dropped, and managed via raw pointer, should be managed via rtl::Reference",
binaryOperator->getBeginLoc())
<< pointeeType
<< binaryOperator->getSourceRange();
} returntrue;
}
bool RefCounting::VisitFunctionDecl(const FunctionDecl * functionDecl) { if (ignoreLocation(functionDecl)) { returntrue;
} // only consider base declarations, not overridden ones, or we warn on methods that // are overriding stuff from external libraries const CXXMethodDecl * methodDecl = dyn_cast<CXXMethodDecl>(functionDecl); if (methodDecl && methodDecl->size_overridden_methods() > 0) { returntrue;
}
checkUnoReference(functionDecl->getReturnType(), functionDecl, nullptr, "return"); returntrue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.