Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions libcxx/include/stdexcept
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD

# ifndef _LIBCPP_ABI_VCRUNTIME
class _LIBCPP_HIDDEN __libcpp_refstring {
const char* __imp_;

bool __uses_refcount() const;
struct __rep;
__rep* __imp_;

public:
explicit __libcpp_refstring(const char* __msg);
__libcpp_refstring(const __libcpp_refstring& __s) _NOEXCEPT;
__libcpp_refstring& operator=(const __libcpp_refstring& __s) _NOEXCEPT;
~__libcpp_refstring();

_LIBCPP_HIDE_FROM_ABI const char* c_str() const _NOEXCEPT { return __imp_; }
_LIBCPP_HIDE_FROM_ABI const char* c_str() const _NOEXCEPT;
};
# endif // !_LIBCPP_ABI_VCRUNTIME

Expand Down
105 changes: 18 additions & 87 deletions libcxx/src/include/refstring.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,112 +15,43 @@
#include <cstring>
#include <stdexcept>

// MacOS and iOS used to ship with libstdc++, and still support old applications
// linking against libstdc++. The libc++ and libstdc++ exceptions are supposed
// to be ABI compatible, such that they can be thrown from one library and caught
// in the other.
//
// For that reason, we must look for libstdc++ in the same process and if found,
// check the string stored in the exception object to see if it is the GCC empty
// string singleton before manipulating the reference count. This is done so that
// if an exception is created with a zero-length string in libstdc++, libc++abi
// won't try to delete the memory.
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) || defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
# define _LIBCPP_CHECK_FOR_GCC_EMPTY_STRING_STORAGE
# include <dlfcn.h>
# include <mach-o/dyld.h>
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

namespace __refstring_imp {
namespace {
typedef int count_t;
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wc99-extensions")

struct _Rep_base {
std::size_t len;
std::size_t cap;
count_t count;
struct __libcpp_refstring::__rep {
ptrdiff_t refcount;
char data[];
};

inline _Rep_base* rep_from_data(const char* data_) noexcept {
char* data = const_cast<char*>(data_);
return reinterpret_cast<_Rep_base*>(data - sizeof(_Rep_base));
}

inline char* data_from_rep(_Rep_base* rep) noexcept {
char* data = reinterpret_cast<char*>(rep);
return data + sizeof(*rep);
}

#if defined(_LIBCPP_CHECK_FOR_GCC_EMPTY_STRING_STORAGE)
inline const char* compute_gcc_empty_string_storage() noexcept {
void* handle = dlopen("/usr/lib/libstdc++.6.dylib", RTLD_NOLOAD);
if (handle == nullptr)
return nullptr;
void* sym = dlsym(handle, "_ZNSs4_Rep20_S_empty_rep_storageE");
if (sym == nullptr)
return nullptr;
return data_from_rep(reinterpret_cast<_Rep_base*>(sym));
}

inline const char* get_gcc_empty_string_storage() noexcept {
static const char* p = compute_gcc_empty_string_storage();
return p;
}
#endif

} // namespace
} // namespace __refstring_imp

using namespace __refstring_imp;

inline __libcpp_refstring::__libcpp_refstring(const char* msg) {
std::size_t len = strlen(msg);
_Rep_base* rep = static_cast<_Rep_base*>(::operator new(sizeof(*rep) + len + 1));
rep->len = len;
rep->cap = len;
rep->count = 0;
char* data = data_from_rep(rep);
std::memcpy(data, msg, len + 1);
__imp_ = data;
auto* rep = static_cast<__rep*>(::operator new(sizeof(__rep) + len + 1));
rep->refcount = 0;
std::memcpy(rep->data, msg, len + 1);
__imp_ = rep;
}

inline __libcpp_refstring::__libcpp_refstring(const __libcpp_refstring& s) noexcept : __imp_(s.__imp_) {
if (__uses_refcount())
__libcpp_atomic_add(&rep_from_data(__imp_)->count, 1);
__libcpp_atomic_add(&__imp_->refcount, 1);
}

inline __libcpp_refstring& __libcpp_refstring::operator=(__libcpp_refstring const& s) noexcept {
bool adjust_old_count = __uses_refcount();
struct _Rep_base* old_rep = rep_from_data(__imp_);
__imp_ = s.__imp_;
if (__uses_refcount())
__libcpp_atomic_add(&rep_from_data(__imp_)->count, 1);
if (adjust_old_count) {
if (__libcpp_atomic_add(&old_rep->count, count_t(-1)) < 0) {
::operator delete(old_rep);
}
}
__rep* old_rep = __imp_;
__imp_ = s.__imp_;
__libcpp_atomic_add(&__imp_->refcount, 1);

if (__libcpp_atomic_add(&old_rep->refcount, ptrdiff_t(-1)) < 0)
::operator delete(old_rep);
return *this;
}

inline __libcpp_refstring::~__libcpp_refstring() {
if (__uses_refcount()) {
_Rep_base* rep = rep_from_data(__imp_);
if (__libcpp_atomic_add(&rep->count, count_t(-1)) < 0) {
::operator delete(rep);
}
}
if (__libcpp_atomic_add(&__imp_->refcount, ptrdiff_t(-1)) < 0)
::operator delete(__imp_);
}

inline bool __libcpp_refstring::__uses_refcount() const {
#if defined(_LIBCPP_CHECK_FOR_GCC_EMPTY_STRING_STORAGE)
return __imp_ != get_gcc_empty_string_storage();
#else
return true;
#endif
}
inline const char* __libcpp_refstring::c_str() const noexcept { return __imp_->data; }

_LIBCPP_END_NAMESPACE_STD

Expand Down
Loading