Improve consistency in cling::Value.
The improvements are: * We provide getX and setX interfaces instead of returning the address for the non const methods. This allows us to be more consistent in terms of lifetimes as now users cannot take the address of block of memory which can be freed by the cling::Value. * We remove the storage types and we rely on the clang::Type which we have in the cling::Value.
This commit is contained in:
parent
e18104aa12
commit
07c4469328
@ -112,19 +112,8 @@ namespace cling {
|
||||
/// \brief The actual value.
|
||||
Storage m_Storage;
|
||||
|
||||
enum EStorageType {
|
||||
kSignedIntegerOrEnumerationType,
|
||||
kUnsignedIntegerOrEnumerationType,
|
||||
kDoubleType,
|
||||
kFloatType,
|
||||
kLongDoubleType,
|
||||
kPointerType,
|
||||
kManagedAllocation,
|
||||
kUnsupportedType
|
||||
};
|
||||
|
||||
/// \brief Which part in m_Storage is active.
|
||||
EStorageType m_StorageType;
|
||||
/// \brief If the \c Value class needs to alloc and dealloc memory.
|
||||
bool m_NeedsManagedAlloc;
|
||||
|
||||
/// \brief The value's type, stored as opaque void* to reduce
|
||||
/// dependencies.
|
||||
@ -177,24 +166,17 @@ namespace cling {
|
||||
/// dependencies.
|
||||
void AssertOnUnsupportedTypeCast() const;
|
||||
|
||||
bool hasPointerType() const;
|
||||
bool hasBuiltinType() const;
|
||||
|
||||
// Allow simplisticCastAs to be partially specialized.
|
||||
template<typename T>
|
||||
struct CastFwd {
|
||||
static T cast(const Value& V) {
|
||||
EStorageType storageType = V.getStorageType();
|
||||
switch (storageType) {
|
||||
case kSignedIntegerOrEnumerationType:
|
||||
case kUnsignedIntegerOrEnumerationType:
|
||||
case kDoubleType:
|
||||
case kFloatType:
|
||||
case kLongDoubleType:
|
||||
return (T) V.getAs<T>();
|
||||
case kPointerType:
|
||||
case kManagedAllocation:
|
||||
if (V.needsManagedAllocation() || V.hasPointerType())
|
||||
return (T) (uintptr_t) V.getAs<void*>();
|
||||
case kUnsupportedType:
|
||||
break; // assert
|
||||
}
|
||||
if (V.hasBuiltinType())
|
||||
return (T) V.getAs<T>();
|
||||
V.AssertOnUnsupportedTypeCast();
|
||||
return T();
|
||||
}
|
||||
@ -203,33 +185,31 @@ namespace cling {
|
||||
template<typename T>
|
||||
struct CastFwd<T*> {
|
||||
static T* cast(const Value& V) {
|
||||
EStorageType storageType = V.getStorageType();
|
||||
if (storageType == kPointerType || storageType == kManagedAllocation)
|
||||
if (V.needsManagedAllocation() || V.hasPointerType())
|
||||
return (T*) (uintptr_t) V.getAs<void*>();
|
||||
V.AssertOnUnsupportedTypeCast();
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
Value(void* QualTypeAsOpaquePtr, Interpreter& Interp, EStorageType stType):
|
||||
m_StorageType(stType),
|
||||
m_Type(QualTypeAsOpaquePtr),
|
||||
m_Interpreter(&Interp) {
|
||||
}
|
||||
// Value(void* QualTypeAsOpaquePtr, Interpreter& Interp):
|
||||
// m_Type(QualTypeAsOpaquePtr),
|
||||
// m_Interpreter(&Interp) {
|
||||
// }
|
||||
|
||||
public:
|
||||
/// \brief Default constructor, creates a value that IsInvalid().
|
||||
Value():
|
||||
m_StorageType(kUnsupportedType), m_Type(nullptr),
|
||||
m_NeedsManagedAlloc(false), m_Type(nullptr),
|
||||
m_Interpreter(nullptr) {}
|
||||
/// \brief Copy a value.
|
||||
Value(const Value& other);
|
||||
/// \brief Move a value.
|
||||
Value(Value&& other):
|
||||
m_Storage(other.m_Storage), m_StorageType(other.m_StorageType),
|
||||
m_Storage(other.m_Storage), m_NeedsManagedAlloc(other.m_NeedsManagedAlloc),
|
||||
m_Type(other.m_Type), m_Interpreter(other.m_Interpreter) {
|
||||
// Invalidate other so it will not release.
|
||||
other.m_StorageType = kUnsupportedType;
|
||||
other.m_NeedsManagedAlloc = false;
|
||||
}
|
||||
|
||||
/// \brief Construct a valid but uninitialized Value. After this call the
|
||||
@ -263,7 +243,7 @@ namespace cling {
|
||||
/// by the m_Storage member is insufficient, or a non-trivial destructor
|
||||
/// must be called.
|
||||
bool needsManagedAllocation() const {
|
||||
return getStorageType() == kManagedAllocation;
|
||||
return m_NeedsManagedAlloc;
|
||||
}
|
||||
|
||||
/// \brief Determine whether the Value has been set.
|
||||
@ -284,22 +264,21 @@ namespace cling {
|
||||
/// \brief Get a reference to the value without type checking.
|
||||
/// T *must* correspond to type. Else use simplisticCastAs()!
|
||||
template <typename T> T getAs() const;
|
||||
template <typename T> T& getAs();
|
||||
|
||||
void*& getPtr() { return m_Storage.m_Ptr; }
|
||||
// FIXME: If the cling::Value is destroyed and it handed out an address that
|
||||
// might be accessing invalid memory.
|
||||
void** getPtrAddress() { return &m_Storage.m_Ptr; }
|
||||
void* getPtr() const { return m_Storage.m_Ptr; }
|
||||
void setPtr(void* Val) { m_Storage.m_Ptr = Val; }
|
||||
|
||||
double& getDouble() { return m_Storage.m_Double; }
|
||||
long double& getLongDouble() { return m_Storage.m_LongDouble; }
|
||||
float& getFloat() { return m_Storage.m_Float; }
|
||||
long long& getLL() { return m_Storage.m_LongLong; }
|
||||
unsigned long long& getULL() { return m_Storage.m_ULongLong; }
|
||||
// FIXME: Add AssertInvalid("##name")
|
||||
#define X(type, name) \
|
||||
type get##name() const {return m_Storage.m_##name;} \
|
||||
void set##name(type Val) {m_Storage.m_##name = Val;} \
|
||||
|
||||
double getDouble() const { return m_Storage.m_Double; }
|
||||
long double getLongDouble() const { return m_Storage.m_LongDouble; }
|
||||
float getFloat() const { return m_Storage.m_Float; }
|
||||
long long getLL() const { return m_Storage.m_LongLong; }
|
||||
unsigned long long getULL() const { return m_Storage.m_ULongLong; }
|
||||
BUILTIN_TYPES
|
||||
|
||||
#undef X
|
||||
|
||||
/// \brief Get the value with cast.
|
||||
//
|
||||
@ -323,12 +302,9 @@ namespace cling {
|
||||
void dump(bool escape = true) const;
|
||||
};
|
||||
|
||||
//template <> void*& Value::getAs() { return m_Storage.m_Ptr; }
|
||||
template <> inline void* Value::getAs() const { return m_Storage.m_Ptr; }
|
||||
template <> inline void*& Value::getAs() { return m_Storage.m_Ptr; }
|
||||
template <> void* Value::getAs() const;
|
||||
#define X(type, name) \
|
||||
template <> type Value::getAs() const; \
|
||||
template <> type& Value::getAs(); \
|
||||
|
||||
BUILTIN_TYPES
|
||||
|
||||
|
@ -135,15 +135,25 @@ namespace {
|
||||
namespace cling {
|
||||
|
||||
Value::Value(const Value& other):
|
||||
m_Storage(other.m_Storage), m_StorageType(other.m_StorageType),
|
||||
m_Storage(other.m_Storage), m_NeedsManagedAlloc(other.m_NeedsManagedAlloc),
|
||||
m_Type(other.m_Type), m_Interpreter(other.m_Interpreter) {
|
||||
if (other.needsManagedAllocation())
|
||||
AllocatedValue::getFromPayload(m_Storage.m_Ptr)->Retain();
|
||||
}
|
||||
|
||||
static bool needsManagedAlloc(clang::QualType QT) {
|
||||
clang::QualType Canon = QT.getCanonicalType();
|
||||
if (Canon->isPointerType() || Canon->isObjectType() ||
|
||||
Canon->isReferenceType())
|
||||
if (Canon->isRecordType() || Canon->isConstantArrayType() ||
|
||||
Canon->isMemberPointerType())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
Value::Value(clang::QualType clangTy, Interpreter& Interp):
|
||||
m_StorageType(determineStorageType(clangTy)),
|
||||
m_Type(clangTy.getAsOpaquePtr()),
|
||||
m_NeedsManagedAlloc(needsManagedAlloc(clangTy)),
|
||||
m_Type(clangTy.getAsOpaquePtr()), // FIXME: What happens clangTy is freed?
|
||||
m_Interpreter(&Interp) {
|
||||
if (needsManagedAllocation())
|
||||
ManagedAllocate();
|
||||
@ -157,7 +167,7 @@ namespace cling {
|
||||
// Retain new one.
|
||||
m_Type = other.m_Type;
|
||||
m_Storage = other.m_Storage;
|
||||
m_StorageType = other.m_StorageType;
|
||||
m_NeedsManagedAlloc = other.m_NeedsManagedAlloc;
|
||||
m_Interpreter = other.m_Interpreter;
|
||||
if (needsManagedAllocation())
|
||||
AllocatedValue::getFromPayload(m_Storage.m_Ptr)->Retain();
|
||||
@ -172,10 +182,10 @@ namespace cling {
|
||||
// Move new one.
|
||||
m_Type = other.m_Type;
|
||||
m_Storage = other.m_Storage;
|
||||
m_StorageType = other.m_StorageType;
|
||||
m_NeedsManagedAlloc = other.m_NeedsManagedAlloc;
|
||||
m_Interpreter = other.m_Interpreter;
|
||||
// Invalidate other so it will not release.
|
||||
other.m_StorageType = kUnsupportedType;
|
||||
other.m_NeedsManagedAlloc = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
@ -214,30 +224,6 @@ namespace cling {
|
||||
return 1;
|
||||
}
|
||||
|
||||
Value::EStorageType Value::determineStorageType(clang::QualType QT) {
|
||||
const clang::Type* desugCanon = QT.getCanonicalType().getTypePtr();
|
||||
if (desugCanon->isSignedIntegerOrEnumerationType())
|
||||
return kSignedIntegerOrEnumerationType;
|
||||
else if (desugCanon->isUnsignedIntegerOrEnumerationType())
|
||||
return kUnsignedIntegerOrEnumerationType;
|
||||
else if (desugCanon->isRealFloatingType()) {
|
||||
const clang::BuiltinType* BT = desugCanon->getAs<clang::BuiltinType>();
|
||||
if (BT->getKind() == clang::BuiltinType::Double)
|
||||
return kDoubleType;
|
||||
else if (BT->getKind() == clang::BuiltinType::Float)
|
||||
return kFloatType;
|
||||
else if (BT->getKind() == clang::BuiltinType::LongDouble)
|
||||
return kLongDoubleType;
|
||||
} else if (desugCanon->isPointerType() || desugCanon->isObjectType()
|
||||
|| desugCanon->isReferenceType()) {
|
||||
if (desugCanon->isRecordType() || desugCanon->isConstantArrayType()
|
||||
|| desugCanon->isMemberPointerType())
|
||||
return kManagedAllocation;
|
||||
return kPointerType;
|
||||
}
|
||||
return kUnsupportedType;
|
||||
}
|
||||
|
||||
void Value::ManagedAllocate() {
|
||||
assert(needsManagedAllocation() && "Does not need managed allocation");
|
||||
void* dtorFunc = 0;
|
||||
@ -258,12 +244,15 @@ namespace cling {
|
||||
GetNumberOfElements(getType()));
|
||||
}
|
||||
|
||||
template <typename T> T convert(clang::QualType QT, Value::Storage& S) {
|
||||
static clang::QualType getBuiltinCanonicalType(clang::QualType QT) {
|
||||
if (const auto *ET = llvm::dyn_cast<clang::EnumType>(QT.getTypePtr()))
|
||||
QT = ET->getDecl()->getIntegerType();
|
||||
|
||||
QT = QT.getCanonicalType();
|
||||
return QT.getCanonicalType();
|
||||
}
|
||||
|
||||
template <typename T> T convert(clang::QualType QT, Value::Storage& S) {
|
||||
QT = getBuiltinCanonicalType(QT);
|
||||
assert(QT->isBuiltinType());
|
||||
|
||||
const auto* BT = llvm::cast<const clang::BuiltinType>(QT.getTypePtr());
|
||||
@ -277,17 +266,28 @@ namespace cling {
|
||||
}
|
||||
}
|
||||
|
||||
template <> void* Value::getAs() const {
|
||||
if (needsManagedAllocation() || hasPointerType())
|
||||
return m_Storage.m_Ptr;
|
||||
return (void*)convert<uintptr_t>(getType(), const_cast<Value::Storage&>(m_Storage));
|
||||
}
|
||||
|
||||
#define X(type, name) \
|
||||
template <> type Value::getAs() const { \
|
||||
return convert<type>(getType(), const_cast<Value::Storage&>(m_Storage)); \
|
||||
} \
|
||||
template <> type& Value::getAs() { \
|
||||
return convert<type&>(getType(), m_Storage); \
|
||||
} \
|
||||
|
||||
BUILTIN_TYPES
|
||||
|
||||
#undef X
|
||||
bool Value::hasPointerType() const {
|
||||
clang::QualType Canon = getType().getCanonicalType();
|
||||
return Canon->hasPointerRepresentation() /*|| Canon->isObjectType()*/;
|
||||
}
|
||||
|
||||
bool Value::hasBuiltinType() const {
|
||||
return getBuiltinCanonicalType(getType())->isBuiltinType();
|
||||
}
|
||||
|
||||
void Value::AssertOnUnsupportedTypeCast() const {
|
||||
assert("unsupported type in Value, cannot cast simplistically!" && 0);
|
||||
|
@ -523,44 +523,42 @@ namespace runtime {
|
||||
CLING_LIB_EXPORT
|
||||
void setValueNoAlloc(void* vpI, void* vpSVR, void* vpQT, char vpOn,
|
||||
float value) {
|
||||
allocateStoredRefValueAndGetGV(vpI, vpSVR, vpQT).getAs<float>() = value;
|
||||
allocateStoredRefValueAndGetGV(vpI, vpSVR, vpQT).setFloat(value);
|
||||
dumpIfNoStorage(vpSVR, vpOn);
|
||||
}
|
||||
|
||||
CLING_LIB_EXPORT
|
||||
void setValueNoAlloc(void* vpI, void* vpSVR, void* vpQT, char vpOn,
|
||||
double value) {
|
||||
allocateStoredRefValueAndGetGV(vpI, vpSVR, vpQT).getAs<double>() = value;
|
||||
allocateStoredRefValueAndGetGV(vpI, vpSVR, vpQT).setDouble(value);
|
||||
dumpIfNoStorage(vpSVR, vpOn);
|
||||
}
|
||||
|
||||
CLING_LIB_EXPORT
|
||||
void setValueNoAlloc(void* vpI, void* vpSVR, void* vpQT, char vpOn,
|
||||
long double value) {
|
||||
allocateStoredRefValueAndGetGV(vpI, vpSVR, vpQT).getAs<long double>()
|
||||
= value;
|
||||
allocateStoredRefValueAndGetGV(vpI, vpSVR, vpQT).setLongDouble(value);
|
||||
dumpIfNoStorage(vpSVR, vpOn);
|
||||
}
|
||||
|
||||
CLING_LIB_EXPORT
|
||||
void setValueNoAlloc(void* vpI, void* vpSVR, void* vpQT, char vpOn,
|
||||
unsigned long long value) {
|
||||
allocateStoredRefValueAndGetGV(vpI, vpSVR, vpQT)
|
||||
.getAs<unsigned long long>() = value;
|
||||
allocateStoredRefValueAndGetGV(vpI, vpSVR, vpQT).setULongLong(value);
|
||||
dumpIfNoStorage(vpSVR, vpOn);
|
||||
}
|
||||
|
||||
CLING_LIB_EXPORT
|
||||
void setValueNoAlloc(void* vpI, void* vpSVR, void* vpQT, char vpOn,
|
||||
const void* value){
|
||||
allocateStoredRefValueAndGetGV(vpI, vpSVR, vpQT).getAs<void*>()
|
||||
= const_cast<void*>(value);
|
||||
allocateStoredRefValueAndGetGV(vpI, vpSVR, vpQT)
|
||||
.setPtr(const_cast<void*>(value));
|
||||
dumpIfNoStorage(vpSVR, vpOn);
|
||||
}
|
||||
|
||||
CLING_LIB_EXPORT
|
||||
void* setValueWithAlloc(void* vpI, void* vpSVR, void* vpQT, char vpOn) {
|
||||
return allocateStoredRefValueAndGetGV(vpI, vpSVR, vpQT).getAs<void*>();
|
||||
return allocateStoredRefValueAndGetGV(vpI, vpSVR, vpQT).getPtr();
|
||||
}
|
||||
} // end namespace internal
|
||||
} // end namespace runtime
|
||||
|
@ -762,7 +762,7 @@ static std::string printEnumValue(const Value &V) {
|
||||
const clang::EnumType *EnumTy = Ty.getNonReferenceType()->getAs<clang::EnumType>();
|
||||
assert(EnumTy && "ValuePrinter.cpp: ERROR, printEnumValue invoked for a non enum type.");
|
||||
clang::EnumDecl *ED = EnumTy->getDecl();
|
||||
uint64_t value = V.getULL();
|
||||
uint64_t value = V.getULongLong();
|
||||
bool IsFirst = true;
|
||||
llvm::APSInt ValAsAPSInt = C.MakeIntValue(value, Ty);
|
||||
for (clang::EnumDecl::enumerator_iterator I = ED->enumerator_begin(),
|
||||
@ -906,33 +906,33 @@ static std::string printUnpackedClingValue(const Value &V) {
|
||||
= llvm::dyn_cast<clang::BuiltinType>(Td.getCanonicalType().getTypePtr())) {
|
||||
switch (BT->getKind()) {
|
||||
case clang::BuiltinType::Bool:
|
||||
return executePrintValue<bool>(V, V.getLL());
|
||||
return executePrintValue<bool>(V, V.getLongLong());
|
||||
|
||||
case clang::BuiltinType::Char_S:
|
||||
return executePrintValue<signed char>(V, V.getLL());
|
||||
return executePrintValue<signed char>(V, V.getLongLong());
|
||||
case clang::BuiltinType::SChar:
|
||||
return executePrintValue<signed char>(V, V.getLL());
|
||||
return executePrintValue<signed char>(V, V.getLongLong());
|
||||
case clang::BuiltinType::Short:
|
||||
return executePrintValue<short>(V, V.getLL());
|
||||
return executePrintValue<short>(V, V.getLongLong());
|
||||
case clang::BuiltinType::Int:
|
||||
return executePrintValue<int>(V, V.getLL());
|
||||
return executePrintValue<int>(V, V.getLongLong());
|
||||
case clang::BuiltinType::Long:
|
||||
return executePrintValue<long>(V, V.getLL());
|
||||
return executePrintValue<long>(V, V.getLongLong());
|
||||
case clang::BuiltinType::LongLong:
|
||||
return executePrintValue<long long>(V, V.getLL());
|
||||
return executePrintValue<long long>(V, V.getLongLong());
|
||||
|
||||
case clang::BuiltinType::Char_U:
|
||||
return executePrintValue<unsigned char>(V, V.getULL());
|
||||
return executePrintValue<unsigned char>(V, V.getULongLong());
|
||||
case clang::BuiltinType::UChar:
|
||||
return executePrintValue<unsigned char>(V, V.getULL());
|
||||
return executePrintValue<unsigned char>(V, V.getULongLong());
|
||||
case clang::BuiltinType::UShort:
|
||||
return executePrintValue<unsigned short>(V, V.getULL());
|
||||
return executePrintValue<unsigned short>(V, V.getULongLong());
|
||||
case clang::BuiltinType::UInt:
|
||||
return executePrintValue<unsigned int>(V, V.getULL());
|
||||
return executePrintValue<unsigned int>(V, V.getULongLong());
|
||||
case clang::BuiltinType::ULong:
|
||||
return executePrintValue<unsigned long>(V, V.getULL());
|
||||
return executePrintValue<unsigned long>(V, V.getULongLong());
|
||||
case clang::BuiltinType::ULongLong:
|
||||
return executePrintValue<unsigned long long>(V, V.getULL());
|
||||
return executePrintValue<unsigned long long>(V, V.getULongLong());
|
||||
|
||||
case clang::BuiltinType::Float:
|
||||
return executePrintValue<float>(V, V.getFloat());
|
||||
|
@ -469,7 +469,7 @@ namespace cling {
|
||||
clang::ASTContext& Ctx = m_Interpreter.getCI()->getASTContext();
|
||||
if (result) {
|
||||
*result = Value(Ctx.IntTy, m_Interpreter);
|
||||
result->getAs<long long>() = exitStatus;
|
||||
result->setLongLong(exitStatus); // FIXME: This should assert.
|
||||
}
|
||||
return (exitStatus == 0) ? AR_Success : AR_Failure;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user