Catch-all printValue implementation changed to enable correct invocation if only parent type overload exists (ex. if there is no overload for TF1*, compiler invokes the overload to its best parent overload match, in the worst case void*). Argument changed from reference to pointer to support this. isEnumType Coverity bug changed from if to assert (coding, not runtime error) Changed the way printValue is invoked in order to correctly cast Value to the needed value (e.g. LL -> short). Extracted value stays in scope while we execute printValue, because we use the address.

This commit is contained in:
Boris Perovic 2015-08-20 16:33:36 +02:00 committed by sftnight
parent fc447d5fd5
commit 39f64ab7b7
2 changed files with 305 additions and 300 deletions

View File

@ -15,151 +15,148 @@ namespace cling {
class Value; class Value;
// General fallback - prints the address
std::string printValue(const void *ptr);
// void pointer // void pointer
std::string printValue(void *ptr); std::string printValue(const void **ptr);
// Bool // Bool
std::string printValue(bool val); std::string printValue(const bool *val);
// Chars // Chars
std::string printValue(char val); std::string printValue(const char *val);
std::string printValue(signed char val); std::string printValue(const signed char *val);
std::string printValue(unsigned char val); std::string printValue(const unsigned char *val);
// Ints // Ints
std::string printValue(short val); std::string printValue(const short *val);
std::string printValue(unsigned short val); std::string printValue(const unsigned short *val);
std::string printValue(int val); std::string printValue(const int *val);
std::string printValue(unsigned int val); std::string printValue(const unsigned int *val);
std::string printValue(long val); std::string printValue(const long *val);
std::string printValue(unsigned long val); std::string printValue(const unsigned long *val);
std::string printValue(long long val); std::string printValue(const long long *val);
std::string printValue(unsigned long long val); std::string printValue(const unsigned long long *val);
// Reals // Reals
std::string printValue(float val); std::string printValue(const float *val);
std::string printValue(double val); std::string printValue(const double *val);
std::string printValue(long double val); std::string printValue(const long double *val);
// Char pointers // Char pointers
std::string printValue(const char *const val); std::string printValue(const char *const *val);
std::string printValue(char *val); std::string printValue(const char **val);
// std::string // std::string
std::string printValue(const std::string &val); std::string printValue(const std::string *val);
// cling::Value // cling::Value
std::string printValue(const Value &value); std::string printValue(const Value *value);
namespace internal { // Collections internal declaration
namespace collectionPrinterInternal {
// Maps declaration // Maps declaration
template<typename CollectionType> template<typename CollectionType>
auto printValue_impl(const CollectionType &obj, short) auto printValue_impl(const CollectionType *obj, short)
-> decltype( -> decltype(
++(obj.begin()), obj.end(), ++(obj->begin()), obj->end(),
obj.begin()->first, obj.begin()->second, obj->begin()->first, obj->begin()->second,
std::string()); std::string());
// Collections like vector, set, deque etc. declaration // Vector, set, deque etc. declaration
template<typename CollectionType> template<typename CollectionType>
auto printValue_impl(const CollectionType &obj, int) auto printValue_impl(const CollectionType *obj, int)
-> decltype( -> decltype(
++(obj.begin()), obj.end(), ++(obj->begin()), obj->end(),
*(obj.begin()), *(obj->begin()),
std::string()); std::string());
// General fallback - print object address declaration // No general fallback anymore here, void* overload used for that now
template<typename T>
std::string printValue_impl(const T &obj, long);
} }
// Collections and general fallback entry function // Collections
template<typename CollectionType> template<typename CollectionType>
auto printValue(const CollectionType &obj) auto printValue(const CollectionType *obj)
-> decltype(internal::printValue_impl(obj, 0), std::string()) -> decltype(collectionPrinterInternal::printValue_impl(obj, 0), std::string())
{ {
return internal::printValue_impl(obj, (short)0); // short -> int -> long = priority order return collectionPrinterInternal::printValue_impl(obj, (short)0); // short -> int -> long = priority order
} }
// Arrays // Arrays
template<typename T, size_t N> template<typename T, size_t N>
std::string printValue(const T (&obj)[N]) { std::string printValue(const T (*obj)[N]) {
std::string str = "{ "; std::string str = "{ ";
for (int i = 0; i < N; ++i) { for (int i = 0; i < N; ++i) {
str = str + printValue(obj[i]); str += printValue(*obj + i);
if (i < N-1) str += ", "; if (i < N - 1) str += ", ";
} }
return str + " }"; return str + " }";
} }
namespace internal { // Collections internal
namespace collectionPrinterInternal {
// Maps // Maps
template<typename CollectionType> template<typename CollectionType>
auto printValue_impl(const CollectionType &obj, short) auto printValue_impl(const CollectionType *obj, short)
-> decltype( -> decltype(
++(obj.begin()), obj.end(), ++(obj->begin()), obj->end(),
obj.begin()->first, obj.begin()->second, obj->begin()->first, obj->begin()->second,
std::string()) std::string())
{ {
std::string str = "{ "; std::string str = "{ ";
auto iter = obj.begin(); auto iter = obj->begin();
auto iterEnd = obj.end(); auto iterEnd = obj->end();
while (iter != iterEnd) { while (iter != iterEnd) {
str = str + printValue(iter->first); str += printValue(&iter->first);
str = str + " => "; str += " => ";
str = str + printValue(iter->second); str += printValue(&iter->second);
++iter; ++iter;
if (iter != iterEnd) { if (iter != iterEnd) {
str = str + ", "; str += ", ";
} }
} }
return str + " }"; return str + " }";
} }
// Collections like vector, set, deque etc. // Vector, set, deque etc.
template<typename CollectionType> template<typename CollectionType>
auto printValue_impl(const CollectionType &obj, int) auto printValue_impl(const CollectionType *obj, int)
-> decltype( -> decltype(
++(obj.begin()), obj.end(), ++(obj->begin()), obj->end(),
*(obj.begin()), *(obj->begin()),
std::string()) std::string())
{ {
std::string str = "{ "; std::string str = "{ ";
auto iter = obj.begin(); auto iter = obj->begin();
auto iterEnd = obj.end(); auto iterEnd = obj->end();
while (iter != iterEnd) { while (iter != iterEnd) {
str = str + printValue(*iter); str += printValue(&(*iter));
++iter; ++iter;
if (iter != iterEnd) { if (iter != iterEnd) {
str = str + ", "; str += ", ";
} }
} }
return str + " }"; return str + " }";
} }
// General fallback - print object address
template<typename T>
std::string printValue_impl(const T &obj, long) {
return "@" + printValue((void *) &obj);
}
} }
} }

