//------------------------------------------------------------------------------ // CLING - the C++ LLVM-based InterpreterG :) // author: Vassil Vassilev // // This file is dual-licensed: you can choose to license it under the University // of Illinois Open Source License or the GNU Lesser General Public License. See // LICENSE.TXT for details. //------------------------------------------------------------------------------ #include "cling/Interpreter/Value.h" #include "cling/Interpreter/CValuePrinter.h" #include "cling/Interpreter/Interpreter.h" #include "cling/Interpreter/Transaction.h" #include "cling/Interpreter/Value.h" #include "cling/Utils/AST.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" #include "clang/Frontend/CompilerInstance.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ExecutionEngine/GenericValue.h" #include #include #include // Fragment copied from LLVM's raw_ostream.cpp #if defined(_MSC_VER) #ifndef STDIN_FILENO # define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO # define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO # define STDERR_FILENO 2 #endif #else //#if defined(HAVE_UNISTD_H) # include //#endif #endif // For address validation #ifdef LLVM_ON_WIN32 #include #else #include #endif using namespace cling; // Implements the CValuePrinter interface. extern "C" void cling_PrintValue(void * /*cling::Value**/ V) { //Value* value = (Value*)V; // We need stream that doesn't close its file descriptor, thus we are not // using llvm::outs. Keeping file descriptor open we will be able to use // the results in pipes (Savannah #99234). //llvm::raw_fd_ostream outs (STDOUT_FILENO, /*ShouldClose*/false); //std::string typeStr = printTypeInternal(*value); //std::string valueStr = printValueInternal(*value); } // Checking whether the pointer points to a valid memory location // Used for checking of void* output // Should be moved to earlier stages (ex. IR) in the future static bool isAddressValid(const void *P) { if (!P || P == (void *) -1) return false; #ifdef LLVM_ON_WIN32 MEMORY_BASIC_INFORMATION MBI; if (!VirtualQuery(P, &MBI, sizeof(MBI))) return false; if (MBI.State != MEM_COMMIT) return false; return true; #else // There is a POSIX way of finding whether an address can be accessed for // reading: write() will return EFAULT if not. int FD[2]; if (pipe(FD)) return false; // error in pipe()? Be conservative... int NBytes = write(FD[1], P, 1/*byte*/); close(FD[0]); close(FD[1]); if (NBytes != 1) { assert(errno == EFAULT && "unexpected pipe write error"); return false; } return true; #endif } static std::string getTypeString(const Value &V) { std::ostringstream strm; clang::ASTContext &C = V.getASTContext(); clang::QualType Ty = V.getType().getDesugaredType(C).getNonReferenceType(); std::string type = cling::utils::TypeName::GetFullyQualifiedName(Ty, C); std::ostringstream typeWithOptDeref; if (llvm::dyn_cast(Ty.getCanonicalType())) { typeWithOptDeref << "(" << type << "*)"; } else if (Ty->isPointerType()) { if (Ty->getPointeeType()->isCharType()) { // Print char pointers as strings. typeWithOptDeref << "(" << type << "*)"; } else { // Fallback to void pointer for other pointers and print the address. typeWithOptDeref << "(const void**)"; } } else if (Ty->isArrayType()) { const clang::ArrayType *ArrTy = Ty->getAsArrayTypeUnsafe(); clang::QualType ElementTy = ArrTy->getElementType(); // In case of char ElementTy, printing as string if (ElementTy->isCharType()) { typeWithOptDeref << "(const char **)"; } else if (Ty->isConstantArrayType()) { const clang::ConstantArrayType *CArrTy = C.getAsConstantArrayType(Ty); const llvm::APInt &APSize = CArrTy->getSize(); size_t size = (size_t) APSize.getZExtValue(); // typeWithOptDeref example for int[40] array: "((int(*)[40])*(void**)0x5c8f260)" typeWithOptDeref << "(" << cling::utils::TypeName::GetFullyQualifiedName(ElementTy, C) << "(*)[" << size << "])*(void**)"; } else { typeWithOptDeref << "(void**)"; } } else { // In other cases, dereference the address of the object. // If no overload or specific template matches, // the general template will be used which only prints the address. typeWithOptDeref << "*(" << type << "**)"; } strm << typeWithOptDeref.str(); return strm.str(); } template 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).getCanonicalType(); if (const clang::BuiltinType *BT = llvm::dyn_cast(Ty.getTypePtr())) { switch (BT->getKind()) { case clang::BuiltinType::Bool: return executePrintValue(V, V.getLL()); case clang::BuiltinType::Char_S: return executePrintValue(V, V.getLL()); case clang::BuiltinType::SChar: return executePrintValue(V, V.getLL()); case clang::BuiltinType::Short: return executePrintValue(V, V.getLL()); case clang::BuiltinType::Int: return executePrintValue(V, V.getLL()); case clang::BuiltinType::Long: return executePrintValue(V, V.getLL()); case clang::BuiltinType::LongLong: return executePrintValue(V, V.getLL()); case clang::BuiltinType::Char_U: return executePrintValue(V, V.getULL()); case clang::BuiltinType::UChar: return executePrintValue(V, V.getULL()); case clang::BuiltinType::UShort: return executePrintValue(V, V.getULL()); case clang::BuiltinType::UInt: return executePrintValue(V, V.getULL()); case clang::BuiltinType::ULong: return executePrintValue(V, V.getULL()); case clang::BuiltinType::ULongLong: return executePrintValue(V, V.getULL()); case clang::BuiltinType::Float: return executePrintValue(V, V.getFloat()); case clang::BuiltinType::Double: return executePrintValue(V, V.getDouble()); case clang::BuiltinType::LongDouble: return executePrintValue(V, V.getLongDouble()); default: return executePrintValue(V, V.getPtr()); } } else if (Ty->isIntegralOrEnumerationType()) { return executePrintValue(V, V.getLL()); } else if (Ty->isFunctionType()) { return executePrintValue(V, &V); } else if (Ty->isPointerType() || Ty->isReferenceType() || Ty->isArrayType()) { return executePrintValue(V, V.getPtr()); } else { // struct case. return executePrintValue(V, V.getPtr()); } } static std::string printEnumValue(const Value &V) { std::stringstream enumString; clang::ASTContext &C = V.getASTContext(); clang::QualType Ty = V.getType().getDesugaredType(C); const clang::EnumType *EnumTy = Ty.getNonReferenceType()->getAs(); assert(EnumTy && "ValuePrinter.cpp: ERROR, printEnumValue invoked for a non enum type."); clang::EnumDecl *ED = EnumTy->getDecl(); uint64_t value = *(const uint64_t *) &V; bool IsFirst = true; llvm::APSInt ValAsAPSInt = C.MakeIntValue(value, Ty); for (clang::EnumDecl::enumerator_iterator I = ED->enumerator_begin(), E = ED->enumerator_end(); I != E; ++I) { if (I->getInitVal() == ValAsAPSInt) { if (!IsFirst) { enumString << " ? "; } enumString << "(" << I->getQualifiedNameAsString() << ")"; IsFirst = false; } } enumString << " : (int) " << ValAsAPSInt.toString(/*Radix = */10); return enumString.str(); } static std::string printFunctionValue(const Value &V, const void *ptr, clang::QualType Ty) { std::string functionString; llvm::raw_string_ostream o(functionString); o << "Function @" << ptr << '\n'; clang::ASTContext &C = V.getASTContext(); Interpreter &Interp = *const_cast(V.getInterpreter()); const Transaction *T = Interp.getLastTransaction(); assert(T->getWrapperFD() && "Must have a wrapper."); clang::FunctionDecl *WrapperFD = T->getWrapperFD(); const clang::FunctionDecl *FD = 0; // CE should be the setValueNoAlloc call expr. if (const clang::CallExpr *CallE = llvm::dyn_cast_or_null( utils::Analyze::GetOrCreateLastExpr(WrapperFD, /*foundAtPos*/0, /*omitDS*/false, &Interp.getSema()))) { if (const clang::FunctionDecl *FDsetValue = llvm::dyn_cast_or_null(CallE->getCalleeDecl())) { if (FDsetValue->getNameAsString() == "setValueNoAlloc" && CallE->getNumArgs() == 5) { const clang::Expr *Arg4 = CallE->getArg(4); while (const clang::CastExpr *CastE = clang::dyn_cast(Arg4)) Arg4 = CastE->getSubExpr(); if (const clang::DeclRefExpr *DeclRefExp = llvm::dyn_cast(Arg4)) FD = llvm::dyn_cast(DeclRefExp->getDecl()); } } } if (FD) { clang::SourceRange SRange = FD->getSourceRange(); const char *cBegin = 0; const char *cEnd = 0; bool Invalid; if (SRange.isValid()) { clang::SourceManager &SM = C.getSourceManager(); clang::SourceLocation LocBegin = SRange.getBegin(); LocBegin = SM.getExpansionRange(LocBegin).first; o << " at " << SM.getFilename(LocBegin); unsigned LineNo = SM.getSpellingLineNumber(LocBegin, &Invalid); if (!Invalid) o << ':' << LineNo; o << ":\n"; bool Invalid = false; cBegin = SM.getCharacterData(LocBegin, &Invalid); if (!Invalid) { clang::SourceLocation LocEnd = SRange.getEnd(); LocEnd = SM.getExpansionRange(LocEnd).second; cEnd = SM.getCharacterData(LocEnd, &Invalid); if (Invalid) cBegin = 0; } else { cBegin = 0; } } if (cBegin && cEnd && cEnd > cBegin && cEnd - cBegin < 16 * 1024) { o << llvm::StringRef(cBegin, cEnd - cBegin + 1); } else { const clang::FunctionDecl *FDef; if (FD->hasBody(FDef)) FD = FDef; FD->print(o); //const clang::FunctionDecl* FD // = llvm::cast(Ty)->getDecl(); } } // type-based print() never and decl-based print() sometimes does not include // a final newline: o << '\n'; functionString = o.str(); return functionString; } static std::string printUnpackedClingValue(const Value &V) { std::stringstream strm; clang::ASTContext &C = V.getASTContext(); clang::QualType QT = V.getType(); clang::QualType Ty = QT.getDesugaredType(C).getNonReferenceType(); if (Ty->isNullPtrType()) { // special case nullptr_t strm << "nullptr_t"; } else if (Ty->isEnumeralType()) { // special case enum printing, using compiled information strm << printEnumValue(V); } else if (Ty->isFunctionType()) { // special case function printing, using compiled information strm << printFunctionValue(V, &V, Ty); } else if ((Ty->isPointerType() || Ty->isMemberPointerType()) && Ty->getPointeeType()->isFunctionProtoType()) { // special case function printing, using compiled information 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 { // default case, modular printing using cling::printValue strm << invokePrintValueOverload(V); } return strm.str(); } namespace cling { // General fallback - prints the address std::string printValue(const void *ptr) { if (!ptr) { return "nullptr"; } else { std::ostringstream strm; strm << "@" << ptr; if (!isAddressValid(ptr)) strm << " "; 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 << " "; return strm.str(); } } // Bool std::string printValue(const bool *val) { return *val ? "true" : "false"; } // Chars static std::string printChar(signed char val, bool apostrophe) { std::ostringstream strm; if (val > 0x1F && val < 0x7F) { if (apostrophe) strm << "'"; strm << val; if (apostrophe) strm << "'"; } else { strm << "0x" << std::hex << (int) val; } return strm.str(); } std::string printValue(const char *val) { return printChar(*val, true); } std::string printValue(const signed char *val) { return printChar(*val, true); } std::string printValue(const unsigned char *val) { return printChar(*val, true); } // Ints std::string printValue(const short *val) { std::ostringstream strm; strm << *val; return strm.str(); } std::string printValue(const unsigned short *val) { std::ostringstream strm; strm << *val; return strm.str(); } std::string printValue(const int *val) { std::ostringstream strm; strm << *val; return strm.str(); } std::string printValue(const unsigned int *val) { std::ostringstream strm; strm << *val; return strm.str(); } std::string printValue(const long *val) { std::ostringstream strm; strm << *val; return strm.str(); } std::string printValue(const unsigned long *val) { std::ostringstream strm; strm << *val; return strm.str(); } std::string printValue(const long long *val) { std::ostringstream strm; strm << *val; return strm.str(); } std::string printValue(const unsigned long long *val) { std::ostringstream strm; strm << *val; return strm.str(); } // Reals std::string printValue(const float *val) { std::ostringstream strm; strm << std::showpoint << *val << "f"; return strm.str(); } std::string printValue(const double *val) { std::ostringstream strm; strm << std::showpoint << *val; return strm.str(); } std::string printValue(const long double *val) { std::ostringstream strm; strm << *val << "L"; return strm.str(); } // Char pointers std::string printValue(const char *const *val) { if (!*val) { return "nullptr"; } else { std::ostringstream strm; strm << "\""; // 10000 limit to prevent potential printing of the whole RAM / inf loop for (const char *cobj = *val; *cobj != 0 && cobj - *val < 10000; ++cobj) { strm << printChar(*cobj, false); } strm << "\""; return strm.str(); } } std::string printValue(const char **val) { return printValue((const char *const *) val); } // std::string std::string printValue(const std::string *val) { return "\"" + *val + "\""; } // cling::Value std::string printValue(const Value *value) { std::ostringstream strm; if (!value->isValid()) { strm << "<<>> @" << value; } else { clang::ASTContext &C = value->getASTContext(); clang::QualType QT = value->getType(); strm << "boxes ["; strm << "(" << cling::utils::TypeName::GetFullyQualifiedName(QT, C) << ") "; if (!QT->isVoidType()) { strm << printUnpackedClingValue(*value); } strm << "]"; } return strm.str(); } namespace valuePrinterInternal { std::string printTypeInternal(const Value &V) { using namespace clang; std::ostringstream strm; clang::ASTContext &C = V.getASTContext(); QualType QT = V.getType().getNonReferenceType(); std::string ValueTyStr; if (const TypedefType *TDTy = dyn_cast(QT)) ValueTyStr = TDTy->getDecl()->getQualifiedNameAsString(); else if (const TagType *TTy = dyn_cast(QT)) ValueTyStr = TTy->getDecl()->getQualifiedNameAsString(); if (ValueTyStr.empty()) ValueTyStr = cling::utils::TypeName::GetFullyQualifiedName(QT, C); else if (QT.hasQualifiers()) ValueTyStr = QT.getQualifiers().getAsString() + " " + ValueTyStr; strm << "("; strm << ValueTyStr; if (V.getType()->isReferenceType()) strm << " &"; strm << ")"; 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); } } // end namespace valuePrinterInternal } // end namespace cling