View File

@ -56,7 +56,7 @@
using namespace cling; using namespace cling;
// Implements the CValuePrinter interface. // Implements the CValuePrinter interface.
extern "C" void cling_PrintValue(void* /*cling::Value**/ V) { extern "C" void cling_PrintValue(void * /*cling::Value**/ V) {
//Value* value = (Value*)V; //Value* value = (Value*)V;
// We need stream that doesn't close its file descriptor, thus we are not // We need stream that doesn't close its file descriptor, thus we are not
@ -71,17 +71,17 @@ extern "C" void cling_PrintValue(void* /*cling::Value**/ V) {
// Checking whether the pointer points to a valid memory location // Checking whether the pointer points to a valid memory location
// Used for checking of void* output // Used for checking of void* output
// Should be moved to earlier stages (ex. IR) in the future // Should be moved to earlier stages (ex. IR) in the future
static bool isAddressValid(void* P) { static bool isAddressValid(const void *P) {
if (!P || P == (void*)-1) if (!P || P == (void *) -1)
return false; return false;
#ifdef LLVM_ON_WIN32 #ifdef LLVM_ON_WIN32
MEMORY_BASIC_INFORMATION MBI; MEMORY_BASIC_INFORMATION MBI;
if (!VirtualQuery(P, &MBI, sizeof(MBI))) if (!VirtualQuery(P, &MBI, sizeof(MBI)))
return false; return false;
if (MBI.State != MEM_COMMIT) if (MBI.State != MEM_COMMIT)
return false; return false;
return true; return true;
#else #else
// There is a POSIX way of finding whether an address can be accessed for // There is a POSIX way of finding whether an address can be accessed for
// reading: write() will return EFAULT if not. // reading: write() will return EFAULT if not.
@ -99,184 +99,182 @@ static bool isAddressValid(void* P) {
#endif #endif
} }
static std::string getValueString(const Value &V) {
clang::ASTContext &C = V.getASTContext();
clang::QualType Ty = V.getType().getDesugaredType(C);
if (const clang::BuiltinType *BT
= llvm::dyn_cast<clang::BuiltinType>(Ty.getCanonicalType())) {
switch (BT->getKind()) {
case clang::BuiltinType::Bool: // intentional fall through
case clang::BuiltinType::Char_U: // intentional fall through
case clang::BuiltinType::Char_S: // intentional fall through
case clang::BuiltinType::SChar: // intentional fall through
case clang::BuiltinType::Short: // intentional fall through
case clang::BuiltinType::Int: // intentional fall through
case clang::BuiltinType::Long: // intentional fall through
case clang::BuiltinType::LongLong: {
std::ostringstream strm;
strm << V.getLL();
return strm.str();
}
break;
case clang::BuiltinType::UChar: // intentional fall through
case clang::BuiltinType::UShort: // intentional fall through
case clang::BuiltinType::UInt: // intentional fall through
case clang::BuiltinType::ULong: // intentional fall through
case clang::BuiltinType::ULongLong: {
std::ostringstream strm;
strm << V.getULL();
return strm.str();
}
break;
case clang::BuiltinType::Float: {
std::ostringstream strm;
strm << V.getFloat();
return strm.str();
}
break;
case clang::BuiltinType::Double: {
std::ostringstream strm;
strm << V.getDouble();
return strm.str();
}
break;
case clang::BuiltinType::LongDouble: {
std::ostringstream strm;
strm << V.getLongDouble();
return strm.str();
}
break;
default:
std::ostringstream strm;
strm << V.getPtr();
return strm.str();
break;
}
}
else if (Ty->isIntegralOrEnumerationType()) {
std::ostringstream strm;
strm << V.getLL();
return strm.str();
}
else if (Ty->isFunctionType()) {
std::ostringstream strm;
strm << (const void *) &V;
return strm.str();
} else if (Ty->isPointerType()
|| Ty->isReferenceType()
|| Ty->isArrayType()) {
std::ostringstream strm;
strm << V.getPtr();
return strm.str();
}
else {
// struct case.
std::ostringstream strm;
strm << V.getPtr();
return strm.str();
}
}
static std::string getCastValueString(const Value &V) { static std::string getTypeString(const Value &V) {
std::ostringstream strm; std::ostringstream strm;
clang::ASTContext &C = V.getASTContext(); clang::ASTContext &C = V.getASTContext();
clang::QualType Ty = V.getType().getDesugaredType(C).getNonReferenceType(); clang::QualType Ty = V.getType().getDesugaredType(C).getNonReferenceType();
std::string type = cling::utils::TypeName::GetFullyQualifiedName(Ty, C); std::string type = cling::utils::TypeName::GetFullyQualifiedName(Ty, C);
std::ostringstream typeWithOptDeref; std::ostringstream typeWithOptDeref;
std::ostringstream suffix;
if (llvm::dyn_cast<clang::BuiltinType>(Ty.getCanonicalType())){ if (llvm::dyn_cast<clang::BuiltinType>(Ty.getCanonicalType())) {
typeWithOptDeref << "(" << type << ")"; typeWithOptDeref << "(" << type << "*)";
} else if (Ty->isPointerType()) { } else if (Ty->isPointerType()) {
if (Ty->getPointeeType()->isCharType()) { if (Ty->getPointeeType()->isCharType()) {
// Print char pointers as strings. // Print char pointers as strings.
typeWithOptDeref << "(" << type << ")"; typeWithOptDeref << "(" << type << "*)";
} else { } else {
// Fallback to void pointer for other pointers and print the address. // Fallback to void pointer for other pointers and print the address.
typeWithOptDeref << "(void*)"; typeWithOptDeref << "(const void**)";
} }
} else if (Ty->isArrayType()) { }
const clang::ArrayType* ArrTy = Ty->getAsArrayTypeUnsafe(); else if (Ty->isArrayType()) {
const clang::ArrayType *ArrTy = Ty->getAsArrayTypeUnsafe();
clang::QualType ElementTy = ArrTy->getElementType(); clang::QualType ElementTy = ArrTy->getElementType();
// Not implementing printing as string in case of char ElementTy // In case of char ElementTy, printing as string
// Can be done like this: typeWithOptDeref << "(const char void*)"; if (ElementTy->isCharType()) {
if (Ty->isConstantArrayType()) { typeWithOptDeref << "(const char **)";
const clang::ConstantArrayType* CArrTy = C.getAsConstantArrayType(Ty); } else if (Ty->isConstantArrayType()) {
const llvm::APInt& APSize = CArrTy->getSize(); const clang::ConstantArrayType *CArrTy = C.getAsConstantArrayType(Ty);
size_t size = (size_t)APSize.getZExtValue(); const llvm::APInt &APSize = CArrTy->getSize();
size_t size = (size_t) APSize.getZExtValue();
// typeWithOptDeref example for int[40] array: "((int(&)[40])*(void*)0x5c8f260)" // typeWithOptDeref example for int[40] array: "((int(*)[40])*(void**)0x5c8f260)"
typeWithOptDeref << "(" << cling::utils::TypeName::GetFullyQualifiedName(ElementTy, C) << "(&)[" << size << "])*(void*)"; typeWithOptDeref << "(" << cling::utils::TypeName::GetFullyQualifiedName(ElementTy, C) << "(*)[" << size << "])*(void**)";
} else { } else {
typeWithOptDeref << "(void*)"; typeWithOptDeref << "(void**)";
} }
} else { }
else {
// In other cases, dereference the address of the object. // In other cases, dereference the address of the object.
// If no overload or specific template matches, // If no overload or specific template matches,
// the general template will be used which only prints the address. // the general template will be used which only prints the address.
typeWithOptDeref << "*(" << type << "*)"; typeWithOptDeref << "*(" << type << "**)";
} }
strm << typeWithOptDeref.str() << getValueString(V) << suffix.str(); strm << typeWithOptDeref.str();
return strm.str(); return strm.str();
} }
static std::string printEnumValue(const Value &V){ template<typename T>
static std::string executePrintValue(const Value &V, const T &val) {
Interpreter *Interp = V.getInterpreter();
std::stringstream printValueSS;
printValueSS << "cling::printValue(";
printValueSS << getTypeString(V);
printValueSS << (const void *) &val;
printValueSS << ");";
Value printValueV;
Interp->evaluate(printValueSS.str(), printValueV);
assert(printValueV.isValid() && "Must return valid value.");
return *(std::string *) printValueV.getPtr();
}
static std::string invokePrintValueOverload(const Value &V) {
clang::ASTContext &C = V.getASTContext();
clang::QualType Ty = V.getType().getDesugaredType(C);
if (const clang::BuiltinType *BT
= llvm::dyn_cast<clang::BuiltinType>(Ty.getCanonicalType())) {
switch (BT->getKind()) {
case clang::BuiltinType::Bool:
return executePrintValue<bool>(V, V.getLL());
case clang::BuiltinType::Char_S:
return executePrintValue<signed char>(V, V.getLL());
case clang::BuiltinType::SChar:
return executePrintValue<signed char>(V, V.getLL());
case clang::BuiltinType::Short:
return executePrintValue<short>(V, V.getLL());
case clang::BuiltinType::Int:
return executePrintValue<int>(V, V.getLL());
case clang::BuiltinType::Long:
return executePrintValue<long>(V, V.getLL());
case clang::BuiltinType::LongLong:
return executePrintValue<long long>(V, V.getLL());
case clang::BuiltinType::Char_U:
return executePrintValue<unsigned char>(V, V.getULL());
case clang::BuiltinType::UChar:
return executePrintValue<unsigned char>(V, V.getULL());
case clang::BuiltinType::UShort:
return executePrintValue<unsigned short>(V, V.getULL());
case clang::BuiltinType::UInt:
return executePrintValue<unsigned int>(V, V.getULL());
case clang::BuiltinType::ULong:
return executePrintValue<unsigned long>(V, V.getULL());
case clang::BuiltinType::ULongLong:
return executePrintValue<unsigned long long>(V, V.getULL());
case clang::BuiltinType::Float:
return executePrintValue<float>(V, V.getFloat());
case clang::BuiltinType::Double:
return executePrintValue<double>(V, V.getDouble());
case clang::BuiltinType::LongDouble:
return executePrintValue<long double>(V, V.getLongDouble());
default:
return executePrintValue<void *>(V, V.getPtr());
}
}
else if (Ty->isIntegralOrEnumerationType()) {
return executePrintValue<long long>(V, V.getLL());
}
else if (Ty->isFunctionType()) {
return executePrintValue<const void *>(V, &V);
}
else if (Ty->isPointerType()
|| Ty->isReferenceType()
|| Ty->isArrayType()) {
return executePrintValue<void *>(V, V.getPtr());
}
else {
// struct case.
return executePrintValue<void *>(V, V.getPtr());
}
}
static std::string printEnumValue(const Value &V) {
std::stringstream enumString; std::stringstream enumString;
clang::ASTContext& C = V.getASTContext(); clang::ASTContext &C = V.getASTContext();
clang::QualType Ty = V.getType().getDesugaredType(C); clang::QualType Ty = V.getType().getDesugaredType(C);
const clang::EnumType *EnumTy = Ty.getNonReferenceType()->getAs<clang::EnumType>(); const clang::EnumType *EnumTy = Ty.getNonReferenceType()->getAs<clang::EnumType>();
if(EnumTy != 0) { assert(EnumTy && "ValuePrinter.cpp: ERROR, printEnumValue invoked for a non enum type.");
clang::EnumDecl *ED = EnumTy->getDecl(); clang::EnumDecl *ED = EnumTy->getDecl();
uint64_t value = *(const uint64_t *) &V; uint64_t value = *(const uint64_t *) &V;
bool IsFirst = true; bool IsFirst = true;
llvm::APSInt ValAsAPSInt = C.MakeIntValue(value, Ty); llvm::APSInt ValAsAPSInt = C.MakeIntValue(value, Ty);
for (clang::EnumDecl::enumerator_iterator I = ED->enumerator_begin(), for (clang::EnumDecl::enumerator_iterator I = ED->enumerator_begin(),
E = ED->enumerator_end(); I != E; ++I) { E = ED->enumerator_end(); I != E; ++I) {
if (I->getInitVal() == ValAsAPSInt) { if (I->getInitVal() == ValAsAPSInt) {
if (!IsFirst) { if (!IsFirst) {
enumString << " ? "; enumString << " ? ";
}
enumString << "(" << I->getQualifiedNameAsString() << ")";
IsFirst = false;
} }
enumString << "(" << I->getQualifiedNameAsString() << ")";
IsFirst = false;
} }
enumString << " : (int) " << ValAsAPSInt.toString(/*Radix = */10);
} else {
// Should not happen, we check for isEnumeralType before invoking this func
enumString << "ERROR: Value is not of enum type!";
} }
enumString << " : (int) " << ValAsAPSInt.toString(/*Radix = */10);
return enumString.str(); return enumString.str();
} }
static std::string printFunctionValue(const Value &V, const void* ptr, clang::QualType Ty) { static std::string printFunctionValue(const Value &V, const void *ptr, clang::QualType Ty) {
std::string functionString; std::string functionString;
llvm::raw_string_ostream o(functionString); llvm::raw_string_ostream o(functionString);
o << "Function @" << ptr << '\n'; o << "Function @" << ptr << '\n';
clang::ASTContext& C = V.getASTContext(); clang::ASTContext &C = V.getASTContext();
Interpreter& Interp = *const_cast<Interpreter*>(V.getInterpreter()); Interpreter &Interp = *const_cast<Interpreter *>(V.getInterpreter());
const Transaction* T = Interp.getLastTransaction(); const Transaction *T = Interp.getLastTransaction();
assert(T->getWrapperFD() && "Must have a wrapper."); assert(T->getWrapperFD() && "Must have a wrapper.");
clang::FunctionDecl* WrapperFD = T->getWrapperFD(); clang::FunctionDecl *WrapperFD = T->getWrapperFD();
const clang::FunctionDecl* FD = 0; const clang::FunctionDecl *FD = 0;
// CE should be the setValueNoAlloc call expr. // CE should be the setValueNoAlloc call expr.
if (const clang::CallExpr* CallE if (const clang::CallExpr *CallE
= llvm::dyn_cast_or_null<clang::CallExpr>( = llvm::dyn_cast_or_null<clang::CallExpr>(
utils::Analyze::GetOrCreateLastExpr(WrapperFD, utils::Analyze::GetOrCreateLastExpr(WrapperFD,
/*foundAtPos*/0, /*foundAtPos*/0,
/*omitDS*/false, /*omitDS*/false,
&Interp.getSema()))) { &Interp.getSema()))) {
if (const clang::FunctionDecl* FDsetValue if (const clang::FunctionDecl *FDsetValue
= llvm::dyn_cast_or_null<clang::FunctionDecl>(CallE->getCalleeDecl())){ = llvm::dyn_cast_or_null<clang::FunctionDecl>(CallE->getCalleeDecl())) {
if (FDsetValue->getNameAsString() == "setValueNoAlloc" && if (FDsetValue->getNameAsString() == "setValueNoAlloc" &&
CallE->getNumArgs() == 5) { CallE->getNumArgs() == 5) {
const clang::Expr* Arg4 = CallE->getArg(4); const clang::Expr *Arg4 = CallE->getArg(4);
while (const clang::CastExpr* CastE while (const clang::CastExpr *CastE
= clang::dyn_cast<clang::CastExpr>(Arg4)) = clang::dyn_cast<clang::CastExpr>(Arg4))
Arg4 = CastE->getSubExpr(); Arg4 = CastE->getSubExpr();
if (const clang::DeclRefExpr* DeclRefExp if (const clang::DeclRefExpr *DeclRefExp
= llvm::dyn_cast<clang::DeclRefExpr>(Arg4)) = llvm::dyn_cast<clang::DeclRefExpr>(Arg4))
FD = llvm::dyn_cast<clang::FunctionDecl>(DeclRefExp->getDecl()); FD = llvm::dyn_cast<clang::FunctionDecl>(DeclRefExp->getDecl());
} }
@ -285,11 +283,11 @@ static std::string printFunctionValue(const Value &V, const void* ptr, clang::Qu
if (FD) { if (FD) {
clang::SourceRange SRange = FD->getSourceRange(); clang::SourceRange SRange = FD->getSourceRange();
const char* cBegin = 0; const char *cBegin = 0;
const char* cEnd = 0; const char *cEnd = 0;
bool Invalid; bool Invalid;
if (SRange.isValid()) { if (SRange.isValid()) {
clang::SourceManager& SM = C.getSourceManager(); clang::SourceManager &SM = C.getSourceManager();
clang::SourceLocation LocBegin = SRange.getBegin(); clang::SourceLocation LocBegin = SRange.getBegin();
LocBegin = SM.getExpansionRange(LocBegin).first; LocBegin = SM.getExpansionRange(LocBegin).first;
o << " at " << SM.getFilename(LocBegin); o << " at " << SM.getFilename(LocBegin);
@ -312,7 +310,7 @@ static std::string printFunctionValue(const Value &V, const void* ptr, clang::Qu
if (cBegin && cEnd && cEnd > cBegin && cEnd - cBegin < 16 * 1024) { if (cBegin && cEnd && cEnd > cBegin && cEnd - cBegin < 16 * 1024) {
o << llvm::StringRef(cBegin, cEnd - cBegin + 1); o << llvm::StringRef(cBegin, cEnd - cBegin + 1);
} else { } else {
const clang::FunctionDecl* FDef; const clang::FunctionDecl *FDef;
if (FD->hasBody(FDef)) if (FD->hasBody(FDef))
FD = FDef; FD = FDef;
FD->print(o); FD->print(o);
@ -327,26 +325,14 @@ static std::string printFunctionValue(const Value &V, const void* ptr, clang::Qu
return functionString; return functionString;
} }
static std::string invokePrintValueOverload(const Value& V) { static std::string printUnpackedClingValue(const Value &V) {
Interpreter *Interp = V.getInterpreter();
std::stringstream printValueSS;
printValueSS << "cling::printValue(";
printValueSS << getCastValueString(V);
printValueSS << ");";
Value printValueV;
Interp->evaluate(printValueSS.str(), printValueV);
assert(printValueV.isValid() && "Must return valid value.");
return *(std::string*)printValueV.getPtr();
}
static std::string printUnpackedClingValue(const Value& V) {
std::stringstream strm; std::stringstream strm;
clang::ASTContext& C = V.getASTContext(); clang::ASTContext &C = V.getASTContext();
clang::QualType QT = V.getType(); clang::QualType QT = V.getType();
clang::QualType Ty = QT.getDesugaredType(C).getNonReferenceType(); clang::QualType Ty = QT.getDesugaredType(C).getNonReferenceType();
if(Ty-> isNullPtrType()) { if (Ty->isNullPtrType()) {
// special case nullptr_t // special case nullptr_t
strm << "nullptr_t"; strm << "nullptr_t";
} else if (Ty->isEnumeralType()) { } else if (Ty->isEnumeralType()) {
@ -358,8 +344,15 @@ static std::string printUnpackedClingValue(const Value& V) {
} else if ((Ty->isPointerType() || Ty->isMemberPointerType()) && Ty->getPointeeType()->isFunctionProtoType()) { } else if ((Ty->isPointerType() || Ty->isMemberPointerType()) && Ty->getPointeeType()->isFunctionProtoType()) {
// special case function printing, using compiled information // special case function printing, using compiled information
strm << printFunctionValue(V, V.getPtr(), Ty->getPointeeType()); strm << printFunctionValue(V, V.getPtr(), Ty->getPointeeType());
} else if (clang::CXXRecordDecl *CXXRD = Ty->getAsCXXRecordDecl()) {
if (CXXRD->isLambda()) {
strm << "@" << V.getPtr();
} else {
// default case, modular printing using cling::printValue
strm << invokePrintValueOverload(V);
}
} else { } else {
// normal case, modular printing using cling::printValue // default case, modular printing using cling::printValue
strm << invokePrintValueOverload(V); strm << invokePrintValueOverload(V);
} }
@ -368,26 +361,39 @@ static std::string printUnpackedClingValue(const Value& V) {
namespace cling { namespace cling {
std::string printValue(void* ptr) { // General fallback - prints the address
std::string printValue(const void *ptr) {
if (!ptr) { if (!ptr) {
return "nullptr"; return "nullptr";
} else { } else {
std::ostringstream strm; std::ostringstream strm;
strm << ptr; strm << "@" << ptr;
if (!isAddressValid(ptr)) if (!isAddressValid(ptr))
strm << " <invalid memory address>"; strm << " <invalid memory address>";
return strm.str(); return strm.str();
} }
} }
// void pointer
std::string printValue(const void **ptr) {
if (!*ptr) {
return "nullptr";
} else {
std::ostringstream strm;
strm << *ptr;
if (!isAddressValid(*ptr))
strm << " <invalid memory address>";
return strm.str();
}
}
// Bool // Bool
std::string printValue(bool val) { std::string printValue(const bool *val) {
return val ? "true" : "false"; return val ? "true" : "false";
} }
// Chars // Chars
static std::string printChar(signed char val, bool apostrophe) {
static std::string printChar(signed char val, bool apostrophe){
std::ostringstream strm; std::ostringstream strm;
if (val > 0x1F && val < 0x7F) { if (val > 0x1F && val < 0x7F) {
if (apostrophe) if (apostrophe)
@ -401,94 +407,95 @@ namespace cling {
return strm.str(); return strm.str();
} }
std::string printValue(char val) { std::string printValue(const char *val) {
return printChar(val, true); return printChar(*val, true);
} }
std::string printValue(signed char val) { std::string printValue(const signed char *val) {
return printChar(val, true); return printChar(*val, true);
} }
std::string printValue(unsigned char val) { std::string printValue(const unsigned char *val) {
return printChar(val, true); return printChar(*val, true);
} }
// Ints // Ints
std::string printValue(short val) { std::string printValue(const short *val) {
std::ostringstream strm; std::ostringstream strm;
strm << val; strm << *val;
return strm.str(); return strm.str();
} }
std::string printValue(unsigned short val) { std::string printValue(const unsigned short *val) {
std::ostringstream strm; std::ostringstream strm;
strm << val; strm << *val;
return strm.str(); return strm.str();
} }
std::string printValue(int val) { std::string printValue(const int *val) {
std::ostringstream strm; std::ostringstream strm;
strm << val; strm << *val;
return strm.str(); return strm.str();
} }
std::string printValue(unsigned int val) { std::string printValue(const unsigned int *val) {
std::ostringstream strm; std::ostringstream strm;
strm << val; strm << *val;
return strm.str(); return strm.str();
} }
std::string printValue(long val) { std::string printValue(const long *val) {
std::ostringstream strm; std::ostringstream strm;
strm << val; strm << *val;
return strm.str(); return strm.str();
} }
std::string printValue(unsigned long val) { std::string printValue(const unsigned long *val) {
std::ostringstream strm; std::ostringstream strm;
strm << val; strm << *val;
return strm.str(); return strm.str();
} }
std::string printValue(long long val) { std::string printValue(const long long *val) {
std::ostringstream strm; std::ostringstream strm;
strm << val; strm << *val;
return strm.str(); return strm.str();
} }
std::string printValue(unsigned long long val) { std::string printValue(const unsigned long long *val) {
std::ostringstream strm; std::ostringstream strm;
strm << val; strm << *val;
return strm.str(); return strm.str();
} }
std::string printValue(float val) { // Reals
std::string printValue(const float *val) {
std::ostringstream strm; std::ostringstream strm;
strm << std::showpoint << val << "f"; strm << std::showpoint << *val << "f";
return strm.str(); return strm.str();
} }
std::string printValue(double val) { std::string printValue(const double *val) {
std::ostringstream strm; std::ostringstream strm;
strm << std::showpoint << val; strm << std::showpoint << *val;
return strm.str(); return strm.str();
} }
std::string printValue(long double val) { std::string printValue(const long double *val) {
std::ostringstream strm; std::ostringstream strm;
strm << val << "L"; strm << *val << "L";
return strm.str(); return strm.str();
} }
// Char pointers // Char pointers
std::string printValue(const char *const val) { std::string printValue(const char *const *val) {
if (!val) { if (!*val) {
return "nullptr"; return "nullptr";
} else { } else {
std::ostringstream strm; std::ostringstream strm;
strm << "\""; strm << "\"";
// 10000 limit to prevent potential printing of the whole RAM / inf loop // 10000 limit to prevent potential printing of the whole RAM / inf loop
for (const char *cobj = val; *cobj != 0 && cobj-val < 10000; ++cobj) { for (const char *cobj = *val; *cobj != 0 && cobj - *val < 10000; ++cobj) {
strm << printChar(*cobj, false); strm << printChar(*cobj, false);
} }
strm << "\""; strm << "\"";
@ -496,29 +503,30 @@ namespace cling {
} }
} }
std::string printValue(char *val) { std::string printValue(const char **val) {
return printValue((const char *const) val); return printValue((const char *const *) val);
} }
// std::string // std::string
std::string printValue(const std::string & val) { std::string printValue(const std::string *val) {
return "\"" + val + "\""; return "\"" + *val + "\"";
} }
std::string printValue(const Value &value) { // cling::Value
std::string printValue(const Value *value) {
std::ostringstream strm; std::ostringstream strm;
if (!value.isValid()) { if (!value->isValid()) {
strm << "<<<invalid>>> @" << &value; strm << "<<<invalid>>> @" << value;
} else { } else {
clang::ASTContext& C = value.getASTContext(); clang::ASTContext &C = value->getASTContext();
clang::QualType QT = value.getType(); clang::QualType QT = value->getType();
strm << "boxes ["; strm << "boxes [";
strm << "(" strm << "("
<< cling::utils::TypeName::GetFullyQualifiedName(QT, C) << cling::utils::TypeName::GetFullyQualifiedName(QT, C)
<< ") "; << ") ";
if (!QT->isVoidType()) { if (!QT->isVoidType()) {
strm << printUnpackedClingValue(value); strm << printUnpackedClingValue(*value);
} }
strm << "]"; strm << "]";
} }
@ -526,41 +534,41 @@ namespace cling {
return strm.str(); return strm.str();
} }
namespace valuePrinterInternal { namespace valuePrinterInternal {
std::string printTypeInternal(const Value& V) { std::string printTypeInternal(const Value &V) {
using namespace clang; using namespace clang;
std::ostringstream strm; std::ostringstream strm;
clang::ASTContext& C = V.getASTContext(); clang::ASTContext &C = V.getASTContext();
QualType QT = V.getType().getNonReferenceType(); QualType QT = V.getType().getNonReferenceType();
std::string ValueTyStr; std::string ValueTyStr;
if (const TypedefType* TDTy = dyn_cast<TypedefType>(QT)) if (const TypedefType *TDTy = dyn_cast<TypedefType>(QT))
ValueTyStr = TDTy->getDecl()->getQualifiedNameAsString(); ValueTyStr = TDTy->getDecl()->getQualifiedNameAsString();
else if (const TagType* TTy = dyn_cast<TagType>(QT)) else if (const TagType *TTy = dyn_cast<TagType>(QT))
ValueTyStr = TTy->getDecl()->getQualifiedNameAsString(); ValueTyStr = TTy->getDecl()->getQualifiedNameAsString();
if (ValueTyStr.empty()) if (ValueTyStr.empty())
ValueTyStr = cling::utils::TypeName::GetFullyQualifiedName(QT, C); ValueTyStr = cling::utils::TypeName::GetFullyQualifiedName(QT, C);
else if (QT.hasQualifiers()) else if (QT.hasQualifiers())
ValueTyStr = QT.getQualifiers().getAsString() + " " + ValueTyStr; ValueTyStr = QT.getQualifiers().getAsString() + " " + ValueTyStr;
strm << "("; strm << "(";
strm << ValueTyStr; strm << ValueTyStr;
if (V.getType()->isReferenceType()) if (V.getType()->isReferenceType())
strm << " &"; strm << " &";
strm << ")"; strm << ")";
return strm.str(); return strm.str();
}
std::string printValueInternal(const Value& V) {
static bool includedRuntimePrintValue = false; // initialized only once as a static function variable
// Include "RuntimePrintValue.h" only on the first printing.
// This keeps the interpreter lightweight and reduces the startup time.
if(!includedRuntimePrintValue) {
V.getInterpreter()->declare("#include \"cling/Interpreter/RuntimePrintValue.h\"");
includedRuntimePrintValue = true;
} }
return printUnpackedClingValue(V);
} std::string printValueInternal(const Value &V) {
} // end namespace valuePrinterInternal static bool includedRuntimePrintValue = false; // initialized only once as a static function variable
// Include "RuntimePrintValue.h" only on the first printing.
// This keeps the interpreter lightweight and reduces the startup time.
if (!includedRuntimePrintValue) {
V.getInterpreter()->declare("#include \"cling/Interpreter/RuntimePrintValue.h\"");
includedRuntimePrintValue = true;
}
return printUnpackedClingValue(V);
}
} // end namespace valuePrinterInternal
} // end namespace cling } // end namespace cling