Last active
April 8, 2024 14:24
-
-
Save oplanre/0b3a91b621000f17e7fa8304363f52c8 to your computer and use it in GitHub Desktop.
PHP Typesystem Extended (C++). This is part of a project i built at work to make it easier/more stable to build php extensions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "lend-forward.h" | |
#include "lend-type.h" // NOLINT(build/include_directory) | |
#include "lend-internal.h" // NOLINT(build/include_directory) | |
#include "lend-handle.h" // NOLINT(build/include_directory) | |
#include "lendconfig.h" // NOLINT(build/include_directory) | |
#include <cstdint> | |
namespace lend { | |
/** | |
* @class HeapObject | |
* @brief Represents an object in the heap. | |
* | |
* The HeapObject class provides a base class for objects that are managed by a | |
* garbage collector. It contains a Header struct that stores metadata about the | |
* object, such as the reference count and type information. The class also | |
* provides methods for manipulating the reference count and accessing the type | |
* information. | |
*/ | |
class HeapObject { | |
protected: | |
class Header { | |
protected: | |
constexpr uint32_t &refcount() { return refcount_; } | |
constexpr uint32_t refcount() const { return refcount_; } | |
constexpr void refcount(uint32_t rc) { refcount_ = rc; } | |
constexpr uint32_t &type_info() { return type_info_; } | |
constexpr uint32_t type_info() const { return type_info_; } | |
constexpr void type_info(uint32_t ti) { type_info_ = ti; } | |
constexpr uint32_t operator++() { return ++refcount_; } | |
constexpr uint32_t operator++(int) { return refcount_++; } | |
constexpr uint32_t operator--() { return --refcount_; } | |
constexpr uint32_t operator--(int) { return refcount_--; } | |
constexpr uint32_t operator+=(uint32_t rc) { return refcount_ += rc; } | |
constexpr uint32_t operator-=(uint32_t rc) { return refcount_ -= rc; } | |
constexpr Header() : refcount_(1) {} | |
constexpr Header(Type::Is type, bool persistent = false, bool immutable = false) | |
: type_info_{IsField::encode(type) | | |
NotCollectableField::encode(type != Type::Is::Object && type != Type::Is::Array) | | |
ProtectedField::encode(false) | ImmutableField::encode(immutable) | PersistentField::encode(persistent) | | |
PersistentLocalField::encode(false)}, | |
refcount_(1) {} | |
constexpr Header(uint32_t type_info) : type_info_{type_info}, refcount_(1) {} | |
constexpr Header(uint32_t type_info, uint32_t refcount) : type_info_{type_info}, refcount_(refcount) {} | |
constexpr Header(const Header &other) : refcount_(1) {} | |
constexpr Header(Header &&other) : refcount_(1) {} | |
constexpr Header &operator=(const Header &other) { return *this; } | |
constexpr Header &operator=(Header &&other) { return *this; } | |
uint32_t refcount_; | |
uint32_t type_info_{0}; | |
// union { | |
// } u; | |
friend class HeapObject; | |
template <class Derived> | |
friend class HeapObjectImpl; | |
friend class GC; | |
}; | |
using IsField = base::BitField<Type::Is, 0, 4>; | |
using NotCollectableField = IsField::Next<bool, 1>; | |
using ProtectedField = NotCollectableField::Next<bool, 1>; | |
using ImmutableField = ProtectedField::Next<bool, 1>; | |
using PersistentField = ImmutableField::Next<bool, 1>; | |
using PersistentLocalField = PersistentField::Next<bool, 1>; | |
template <typename T, int size> | |
using NextBitField = PersistentLocalField::Next<T, size>; | |
#if LEND_RC_DEBUG | |
static bool g_rc_debug_; | |
/* The GC::Persistent flag is reused for IsWeaklyReferenced on objects. | |
* Skip checks for OBJECT/nullptr type to avoid interpreting the flag | |
* incorrectly. */ | |
static void RCModCheck(Header &h) { | |
if (g_rc_debug_) { | |
auto type = IsField::decode(h.type_info_); | |
if (type != Type::Is::Object && type != Type::Is::Null) { | |
DCHECK(!(ImmutableField::decode(h.type_info()))); | |
// LEND_ASSUME((Flags(h.type_info()) & (GC::Persistent | | |
// GC::PersistentLocal)) != GC::Persistent); | |
DCHECK((PersistentField::decode(h.type_info()) | PersistentLocalField::decode(h.type_info())) != | |
PersistentField::encode(true)); | |
} | |
} | |
} | |
static void MakePersistentLocal(Header &h) { h.type_info_ = PersistentLocalField::update(h.type_info_, true); } | |
#else | |
static inline void RCModCheck(Header &h) {} | |
static inline void MakePersistentLocal(Header &h) {} | |
#endif | |
static uint32_t AddRef(Header &h) { | |
RCModCheck(h); | |
return ++h.refcount_; | |
} | |
static void TryAddRef(Header &h) { | |
if (ImmutableField::decode(h.type_info()) == false) { | |
RCModCheck(h); | |
++h.refcount_; | |
} | |
} | |
static void TryDelRef(Header &h) { | |
if (ImmutableField::decode(h.type_info()) == false) { | |
RCModCheck(h); | |
--h.refcount_; | |
} | |
} | |
static uint32_t DeleteRef(Header &h) { | |
DCHECK_GT(h.refcount(), 0); | |
RCModCheck(h); | |
return --h.refcount_; | |
} | |
static uint32_t AddRef(Header &h, uint32_t rc) { | |
RCModCheck(h); | |
return h.refcount_ += rc; | |
} | |
static uint32_t DeleteRef(Header &h, uint32_t rc) { | |
RCModCheck(h); | |
return h.refcount_ -= rc; | |
} | |
public: | |
constexpr uint32_t refcount() const { return gc_.refcount_; } | |
constexpr uint32_t type_info() const { return gc_.type_info_; } | |
constexpr uint32_t type_info() { return gc_.type_info_; } | |
constexpr uint32_t refcount(uint32_t rc) { return gc_.refcount_ = rc; } | |
constexpr uint32_t type_info(uint32_t ti) { return gc_.type_info_ = ti; } | |
constexpr uint32_t type_info(Type::Is type) { return IsField::update(gc_.type_info_, type); } | |
constexpr uint32_t IncRef(uint32_t rc = 1) { return AddRef(gc_, rc); } | |
constexpr uint32_t DecRef(uint32_t rc = 1) { return DeleteRef(gc_, rc); } | |
void TryAddRef() { TryAddRef(gc_); } | |
void TryDelRef() { TryDelRef(gc_); } | |
constexpr Type::Is GetType() const { return IsField::decode(type_info()); } | |
template <Type::Is type> | |
void SetType() { | |
type_info(IsField::update(type_info(), type)); | |
} | |
template <Type::Is type> | |
bool Is() const { | |
return GetType() == type; | |
} | |
template <Type::Is type> | |
bool IsNot() const { | |
return GetType() != type; | |
} | |
void MarkAsImmutable(bool v = true) { type_info(ImmutableField::update(type_info(), v)); } | |
constexpr bool IsImmutable() const { return ImmutableField::decode(type_info()); } | |
constexpr bool IsNotCollectable() const { return NotCollectableField::decode(type_info()); } | |
constexpr bool IsProtected() const { return ProtectedField::decode(type_info()); } | |
constexpr bool IsPersistent() const { return PersistentField::decode(type_info()); } | |
constexpr bool IsPersistentLocal() const { return PersistentLocalField::decode(type_info()); } | |
constexpr bool IsRecursive() const { return ProtectedField::decode(type_info()); } | |
constexpr void ProtectRecursion() { type_info(ProtectedField::update(type_info(), true)); } | |
constexpr void UnProtectRecursion() { type_info(ProtectedField::update(type_info(), false)); } | |
constexpr inline void TryProtectRecursion() { | |
if (!IsImmutable()) ProtectRecursion(); | |
} | |
constexpr inline void TryUnProtectRecursion() { | |
if (!IsImmutable()) UnProtectRecursion(); | |
} | |
constexpr HeapObject() : gc_() {} | |
constexpr HeapObject(uint32_t type_info) : gc_(type_info, 1) {} | |
constexpr HeapObject(uint32_t type_info, uint32_t refcount) : gc_(type_info, refcount) {} | |
constexpr HeapObject(Type::Is type, bool persistent = false, bool immutable = false) : gc_(type, persistent, immutable) {} | |
constexpr HeapObject(uint32_t refcount, Type::Is type, bool persistent = false, bool immutable = false) | |
: gc_(type, persistent, immutable) { | |
gc_.refcount_ = refcount; | |
} | |
constexpr HeapObject(const HeapObject &other) : gc_(other.type_info(), 1) {} | |
constexpr HeapObject(HeapObject &&other) : gc_(other.type_info(), 1) {} | |
constexpr HeapObject &operator=(const HeapObject &other) { return *this; } | |
constexpr HeapObject &operator=(HeapObject &&other) { return *this; } | |
template <class T> | |
static T *As(HeapObject *p) { | |
static_assert(std::is_base_of<HeapObject, T>::value, "T must be a subclass of HeapObject"); | |
return static_cast<T *>(p); | |
} | |
template <class T> | |
static T *As(const HeapObject *p) { | |
static_assert(std::is_base_of<HeapObject, T>::value, "T must be a subclass of HeapObject"); | |
DCHECK(dynamic_cast<T *>(p) != nullptr); | |
return static_cast<T *>(p); | |
} | |
template <class T> | |
T *As() { | |
return As<T>(this); | |
} | |
template <class T> | |
const T *As() const { | |
return As<T>(this); | |
} | |
void Destroy() {} | |
private: | |
~HeapObject() = default; | |
friend class GC; | |
friend class TVal; | |
template <class Derived> | |
friend class HeapObjectImpl; | |
template <class Derived> | |
friend class ClassEntryBase; | |
friend class PHPArray; | |
template <size_t N> | |
friend class PHPObjectBase; | |
friend class TString; | |
friend class Resource; | |
friend class Reference; | |
friend class AstReference; | |
struct Header gc_; | |
}; | |
template <class Derived> | |
class HeapObjectImpl; | |
namespace internal { | |
template <class T> | |
Handle<T> ToDerivedHandle(Handle<HeapObjectImpl<T>> h) { | |
return Handle<T>::Cast(h); | |
} | |
} // namespace internal | |
template <class Derived> | |
class HeapObjectImpl : public HeapObject { | |
public: | |
using HeapObject::Header; | |
using HeapObject::ImmutableField; | |
using HeapObject::IsField; | |
using HeapObject::NotCollectableField; | |
using HeapObject::PersistentField; | |
using HeapObject::PersistentLocalField; | |
using HeapObject::ProtectedField; | |
// typedef void(LEND_FASTCALL *Destructor)(HeapObject *p); | |
using Destructor = void (*)(HeapObject *p); | |
static constexpr auto kEmptyDtor = [](HeapObject *) {}; | |
static void DestroyReference(Reference *r); | |
Handle<Derived> ToHandle() const { return internal::ToDerivedHandle(Handle<HeapObjectImpl>(this)); } | |
Handle<Derived> ToHandle() { return internal::ToDerivedHandle(Handle<HeapObjectImpl>(this)); } | |
protected: | |
using HeapObject::HeapObject; | |
constexpr class Header &header() { return gc_; } | |
constexpr const class Header &header() const { return gc_; } | |
static void DestroyRefCounted(HeapObjectImpl *p) { p->Destroy(); } | |
friend class GC; | |
friend class TVal; | |
template <class T> | |
friend class HeapObjectImpl; | |
template <class T> | |
friend class ClassEntryBase; | |
}; | |
template <> | |
class HeapObjectImpl<TString> : public HeapObject { | |
protected: | |
using HeapObject::HeapObject; | |
ULong hash_; | |
size_t len_; | |
uint8_t val_[1]; | |
// virtual ~HeapObjectImpl() { Destroy(); } | |
public: | |
/* refcount is a map_ptr offset of class_entry */ | |
using IsClassNamePointerField = HeapObject::ProtectedField; | |
/* interned string */ | |
using IsInternedField = HeapObject::ImmutableField; | |
/* allocated using malloc */ | |
using IsPersistentField = HeapObject::PersistentField; | |
/* relives request boundary */ | |
using IsPermanentField = IsPersistentField::Next<bool, 1>; | |
/* valid UTF-8 according to PCRE */ | |
using IsValidUtf8Field = IsPermanentField::Next<bool, 1>; | |
/* flags that will be copied when concatenating */ | |
using CopyableConcatPropertiesField = IsPermanentField::Next<int, 1>; | |
bool HasClassEntryCache() { return IsClassNamePointerField::decode(type_info()); } | |
int CopyableConcatProperties() const { return CopyableConcatPropertiesField::decode(type_info()); } | |
/* This method returns the copyable concat properties which hold on both | |
* strings. */ | |
int CopyableConcatProperties(Handle<const HeapObjectImpl> that) const { | |
return CopyableConcatPropertiesField::decode(type_info()) & CopyableConcatPropertiesField::decode(that->type_info()); | |
} | |
void SetCopyableConcatProperties(int properties) { | |
type_info(CopyableConcatPropertiesField::update(type_info(), properties)); | |
} | |
constexpr HeapObjectImpl() : HeapObject(Type::Is::String, false), len_(0), hash_(0) {} | |
constexpr HeapObjectImpl(size_t len, bool persistent = false, bool is_valid_utf8 = false, bool interned = false) | |
: HeapObject(Type::Is::String, persistent, interned), len_(len), hash_(0) { | |
IsValidUtf8Field::encode(is_valid_utf8); | |
} | |
constexpr HeapObjectImpl(const char *str, | |
size_t len, | |
bool persistent = false, | |
bool is_valid_utf8 = false, | |
bool interned = false, | |
bool is_class_name_pointer = false, | |
bool is_permanent = false) | |
: HeapObject(Type::Is::String, persistent, interned), len_(len), hash_(0) { | |
type_info(type_info() | IsValidUtf8Field::encode(is_valid_utf8) | IsClassNamePointerField::encode(is_class_name_pointer) | | |
IsPermanentField::encode(is_permanent)); | |
memcpy(val_, str, len); | |
val_[len] = '\0'; | |
} | |
HeapObjectImpl(const char *str) : HeapObject(Type::Is::String, false, false), len_(strlen(str)), hash_(0) { | |
memcpy(val_, str, len_); | |
val_[len_] = '\0'; | |
} | |
constexpr HeapObjectImpl(const HeapObjectImpl *that, | |
bool persistent = false, | |
bool is_valid_utf8 = false, | |
bool interned = false, | |
bool is_class_name_pointer = false, | |
bool is_permanent = false) | |
: HeapObject(Type::Is::String, persistent, interned), len_(that->len_), hash_(that->hash_) { | |
type_info(type_info() | IsValidUtf8Field::encode(is_valid_utf8) | IsClassNamePointerField::encode(is_class_name_pointer) | | |
IsPermanentField::encode(is_permanent)); | |
memcpy(val_, that->val_, len_); | |
val_[len_] = '\0'; | |
} | |
ULong HashValue() const { return hash_; } | |
ULong HashValue(ULong hash) { return hash_ = hash; } | |
size_t length() const { return len_; } | |
void SetLength(size_t len) { len_ = len; } | |
size_t size() const { return len_; } | |
// size_t &length() { return len_; } | |
uint8_t *GetChars() { return val_; } | |
char *c_str() { return reinterpret_cast<char *>(val_); } | |
char &at(size_t i) { return c_str()[i]; } | |
const char *c_str() const { return c_str(); } | |
const char &at(size_t i) const { return at(i); } | |
bool empty() const { return len_ == 0; } | |
template <typename ByteT = char> | |
Vector<ByteT> ToVector() { | |
return {reinterpret_cast<ByteT *>(val_), len_}; | |
} | |
template <typename ByteT = char> | |
Vector<const ByteT> ToConstVector() { | |
return ToVector(); | |
} | |
template <typename ByteT = char> | |
Vector<ByteT> ToVector() const { | |
return ToVector(); | |
} | |
template <typename ByteT = char> | |
Vector<const ByteT> ToConstVector() const { | |
return ToConstVector(); | |
} | |
bool IsInterned() { return IsInternedField::decode(type_info()); } | |
bool IsPersistent() { return IsPersistentField::decode(type_info()); } | |
bool IsPermanent() { return IsPermanentField::decode(type_info()); } | |
bool IsValidUtf8() { return IsValidUtf8Field::decode(type_info()); } | |
bool IsClassNamePointer() { return IsClassNamePointerField::decode(type_info()); } | |
void MarkAsClassNamePointer(bool v = true) { type_info(IsClassNamePointerField::update(type_info(), v)); } | |
void MarkAsInterned(bool v = true) { type_info(IsInternedField::update(type_info(), v)); } | |
void MarkAsPermanent(bool v = true) { type_info(IsPermanentField::update(type_info(), v)); } | |
void MarkAsValidUtf8(bool v = true) { type_info(IsValidUtf8Field::update(type_info(), v)); } | |
}; | |
template <> | |
class HeapObjectImpl<Resource> : public HeapObject { | |
public: | |
constexpr HeapObjectImpl() : HeapObject(Type::Is::Resource), handle_(0), type_(0), ptr_(nullptr) {} | |
constexpr HeapObjectImpl(Long handle, int type, void *ptr, bool persist = false) | |
: HeapObject(Type::Is::Resource, persist), handle_(handle), type_(type), ptr_(ptr) {} | |
constexpr Long Handle() const { return handle_; } | |
constexpr int Type() const { return type_; } | |
constexpr void *Val() const { return ptr_; } | |
template <typename T> | |
constexpr T *Val() const { | |
return static_cast<T *>(ptr_); | |
} | |
protected: | |
Long handle_; | |
int type_; | |
void *ptr_; | |
}; | |
// An abstract superclass for classes representing PHP primitive values | |
// other than Smi. It doesn't carry any functionality but allows primitive | |
// classes to be identified in the type system. | |
template <class Derived> | |
class PrimitiveHeapObject : public HeapObjectImpl<Derived> { | |
protected: | |
using HeapObjectImpl<Derived>::HeapObjectImpl; | |
}; | |
template <> | |
class PrimitiveHeapObject<internal::BigIntBase> : public HeapObjectImpl<internal::BigIntBase> { | |
public: | |
using digit_t = uintptr_t; | |
static const int kDigitSize = sizeof(digit_t); | |
inline int length() const { return LengthBits::decode(static_cast<uint32_t>(bitfield_)); } | |
inline digit_t *digits_start() { return &digits_[0]; } | |
// The maximum kMaxLengthBits that the current implementation supports | |
// would be kMaxInt - kSystemPointerSize * kBitsPerByte - 1. | |
// Since we want a platform independent limit, choose a nice round number | |
// somewhere below that maximum. | |
static constexpr int kMaxLengthBits = 1 << 30; // ~1 billion. | |
static constexpr int kMaxLength = kMaxLengthBits / (internal::kApiSystemPointerSize * internal::kBitsPerByte); | |
// Sign and length are stored in the same bitfield. Since the GC needs to be | |
// able to read the length concurrently, the getters and setters are atomic. | |
static const int kLengthFieldBits = 30; | |
static_assert(kMaxLength <= ((1 << kLengthFieldBits) - 1)); | |
using SignBits = base::BitField<bool, 0, 1>; | |
using LengthBits = SignBits::Next<int, kLengthFieldBits>; | |
static_assert(LengthBits::kLastUsedBit < 32); | |
protected: | |
Address digits() const { return reinterpret_cast<Address>(digits_); } | |
Address digits() { return reinterpret_cast<Address>(digits_); } | |
// kMaxLength definition assumes this: | |
static_assert(kDigitSize == internal::kApiSystemPointerSize); | |
static constexpr int kDigitBits = kDigitSize * internal::kBitsPerByte; | |
static constexpr int kHalfDigitBits = kDigitBits / 2; | |
static constexpr digit_t kHalfDigitMask = (1ull << kHalfDigitBits) - 1; | |
constexpr PrimitiveHeapObject() : HeapObjectImpl(Type::Is::BigInt, false), bitfield_(0) {} | |
constexpr PrimitiveHeapObject(int length, bool sign, bool persistent = false) | |
: HeapObjectImpl(Type::Is::BigInt, persistent), bitfield_(LengthBits::encode(length) | SignBits::encode(sign)) {} | |
constexpr PrimitiveHeapObject(int length, bool sign, bool persistent, bool immutable) | |
: HeapObjectImpl(Type::Is::BigInt, persistent, immutable), | |
bitfield_(LengthBits::encode(length) | SignBits::encode(sign)) {} | |
bool is_zero() const { return length() == 0; } | |
friend class ::lend::internal::BigInt; // MSVC wants full namespace. | |
friend class MutableBigInt; | |
inline bool sign() const { return SignBits::decode(static_cast<uint32_t>(bitfield_)); } | |
inline digit_t &at(int n) { | |
LEND_ASSUME(0 <= n && n < length()); | |
return *reinterpret_cast<digit_t *>(digits() + n * kDigitSize); | |
} | |
inline const digit_t &at(int n) const { | |
LEND_ASSUME(0 <= n && n < length()); | |
return *reinterpret_cast<const digit_t *>(digits() + n * kDigitSize); | |
} | |
inline static int SizeFor(int length) { return sizeof(PrimitiveHeapObject) + length * kDigitSize; } | |
inline digit_t digit(int n) const { return at(n); } | |
inline void set_digit(int n, digit_t value) { at(n) = value; } | |
inline void set_sign(bool new_sign) { bitfield_ = SignBits::update(bitfield_, new_sign); } | |
inline void set_length(int new_length) { bitfield_ = LengthBits::update(bitfield_, new_length); } | |
inline void initialize_bitfield(bool sign, int length) { bitfield_ = LengthBits::encode(length) | SignBits::encode(sign); } | |
void InitializeDigits(int length, uint8_t value) { | |
memset(reinterpret_cast<void *>(digits() + length * kDigitSize), value, length * kDigitSize); | |
} | |
// Clear uninitialized padding space. | |
inline void clear_padding() { | |
int length = this->length(); | |
if (length < kMaxLength) { | |
memset(reinterpret_cast<void *>(this->digits() + length * kDigitSize), 0, (kMaxLength - length) * kDigitSize); | |
} | |
} | |
// copy | |
static void Copy(const PrimitiveHeapObject *from, PrimitiveHeapObject *to) { | |
int length = from->length(); | |
to->initialize_bitfield(from->sign(), length); | |
memcpy(reinterpret_cast<void *>(to->digits()), reinterpret_cast<const void *>(from->digits()), length * kDigitSize); | |
to->clear_padding(); | |
} | |
private: | |
int32_t bitfield_; | |
digit_t digits_[1]; | |
}; | |
} // namespace lend |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <cstdint> | |
#include <optional> | |
#include "lend-bounds.h" | |
#include "lend-forward.h" // NOLINT(build/include_directory) | |
#include "lend-template-utils.h" // NOLINT(build/include_directory) | |
#include "lend-heap-object.h" // NOLINT(build/include_directory) | |
#include "lend-vector.h" // NOLINT(build/include_directory) | |
#include "lend-type.h" // NOLINT(build/include_directory) | |
#include "lend-maybe.h" // NOLINT(build/include_directory) | |
#include "lend-handle.h" // NOLINT(build/include_directory) | |
#include "lendconfig.h" // NOLINT(build/include_directory) | |
namespace lend { | |
namespace concepts { | |
template <class T> | |
concept InlineValueTypes = | |
std::is_same_v<T, std::nullptr_t> || std::is_same_v<T, std::nullopt_t> || std::is_same_v<T, std::true_type> || | |
std::is_same_v<T, std::false_type> || std::is_same_v<T, int> || std::is_same_v<T, Long> || std::is_same_v<T, bool> || | |
std::is_same_v<T, double> || std::is_same_v<T, TVal>; | |
template <class T> | |
concept HandleTypes = | |
std::is_same_v<T, Handle<HeapObjectImpl<T>>> || std::is_same_v<T, Handle<TString>> || std::is_same_v<T, Handle<TVal>> || | |
std::is_same_v<T, Handle<PHPArray>> || std::is_same_v<T, Handle<PHPObject>> || std::is_same_v<T, Handle<Resource>> || | |
std::is_same_v<T, Handle<Reference>> || std::is_same_v<T, Handle<AstReference>> || std::is_same_v<T, Handle<ClassEntry>> || | |
std::is_same_v<T, Handle<interpreter::Function>>; | |
template <class T> | |
concept TValIsh = InlineValueTypes<T> || HandleTypes<T>; | |
} // namespace concepts | |
class LEND_EXPORT TVal { | |
public: | |
struct TypeHelper final : internal::AllStatic { | |
/** StaticVarUninitialized is sed for static variables to check if they have been initialized. We | |
* can't use Type::Is::Undef because we can't store Type::Is::Undef tvals in | |
* the static_variables PHPArray. This needs to live in type_info so that | |
* the OpCode::Assign overrides it but is moved to extra | |
*/ | |
enum Flags : uint8_t { | |
Undefined = 0 << 0, | |
HeapObject = 1 << 0, | |
Collectable = 1 << 1, | |
StaticVarUninitialized = 1 << 2, | |
}; | |
using IsField = Type::IsField; | |
using HeapObjectField = Type::MayBeFields::HeapObject; | |
using CollectableField = Type::MayBeFields::Collectable; | |
using UninitializedStaticVarField = CollectableField::template Next<bool, 1>; | |
using FlagsField = IsField::template Next<Flags, 2>; | |
using Is = Type::Is; | |
static constexpr bool IsHeapObject(Is t) { return base::IsInRange(t, Is::String, Is::ConstantAst); } | |
static constexpr bool IsCollectable(Is t) { return base::IsInRange(t, Is::Array, Is::Object); } | |
template <Is t> | |
struct Metadata { | |
static constexpr bool is_heap_object = base::IsInRange(t, Is::String, Is::ConstantAst); | |
static constexpr bool is_collectable = base::IsInRange(t, Is::Array, Is::Object); | |
static constexpr uint64_t value = | |
IsField::encode(t) | HeapObjectField::encode(is_heap_object) | CollectableField::encode(is_collectable); | |
static constexpr Is type = t; | |
}; | |
template <> | |
struct Metadata<Is::InternedString> { | |
static constexpr uint64_t value = uint64_t(Is::InternedString); | |
static constexpr Is type = Is::InternedString; | |
}; | |
template <Is t> | |
static constexpr Is Initialize() { | |
return Metadata<t>::value; | |
} | |
template <Is t> | |
static constexpr uint64_t Update(uint64_t bits) { | |
return Metadata<t>::value | (bits & FlagsField::kMask); | |
} | |
enum class IsExtended : uint32_t { | |
Array = Metadata<Is::Array>::value, | |
Object = Metadata<Is::Object>::value, | |
String = Metadata<Is::String>::value, | |
InternedString = Metadata<Is::InternedString>::value, | |
Resource = Metadata<Is::Resource>::value, | |
Reference = Metadata<Is::Reference>::value, | |
ConstantAst = Metadata<Is::ConstantAst>::value, | |
BigInt = Metadata<Is::BigInt>::value, | |
}; | |
template <class T, int size> | |
using NextBitField = FlagsField::template Next<T, size>; | |
static_assert(HeapObject << 8 == HeapObjectField::encode(true)); | |
static_assert(Collectable << 8 == CollectableField::encode(true)); | |
static constexpr unsigned Encode(Is type) { | |
return IsField::encode(type) | HeapObjectField::encode(IsHeapObject(type)) | | |
CollectableField::encode(IsCollectable(type)); | |
} | |
// static constexpr Type::Is Decode(unsigned bits) { return IsField::decode(bits); } | |
static constexpr bool IsCollectable(unsigned bits) { return CollectableField::decode(bits); } | |
static constexpr bool IsHeapObject(unsigned bits) { return HeapObjectField::decode(bits); } | |
static constexpr bool IsUninitializedStaticVar(unsigned bits) { return UninitializedStaticVarField::decode(bits); } | |
static constexpr unsigned MarkUninitializedStaticVar(unsigned bits) { | |
return UninitializedStaticVarField::update(bits, true); | |
} | |
static constexpr unsigned ClearFlags(unsigned bits) { return FlagsField::update(bits, Flags::Undefined); } | |
static constexpr Type::Is Type(unsigned bits) { return IsField::decode(bits); } | |
static constexpr unsigned SetType(unsigned bits, Type::Is type) { return IsField::update(bits, type); } | |
static constexpr Flags TypeFlags(unsigned bits) { return FlagsField::decode(bits); } | |
}; | |
struct Value { | |
private: | |
friend class TVal; | |
internal::Address ptr() const { return storage_.address_; } | |
Long integer() const { return storage_.long_; } | |
double floating_point() const { return storage_.double_; } | |
using LowBits = base::BitField<uint32_t, 0, 32, uint64_t>; | |
using HighBits = LowBits::template Next<uint32_t, 32>; | |
static_assert(HighBits::kSize == 32 && HighBits::kMax == std::numeric_limits<uint32_t>::max()); | |
uint32_t *high_bits_ptr_raw() { return reinterpret_cast<uint32_t *>(&type_info_) + 1; } | |
Handle<uint32_t> high_bits_ptr() { return Handle<uint32_t>::New(high_bits_ptr_raw()); } | |
uint32_t GetHighBits() const { return HighBits::decode(type_info_); } | |
constexpr void SetHighBits(uint32_t value) { type_info_ = HighBits::update(type_info_, value); } | |
constexpr void IncrementHighBits() { SetHighBits(GetHighBits() + 1); } | |
constexpr void DecrementHighBits() { SetHighBits(GetHighBits() - 1); } | |
constexpr void ResetHighBits() { SetHighBits(0); } | |
constexpr void CopyLowBits(const Value &that) { type_info_ = LowBits::update(type_info_, LowBits::decode(that.type_info_)); } | |
constexpr void CopyHighBits(const Value &that) { SetHighBits(that.GetHighBits()); } | |
constexpr void CopyValue(const Value &that) { | |
if (that.Is<Type::Is::Long>()) { | |
storage_.long_ = that.storage_.long_; | |
} else if (that.Is<Type::Is::Double>()) { | |
storage_.double_ = that.storage_.double_; | |
} else { | |
storage_.address_ = that.storage_.address_; | |
} | |
CopyLowBits(that); | |
} | |
public: | |
union Storage { | |
Long long_; | |
double double_; | |
internal::Address address_{internal::ValueHelper::kEmpty}; | |
// void *address_ptr_; | |
} storage_; | |
uint64_t type_info_{0}; | |
using TypeInfo = TypeHelper; | |
constexpr Type::Is Type() { return TypeInfo::Type(type_info_); } | |
constexpr Type::Is Type() const { return TypeInfo::Type(type_info_); } | |
constexpr Type::Is Type(Type::Is type) { return TypeInfo::Type(type_info_ = TypeInfo::SetType(type_info_, type)); } | |
constexpr Value() : storage_{.address_ = internal::ValueHelper::kEmpty} { Type(Type::Is::Undefined); } | |
constexpr Value(Type::Is type) { Type(type); } | |
constexpr Value(std::nullptr_t nullify) { Type(Type::Is::Null); } | |
constexpr Value(std::nullopt_t nullify) { Type(Type::Is::Null); } | |
constexpr Value(std::true_type true_t) { Type(Type::Is::True); } | |
constexpr Value(std::false_type false_t) { Type(Type::Is::False); } | |
constexpr Value(bool boolean) { Type(boolean ? Type::Is::True : Type::Is::False); } | |
constexpr Value(Long integer) : storage_{integer} { Type(Type::Is::Long); } | |
constexpr Value(int integer) : storage_{integer} { Type(Type::Is::Long); } | |
template <class E> | |
requires(std::is_enum_v<E> && !std::is_same_v<E, Type::Is> && !std::is_same_v<E, Type::MayBe>) | |
constexpr Value(E enum_value) : storage_{static_cast<lend::Long>(enum_value)} { | |
Type(Type::Is::Long); | |
} | |
constexpr Value(double floating_point) : storage_{.double_ = floating_point} { Type(Type::Is::Double); } | |
constexpr Value(Handle<HeapObjectImpl<TString>> string) { | |
if (string->IsInterned()) { | |
Set<Type::Is::InternedString>(string); | |
} else { | |
Set<Type::Is::String>(string); | |
} | |
} | |
constexpr Value(Handle<HeapObjectImpl<PHPArray>> array) { Set<Type::Is::Array>(array); } | |
constexpr Value(Handle<HeapObjectImpl<PHPObject>> object) { Set<Type::Is::Object>(object); } | |
constexpr Value(Handle<HeapObjectImpl<Resource>> resource) { Set<Type::Is::Resource>(resource); } | |
constexpr Value(Handle<HeapObjectImpl<Reference>> reference) { Set<Type::Is::Reference>(reference); } | |
constexpr Value(Handle<HeapObjectImpl<AstReference>> ast) { Set<Type::Is::ConstantAst>(ast); } | |
constexpr Value(Handle<TString> string) : Value(string.As<HeapObjectImpl<TString>>()) {} | |
constexpr Value(Handle<PHPArray> array) { Set<Type::Is::Array>(array); } | |
constexpr Value(Handle<PHPObject> object) { Set<Type::Is::Object>(object); } | |
constexpr Value(Handle<Resource> resource) { Set<Type::Is::Resource>(resource); } | |
constexpr Value(Handle<Reference> reference) { Set<Type::Is::Reference>(reference); } | |
constexpr Value(Handle<AstReference> ast) { Set<Type::Is::ConstantAst>(ast); } | |
template <class T> | |
constexpr Value(Handle<T> h) { | |
Set<Type::Is::Pointer>(h); | |
} | |
template <Type::Is First, Type::Is... Rest> | |
LEND_NODISCARD constexpr bool Is() const { | |
return base::IsOneOf(Type(), First, Rest...); | |
} | |
template <Type::Is T> | |
LEND_NODISCARD constexpr bool IsNot() const { | |
return Type() != T; | |
} | |
constexpr Value(const Value &that) | |
// : type_info_(that.type_info_) | |
{ | |
CopyLowBits(that); | |
if (that.Is<Type::Is::Long>()) { | |
storage_.long_ = that.storage_.long_; | |
} else if (that.Is<Type::Is::Double>()) { | |
storage_.double_ = that.storage_.double_; | |
} else { | |
storage_.address_ = that.storage_.address_; | |
} | |
} | |
// constexpr Value(Handle<TVal> v) : type_info_(HighBits::update(v->value_.type_info_, 0)) { | |
// if (v->Is<Type::Is::Long>()) { | |
// long_ = v->Long(); | |
// } else if (v->Is<Type::Is::Double>()) { | |
// double_ = v->Double(); | |
// } else { | |
// address_ = v->value_.address_; | |
// } | |
// } | |
constexpr Value(Handle<TVal> v) : Value(v->value_) {} | |
constexpr Value(TVal that) : Value(that.value_) {} | |
constexpr Value(Value &&that) : type_info_(that.type_info_) { | |
if (that.Is<Type::Is::Long>()) { | |
storage_.long_ = that.storage_.long_; | |
} else if (that.Is<Type::Is::Double>()) { | |
storage_.double_ = that.storage_.double_; | |
} else { | |
storage_.address_ = that.storage_.address_; | |
} | |
} | |
constexpr Value &operator=(const Value &that) { | |
if (that.Is<Type::Is::Long>()) { | |
storage_.long_ = that.storage_.long_; | |
} else if (that.Is<Type::Is::Double>()) { | |
storage_.double_ = that.storage_.double_; | |
} else { | |
storage_.address_ = that.storage_.address_; | |
} | |
type_info_ = that.type_info_; | |
return *this; | |
} | |
constexpr Value &operator=(Value &&that) { | |
operator=(that); | |
return *this; | |
} | |
template <class T> | |
Handle<T> ToHandle() { | |
return Handle<T>::New(ptr()); | |
} | |
template <class T> | |
Handle<T> ToHandle() const { | |
return Handle<T>::New(ptr()); | |
} | |
template <class T> | |
T *As() { | |
return reinterpret_cast<T *>(ptr()); | |
} | |
template <class T> | |
T *As() const { | |
return reinterpret_cast<T *>(ptr()); | |
} | |
template <Type::Is type, class T> | |
void Set(T *p) { | |
storage_.address_ = internal::ValueHelper::ValueAsAddress<T>(p); | |
type_info_ = LowBits::update(type_info_, TypeInfo::Metadata<type>::value); | |
} | |
template <Type::Is type, class T> | |
void Set(Handle<T> p) { | |
storage_.address_ = p.ptr(); | |
type_info_ = LowBits::update(type_info_, TypeInfo::Metadata<type>::value); | |
} | |
}; | |
using TypeInfo = Value::TypeInfo; | |
// constexpr Data GetData() const { return {value_, type_info_, bits_}; } | |
explicit constexpr TVal(Value v) : value_(std::move(v)) {} | |
explicit constexpr TVal(const Value &v) : value_(v) {} | |
void Clear(); | |
static void Clear(TVal *z) { z->Clear(); } | |
void ClearInternal(); | |
void ClearNoGC(); | |
void ClearString(); | |
unsigned type_info(unsigned info) { return value_.type_info_ = info; } | |
LEND_NODISCARD constexpr unsigned type_info() const { return value_.type_info_; } | |
LEND_NODISCARD constexpr Long Long() const { return value_.integer(); } | |
LEND_NODISCARD constexpr double Double() const { return value_.floating_point(); } | |
LEND_NODISCARD Handle<TString> String() { return value_.ToHandle<TString>(); } | |
LEND_NODISCARD Handle<TString> String() const { return value_.ToHandle<TString>(); } | |
template <size_t N = 0> | |
Handle<PHPObjectBase<N>> Object() { | |
return value_.ToHandle<PHPObjectBase<N>>(); | |
} | |
template <size_t N = 0> | |
Handle<PHPObjectBase<N>> Object() const { | |
return value_.ToHandle<PHPObjectBase<N>>(); | |
} | |
LEND_NODISCARD Handle<TVal> Indirect() const { return value_.ToHandle<TVal>(); } | |
template <typename T = void> | |
LEND_NODISCARD T *Pointer() const { | |
return value_.template As<T>(); | |
} | |
template <typename T = void> | |
LEND_NODISCARD T *Pointer() { | |
return value_.template As<T>(); | |
} | |
LEND_NODISCARD internal::Address Address() const { return value_.ptr(); } | |
template <typename T> | |
LEND_NODISCARD Handle<T> PointerAsHandle() const { | |
return value_.ToHandle<T>(); | |
} | |
LEND_NODISCARD auto Counted() const { return PointerAsHandle<HeapObject>(); } | |
LEND_NODISCARD auto Array() const { return PointerAsHandle<PHPArray>(); } | |
LEND_NODISCARD auto Resource() const { return PointerAsHandle<class Resource>(); } | |
LEND_NODISCARD auto Reference() const { return PointerAsHandle<class Reference>(); } | |
LEND_NODISCARD auto Ast() const { return PointerAsHandle<class AstReference>(); } | |
LEND_NODISCARD auto ClassEntry() const { return PointerAsHandle<class ClassEntry>(); } | |
LEND_NODISCARD auto Function() const { return PointerAsHandle<interpreter::Function>(); } | |
LEND_NODISCARD auto Counted() { return PointerAsHandle<HeapObject>(); } | |
LEND_NODISCARD auto Array() { return PointerAsHandle<PHPArray>(); } | |
LEND_NODISCARD auto Resource() { return PointerAsHandle<class Resource>(); } | |
LEND_NODISCARD auto Reference() { return PointerAsHandle<class Reference>(); } | |
LEND_NODISCARD auto Ast() { return PointerAsHandle<class AstReference>(); } | |
LEND_NODISCARD auto ClassEntry() { return PointerAsHandle<class ClassEntry>(); } | |
LEND_NODISCARD auto Function() { return PointerAsHandle<interpreter::Function>(); } | |
LEND_INLINE void UnwrapReference(); | |
LEND_NODISCARD Handle<uint32_t> GuardAddress() { return value_.high_bits_ptr(); } | |
void Bits(uint32_t value) { value_.SetHighBits(value); } | |
LEND_NODISCARD uint32_t Bits() const { return value_.GetHighBits(); } | |
LEND_NODISCARD uint32_t Next() const { return Bits(); } | |
LEND_NODISCARD uint32_t CacheSlot() const { return Bits(); } | |
LEND_NODISCARD uint32_t Line() const { return Bits(); } | |
LEND_NODISCARD uint32_t NumArgs() const { return Bits(); } | |
LEND_NODISCARD uint32_t NumArgs() { return Bits(); } | |
LEND_NODISCARD uint32_t InstructionLine() const { return Bits(); } | |
LEND_NODISCARD uint32_t ForEachPosition() const { return Bits(); } | |
LEND_NODISCARD uint32_t ForEachIteration() const { return Bits(); } | |
LEND_NODISCARD uint32_t PropertyGuard() const { return Bits(); } | |
LEND_NODISCARD uint32_t ConstantFlags() const { return Bits(); } | |
LEND_NODISCARD uint32_t Extra() const { return Bits(); } | |
LEND_NODISCARD uint32_t PropFlag() const { return Bits(); } | |
void Next(uint32_t value) { Bits(value); } | |
void CacheSlot(uint32_t value) { Bits(value); } | |
void Line(uint32_t value) { Bits(value); } | |
void NumArgs(uint32_t value) { Bits(value); } | |
void InstructionLine(uint32_t value) { Bits(value); } | |
void ForEachPosition(uint32_t value) { Bits(value); } | |
void ForEachIteration(uint32_t value) { Bits(value); } | |
void PropertyGuard(uint32_t value) { Bits(value); } | |
void ConstantFlags(uint32_t value) { Bits(value); } | |
void Extra(uint32_t value) { Bits(value); } | |
void PropFlag(uint32_t value) { Bits(value); } | |
LEND_NODISCARD Type::Is GetType() const { return value_.Type(); } | |
LEND_NODISCARD Type::Is Type() const { return value_.Type(); } | |
LEND_NODISCARD Type::Is Type() { return value_.Type(); } | |
void SetType(Type::Is type) { value_.Type(type); } | |
// void SetType(uint32_t info) { type_info_ = info; } | |
void MarkUninitializedStaticVar() { value_.type_info_ = Value::TypeInfo::MarkUninitializedStaticVar(value_.type_info_); } | |
Type::Is Type(Type::Is type) { return value_.Type(type); } | |
LEND_NODISCARD bool IsHeapObject() const { return TypeInfo::IsHeapObject(value_.type_info_); } | |
LEND_NODISCARD bool IsCollectableType() const { return TypeInfo::IsCollectable(value_.type_info_); } | |
LEND_NODISCARD bool IsUninitializedStaticVar() const { return TypeInfo::IsUninitializedStaticVar(value_.type_info_); } | |
LEND_NODISCARD bool IsConstantType() const { return Is<Type::Is::ConstantAst>(); } | |
LEND_NODISCARD bool IsCopyableType() const { return Is<Type::Is::Array>(); } | |
LEND_NODISCARD bool IsImmutableType() const { return Is<Type::Is::Array>(); } | |
LEND_NODISCARD bool IsNumber() const { return Is<Type::Is::Long>() || Is<Type::Is::Double>(); } | |
LEND_NODISCARD bool IsTrue() { return Is<Type::Is::True>(); } | |
LEND_NODISCARD bool IsFalse() { return Is<Type::Is::False>(); } | |
LEND_NODISCARD bool IsReference() { return Is<Type::Is::Reference>(); } | |
LEND_NODISCARD bool IsUndefined() { return Is<Type::Is::Undefined>(); } | |
LEND_NODISCARD bool IsReference() const { return Is<Type::Is::Reference>(); } | |
LEND_NODISCARD bool IsUndefined() const { return Is<Type::Is::Undefined>(); } | |
LEND_NODISCARD bool IsNull() { return Is<Type::Is::Null>(); } | |
LEND_NODISCARD bool IsError() { return Is<Type::Is::Error>(); } | |
LEND_NODISCARD bool IsRecursive() const { return Counted()->IsRecursive(); } | |
// Assert that the value is truthy regardless of type. Returns false if the value is null or false. | |
LEND_NODISCARD bool IsTruthy(); | |
LEND_NODISCARD bool IsTruthy() const { return IsTruthy(); } | |
LEND_NODISCARD bool IsIterable(); | |
void ClearFlags() { value_.type_info_ = TypeInfo::ClearFlags(value_.type_info_); } | |
void CopyValue(Handle<TVal> that) { value_ = Value(that); } | |
void CopyValue(Handle<const TVal> that) { value_ = Value(that); } | |
constexpr void CopyValue(const TVal &that) { value_ = Value(that); } | |
constexpr void CopyValue(const Value &that) { value_ = that; } | |
void Copy(const TVal &that); | |
void Copy(Handle<const TVal> that); | |
void Duplicate(Handle<const TVal> that); | |
/* This function should only be used as a copy constructor, i.e. it | |
* should only be called AFTER a lend::TVal has been copied to another | |
* location using CopyValue. Do not call it before copying, | |
* otherwise a reference may be leaked. */ | |
void AddRefAfterCopy(); | |
static void AddRefAfterCopy(TVal *z) { z->AddRefAfterCopy(); } | |
/* ZVAL_COPY_OR_DUP() should be used instead of ZVAL_COPY() and ZVAL_DUP() | |
* in all places where the source may be a persistent lend::TVal. | |
*/ | |
void CopyOrDuplicate(Handle<const TVal> that); | |
LEND_INLINE void CopyOrDuplicate(const TVal &that) { CopyOrDuplicate(that.ToHandle()); } | |
void CopyValueProperty(const TVal *that) { *this = *that; } | |
void CopyValueProperty(Handle<const TVal> that) { CopyValueProperty(*that); } | |
void CopyProperty(TVal &that) { | |
Copy(that.ToHandle()); | |
PropFlag(that.PropFlag()); | |
} | |
void CopyProperty(Handle<const TVal> that) { | |
Copy(that); | |
PropFlag(that->PropFlag()); | |
} | |
void CopyOrDuplicateProperty(Handle<const TVal> that) { | |
CopyOrDuplicate(that); | |
PropFlag(that->PropFlag()); | |
} | |
LEND_NODISCARD bool MayModifyArgInPlace() const { | |
return IsHeapObject() && !(Counted()->IsImmutable() || Counted()->IsPersistent()) && Refcount() == 1; | |
} | |
template <Type::Is First, Type::Is... Rest> | |
LEND_NODISCARD constexpr bool Is() const { | |
return value_.template Is<First, Rest...>(); | |
} | |
template <Type::Is T> | |
LEND_NODISCARD constexpr bool IsNot() const { | |
return GetType() != T; | |
} | |
Handle<TVal> ToHandle() { return Handle<TVal>::New(this); } | |
Handle<const TVal> ToHandle() const { return Handle<const TVal>::New(this); } | |
LEND_NODISCARD uint32_t Refcount() const; | |
uint32_t SetRefcount(uint32_t rc); | |
uint32_t AddRef() const; | |
uint32_t DelRef() const; | |
void TryAddRef() const; | |
void TryDelRef() const; | |
void NewResource(lend::Long handle, int type, void *ptr); | |
void NewPersistentResource(lend::Long handle, int type, void *ptr); | |
void NewReference(); | |
void NewReference(Handle<TVal> r); | |
void NewReference(uint32_t refcount); | |
void NewPersistentReference(Handle<TVal> r); | |
void Nullify() { SetType(Type::Is::Null); } | |
void False() { SetType(Type::Is::False); } | |
void True() { SetType(Type::Is::True); } | |
void Bool(bool v) { SetType(v ? Type::Is::True : Type::Is::False); } | |
void Error() { SetType(Type::Is::Error); } | |
void ProtectRecursion() { Counted()->ProtectRecursion(); } | |
void UnProtectRecursion() { Counted()->UnProtectRecursion(); } | |
constexpr void Long(lend::Long v) { value_ = Value(v); } | |
constexpr void Double(double v) { value_ = Value(v); } | |
constexpr Vector<const char> TypeName(); | |
constexpr Vector<const char> ValueName(); | |
constexpr Vector<const char> TypeName() const; | |
constexpr Vector<const char> ValueName() const; | |
LEND_INLINE void InternedString(TString *s) { value_.Set<Type::Is::InternedString>(s); } | |
LEND_INLINE void NewString(TString *s) { value_.Set<Type::Is::String>(s); } | |
LEND_INLINE void InternedString(Handle<TString> s) { value_.Set<Type::Is::InternedString>(s); } | |
LEND_INLINE void NewString(Handle<TString> s) { value_.Set<Type::Is::String>(s); } | |
LEND_INLINE void Resource(class Resource *r) { value_.Set<Type::Is::Resource>(r); } | |
LEND_INLINE void BigInt(internal::BigIntBase *b) { value_.Set<Type::Is::BigInt>(b); } | |
LEND_INLINE void Reference(class Reference *r) { value_.Set<Type::Is::Reference>(r); } | |
LEND_INLINE void Array(Handle<PHPArray> a) { value_.Set<Type::Is::Array>(a); } | |
LEND_INLINE void Array(const PHPArray &a) { value_.Set<Type::Is::Array>(&a); } | |
LEND_INLINE void Ast(AstReference *ast) { value_.Set<Type::Is::ConstantAst>(ast); } | |
LEND_INLINE void Indirect(Handle<TVal> v) { value_.Set<Type::Is::Indirect>(v); } | |
LEND_INLINE void Indirect(const TVal &v) { value_.Set<Type::Is::Indirect>(&v); } | |
template <size_t N = 0> | |
LEND_INLINE void Object(PHPObjectBase<N> *o) { | |
value_.Set<Type::Is::Object>(o); | |
} | |
template <size_t N = 0> | |
LEND_INLINE void Object(Handle<PHPObjectBase<N>> o) { | |
value_.Set<Type::Is::Object>(o); | |
} | |
template <class T> | |
LEND_INLINE void Pointer(T *p) { | |
value_.Set<Type::Is::Pointer, T>(p); | |
} | |
template <class T> | |
LEND_INLINE void AliasPointer(T *p) { | |
value_.Set<Type::Is::AliasPointer, T>(p); | |
} | |
template <class T> | |
LEND_INLINE void Pointer(Handle<T> p) { | |
value_.Set<Type::Is::Pointer, T>(p); | |
} | |
template <class T> | |
LEND_INLINE void AliasPointer(Handle<T> p) { | |
value_.Set<Type::Is::AliasPointer, T>(p); | |
} | |
LEND_INLINE void Function(interpreter::Function *f) { value_.Set<Type::Is::Pointer>(f); } | |
LEND_INLINE void ClassEntry(lend::ClassEntry *c) { value_.Set<Type::Is::Pointer>(c); } | |
LEND_INLINE void String(Handle<HeapObjectImpl<TString>> s) { value_.Set<Type::Is::String>(s); } | |
LEND_INLINE const char *c_str() const; | |
LEND_INLINE char *c_str(); | |
LEND_INLINE size_t length() const; | |
LEND_INLINE void length(size_t l); | |
template <class ByteT> | |
LEND_INLINE Vector<ByteT> StringBytes(); | |
template <class ByteT> | |
LEND_INLINE Vector<ByteT> StringBytes() const; | |
LEND_INLINE void EmptyArray(); | |
LEND_INLINE void EmptyString(); | |
LEND_INLINE void EmptyPersistentString(); | |
LEND_INLINE void String(Vector<const char>); | |
void String(const char *); | |
void String(const char *, size_t); | |
LEND_INLINE void PersistentString(Vector<const char>); | |
LEND_INLINE void FastString(Vector<const char>); | |
LEND_INLINE void PersistentString(const char *); | |
LEND_INLINE void FastString(const char *); | |
LEND_INLINE void PersistentString(const char *, size_t); | |
LEND_INLINE void FastString(const char *, size_t); | |
LEND_INLINE void Char(char c); | |
LEND_INLINE void CopyString(Handle<TString> s); | |
LEND_INLINE void NewPersistentArray(); | |
// LEND_INLINE void CopyObject(PHPObject *o); | |
template <size_t N = 0> | |
LEND_INLINE void CopyObject(Handle<HeapObjectImpl<PHPObjectBase<N>>> o) { | |
Object<N>(o); | |
o->IncRef(1); | |
} | |
template <size_t N = 0> | |
LEND_INLINE void CopyObject(Handle<PHPObjectBase<N>> o) { | |
Object<N>(o); | |
o->IncRef(1); | |
} | |
void OptimizedDeReference(); | |
void OptimizedDeIndirect(); | |
LEND_INLINE internal::BigInt *BigInt(); | |
LEND_INLINE void DeReference(); | |
LEND_INLINE void DeIndirect(); | |
LEND_INLINE void MakeReference(); | |
LEND_INLINE void UnReference(); | |
LEND_INLINE void CopyAndDeReference(Handle<TVal> v); | |
LEND_INLINE void DetachString(); | |
LEND_INLINE void DetachArray(); | |
LEND_INLINE void DetachNoRef(); | |
LEND_INLINE void Detach(); | |
LEND_INLINE Handle<TVal> ReferencedVal(); | |
LEND_INLINE Handle<TVal> ReferencedVal() const; | |
constexpr void Undefine() { SetType(Type::Is::Undefined); } | |
constexpr TVal() { Undefine(); } | |
constexpr TVal(const TVal &that) { CopyValue(that); } | |
constexpr TVal(Maybe<TVal> &that) { | |
if (that.IsNothing()) | |
Undefine(); | |
else | |
CopyValue(that.FromJust()); | |
} | |
constexpr TVal &operator=(const TVal &that) { | |
CopyValue(that); | |
return *this; | |
} | |
constexpr TVal &operator=(TVal &&that) { | |
value_ = that.value_; | |
return *this; | |
} | |
// LEND_INLINE explicit constexpr TVal(TypeInfo info) : value_(), {SetType(info);} | |
LEND_INLINE explicit constexpr TVal(Type::Is t) : value_(t) {} | |
LEND_INLINE explicit constexpr TVal(bool boolean) : value_(boolean) {} | |
LEND_INLINE explicit constexpr TVal(std::nullptr_t nullify_val) : value_((nullify_val)) {} | |
LEND_INLINE explicit constexpr TVal(std::nullopt_t nullify_val) : value_((nullify_val)) {} | |
LEND_INLINE explicit constexpr TVal(std::true_type true_val) : value_((true_val)) {} | |
LEND_INLINE explicit constexpr TVal(std::false_type false_val) : value_((false_val)) {} | |
LEND_INLINE explicit constexpr TVal(lend::Long integer) : value_(integer) {} | |
LEND_INLINE explicit constexpr TVal(int integer) : value_(integer) {} | |
template <class E> | |
requires(std::is_enum_v<E> && !std::is_same_v<E, Type::Is> && !std::is_same_v<E, Type::MayBe>) | |
LEND_INLINE explicit constexpr TVal(E e) : TVal(static_cast<lend::Long>(e)) {} | |
LEND_INLINE explicit constexpr TVal(double floating_point) : value_(floating_point) {} | |
LEND_INLINE explicit constexpr TVal(Handle<TString> string) : value_(string.As<HeapObjectImpl<TString>>()) {} | |
LEND_INLINE explicit constexpr TVal(const char *string); | |
LEND_INLINE explicit constexpr TVal(const char *string, size_t l); | |
LEND_INLINE explicit constexpr TVal(Vector<const char> v); | |
LEND_INLINE explicit constexpr TVal(PHPArray *array); | |
LEND_INLINE explicit constexpr TVal(PHPObject *object); | |
LEND_INLINE explicit constexpr TVal(Handle<PHPArray> array); | |
LEND_INLINE explicit constexpr TVal(Handle<PHPObject> object); | |
LEND_INLINE explicit constexpr TVal(lend::Resource *v); | |
LEND_INLINE explicit constexpr TVal(lend::Reference *v); | |
LEND_INLINE explicit constexpr TVal(AstReference *v); | |
LEND_INLINE explicit constexpr TVal(Handle<TVal> v); | |
LEND_INLINE explicit constexpr TVal(void *v); | |
LEND_INLINE explicit constexpr TVal(lend::ClassEntry *v); | |
LEND_INLINE explicit constexpr TVal(interpreter::Function *v); | |
void InitArray(size_t size = 0); | |
template <size_t N = 0> | |
void InitObject(); | |
Result InitObject(Handle<lend::ClassEntry> ce, Handle<PHPArray> properties = {}) { return InitObjectImpl(ce, properties); } | |
LEND_INLINE void AddAssociation(Vector<const char> key) { AddAssociationImpl(key, TVal()); } | |
template <concepts::TValIsh T> | |
LEND_INLINE void AddAssociation(Vector<const char> key, T value) { | |
AddAssociationImpl(key, TVal(value)); | |
} | |
template <concepts::TValIsh T> | |
LEND_INLINE void AddAssociation(Vector<const char> key, T &&value) { | |
AddAssociationImpl(key, TVal(std::forward<T>(value))); | |
} | |
LEND_INLINE void AddIndex(lend::ULong, const char *, size_t); | |
LEND_INLINE Result AddIndex(lend::ULong, lend::Handle<TVal> value); | |
LEND_INLINE Result AddIndex(lend::ULong, TVal &&value); | |
template <concepts::TValIsh T> | |
LEND_INLINE void AddIndex(lend::ULong idx, T value) { | |
AddIndex(idx, TVal(value)); | |
} | |
template <concepts::TValIsh T> | |
LEND_INLINE Result Push(T value); | |
Result array_set_zval_key(PHPArray *ht, lend::Handle<TVal> key, lend::Handle<TVal> value); | |
LEND_INLINE void AddProperty(Vector<const char>); | |
template <concepts::TValIsh T> | |
LEND_INLINE void AddProperty(Vector<const char> key, T value) { | |
AddPropertyImpl(key, TVal(value)); | |
} | |
template <concepts::TValIsh T> | |
LEND_INLINE void AddProperty(Vector<const char> key, T &&value) { | |
AddPropertyImpl(key, TVal(std::forward<T>(value))); | |
} | |
template <concepts::TValIsh T> | |
LEND_INLINE void AddProperty(const char *key, T value) { | |
AddPropertyImpl(base::CStrVector(key), TVal(value)); | |
} | |
template <concepts::TValIsh T> | |
LEND_INLINE void AddProperty(const char *key, T &&value) { | |
AddPropertyImpl(base::CStrVector(key), TVal(std::forward<T>(value))); | |
} | |
bool IsIdentical(Handle<const TVal> that) const; | |
HashPosition IteratorPosition(uint32_t idx); | |
Result UpdateConstant(); | |
Result UpdateConstant(lend::ClassEntry *scope); | |
lend::Long AsLongImpl(bool is_strict) const; | |
double AsDoubleImpl() const; | |
Handle<TString> AsStringImpl() const; | |
Handle<TString> AsString() const; | |
LEND_INLINE auto AsLong(bool is_strict = false) const { return (Is<Type::Is::Long>()) ? Long() : AsLongImpl(is_strict); } | |
LEND_INLINE auto AsDouble() const { return (Is<Type::Is::Double>()) ? Double() : AsDoubleImpl(); } | |
LEND_INLINE auto AsBool() const { return (Is<Type::Is::True>()) ? true : (Is<Type::Is::False>()) ? false : IsTruthy(); } | |
private: | |
void CopyCtor(); | |
Result InitObjectImpl(Handle<lend::ClassEntry> ce, Handle<PHPArray> properties = {}); | |
void AddPropertyImpl(Vector<const char> key, lend::Handle<TVal> value); | |
void AddPropertyImpl(Vector<const char> key, TVal value); | |
void AddAssociationImpl(Vector<const char> key, lend::TVal value); | |
friend class Operations; | |
friend class Converter; | |
friend Type::Is IsNumericString(Vector<const char> str, | |
Handle<TVal> result, | |
bool allow_errors, | |
int *oflow_info, | |
bool *trailing_data); | |
protected: | |
Value value_; | |
}; | |
namespace internal::ast { | |
struct Location { | |
Location(int b, int e) : beg_pos(b), end_pos(e) {} | |
Location() : beg_pos(0), end_pos(0) {} | |
int length() const { return end_pos - beg_pos; } | |
bool IsValid() const { return base::IsInRange(beg_pos, 0, end_pos); } | |
static Location Invalid() { return Location(-1, 0); } | |
int beg_pos; | |
int end_pos; | |
}; | |
} // namespace internal::ast | |
} // namespace lend |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <compare> | |
#include <concepts> | |
#include <cstddef> | |
#include <cstdint> | |
#include <string_view> | |
#include <type_traits> | |
#include <vector> | |
#include "lend-handle.h" // NOLINT(build/include_directory) | |
#include "lend-static-string.h" // NOLINT(build/include_directory) | |
#include "lend-forward.h" // NOLINT(build/include_directory) | |
#include "lend-vector.h" // NOLINT(build/include_directory) | |
namespace lend { | |
class TypeList : public Vector<const Type> { | |
using Base = Vector<const Type>; | |
public: | |
using Base::Base; | |
constexpr TypeList(Base base) : Base(base) {} | |
constexpr TypeList(Vector<Type> base) : Base(base) {} | |
constexpr TypeList() = default; | |
}; | |
class Type { | |
public: | |
static constexpr uint8_t kIsHeapObject = 1u << 0; | |
static constexpr uint8_t kIsCollectable = 1u << 1; | |
static constexpr uint64_t kMask = 1u << 8; | |
static constexpr uint64_t kFlagsMask = 1u << 16; | |
static constexpr uint64_t kFlagsShift = 8; | |
static constexpr uint64_t kInfoExtraShift = 16; | |
enum class Is : uint8_t { | |
Undefined = 0, | |
Null, | |
False, | |
True, | |
Long, | |
Double, | |
String, | |
Array, | |
Object, | |
Resource, | |
Reference, | |
ConstantAst, | |
Callable, | |
Iterable, | |
Void, | |
Static, | |
Mixed, | |
Never, | |
Bool, | |
Number, | |
BigInt, | |
/* internal types */ | |
Indirect = 12, | |
Pointer = 13, | |
AliasPointer = 14, | |
Error = 15, | |
InternedString = String, | |
}; | |
template <Is t> | |
struct Deduce { | |
using type = TVal *; | |
}; | |
#define LEND_TYPE_DEDUCE(t, v) \ | |
template <> \ | |
struct Deduce<Is::t> { \ | |
using type = v; \ | |
}; | |
LEND_TYPE_DEDUCE(Undefined, std::nullptr_t) | |
LEND_TYPE_DEDUCE(Null, std::nullptr_t) | |
LEND_TYPE_DEDUCE(False, bool) | |
LEND_TYPE_DEDUCE(True, bool) | |
LEND_TYPE_DEDUCE(Bool, bool) | |
LEND_TYPE_DEDUCE(Number, double) | |
LEND_TYPE_DEDUCE(Long, Long) | |
LEND_TYPE_DEDUCE(Double, double) | |
LEND_TYPE_DEDUCE(String, Handle<TString>) | |
LEND_TYPE_DEDUCE(Array, Handle<PHPArray>) | |
LEND_TYPE_DEDUCE(Object, Handle<PHPObject>) | |
LEND_TYPE_DEDUCE(Resource, Handle<Resource>) | |
LEND_TYPE_DEDUCE(Reference, Handle<Reference>) | |
LEND_TYPE_DEDUCE(ConstantAst, Handle<internal::ast::Node>) | |
#undef LEND_TYPE_DEDUCE | |
template <typename T> | |
struct DeduceFromCpp { | |
static constexpr Is value = Is::Mixed; | |
}; | |
#define LEND_TYPE_DEDUCE_FROM_HANDLE(t, v) \ | |
template <> \ | |
struct DeduceFromCpp<v> { \ | |
static constexpr Is value = Is::t; \ | |
}; | |
LEND_TYPE_DEDUCE_FROM_HANDLE(String, Handle<TString>) | |
LEND_TYPE_DEDUCE_FROM_HANDLE(Array, Handle<PHPArray>) | |
LEND_TYPE_DEDUCE_FROM_HANDLE(Object, Handle<PHPObject>) | |
LEND_TYPE_DEDUCE_FROM_HANDLE(Resource, Handle<Resource>) | |
LEND_TYPE_DEDUCE_FROM_HANDLE(Reference, Handle<Reference>) | |
LEND_TYPE_DEDUCE_FROM_HANDLE(ConstantAst, Handle<internal::ast::Node>) | |
template <Is t> | |
struct Mask { | |
static constexpr uint64_t value = 1u << static_cast<uint64_t>(t); | |
}; | |
using IsField = base::BitField<Is, 0, 8, uint64_t>; | |
struct MayBeFields { | |
private: | |
template <Is t, typename T = bool, size_t Size = 1> | |
using Field = base::BitField<bool, static_cast<uint8_t>(t), Size>; | |
public: | |
using Undefined = Field<Is::Undefined>; | |
using Null = Field<Is::Null>; | |
using False = Field<Is::False>; | |
using True = Field<Is::True>; | |
using Long = Field<Is::Long>; | |
using Double = Field<Is::Double>; | |
using String = Field<Is::String>; | |
using Array = Field<Is::Array>; | |
using Object = Field<Is::Object>; | |
using Resource = Field<Is::Resource>; | |
using Reference = Field<Is::Reference>; | |
using Callable = Field<Is::Callable>; | |
using Void = Field<Is::Void>; | |
using Static = Field<Is::Static>; | |
using Never = Field<Is::Never>; | |
using Last = Never; | |
template <class T2, int size2> | |
using Next = Last::Next<T2, size2>; | |
static constexpr auto kMask = Next<bool, 1>::kMask - 1; | |
// HeapObject and Collectable only apply to Array and Object | |
using HeapObject = Object; | |
using Collectable = HeapObject::Next<bool, 1>; | |
//Array of Field | |
using ArrayOfField = base::BitField<uint32_t, Reference::kShift, Reference::kShift>; | |
static constexpr auto kArrayMask = ArrayOfField::kMax; | |
using BigInt = Field<Is::BigInt>; | |
}; | |
enum class MayBe : uint32_t { | |
None = 0, | |
Undefined = MayBeFields::Undefined::kMask, | |
Null = MayBeFields::Null::kMask, | |
False = MayBeFields::False::kMask, | |
True = MayBeFields::True::kMask, | |
Long = MayBeFields::Long::kMask, | |
Double = MayBeFields::Double::kMask, | |
String = MayBeFields::String::kMask, | |
Array = MayBeFields::Array::kMask, | |
Object = MayBeFields::Object::kMask, | |
Resource = MayBeFields::Resource::kMask, | |
Reference = MayBeFields::Reference::kMask, | |
Callable = MayBeFields::Callable::kMask, | |
Void = MayBeFields::Void::kMask, | |
Never = MayBeFields::Never::kMask, | |
Static = MayBeFields::Static::kMask, | |
BigInt = MayBeFields::BigInt::kMask, | |
Bool = (False | True), | |
Number = (Long | Double), | |
Any = Null | Bool | Long | Double | String | Array | Object | Resource, | |
ArrayShift = MayBeFields::ArrayOfField::kShift, | |
ArrayOfNull = Null << ArrayShift, | |
ArrayOfFalse = False << ArrayShift, | |
ArrayOfTrue = True << ArrayShift, | |
ArrayOfLong = Long << ArrayShift, | |
ArrayOfDouble = Double << ArrayShift, | |
ArrayOfString = String << ArrayShift, | |
ArrayOfArray = Array << ArrayShift, | |
ArrayOfObject = Object << ArrayShift, | |
ArrayOfResource = Resource << ArrayShift, | |
ArrayOfAny = Any << ArrayShift, | |
ArrayOfRef = Reference << ArrayShift, | |
ArrayPacked = 1u << 21, | |
ArrayNumericHash = 1u << 22, | |
ArrayStringHash = 1u << 23, | |
Class = 1u << 24, | |
Indirect = 1u << 25, | |
PackedGuard = 1u << 27, | |
ClassGuard = 1u << 27, | |
Guard = 1u << 28, | |
Rc1 = 1u << 30, | |
Rcn = 1u << 31, | |
ArrayKeyLong = ArrayPacked | ArrayNumericHash, | |
ArrayKeyString = ArrayStringHash, | |
ArrayKeyAny = ArrayKeyLong | ArrayKeyString, | |
AnyArray = Array | ArrayKeyAny | ArrayOfAny | ArrayOfRef, | |
}; | |
enum class Flags : std::uint32_t { | |
None = 0, | |
// Whether the type is a union type | |
UnionBit = 1u << 18, | |
// Whether the type list is an intersection type | |
IntersectionBit = 1u << 19, | |
// Whether the type list is zone allocated | |
ArenaBit = 1u << 20, | |
// For BC behaviour with iterable type | |
IterableBit = 1u << 21, | |
// Only one of these bits may be set. | |
ListBit = 1u << 22, | |
LiteralNameBit = 1u << 23, | |
NameBit = 1u << 24, | |
// Common mask for complex types | |
KindMask = ListBit | NameBit, | |
Union = UnionBit | ListBit, | |
Intersection = IntersectionBit | ListBit, | |
// Type mask excluding the flags above. | |
MayBeMask = UnionBit - 1, | |
// Must have same value as MayBe::Null | |
NullableBit = Mask<Is::Null>::value, | |
ExtraFlagsShift = 25, | |
/* Extra flags */ | |
ReferenceBit = 1u << ExtraFlagsShift, | |
// Whether the type is variadic | |
VariadicBit = 1u << (ExtraFlagsShift + 2), | |
// Whether the type is promoted | |
PromotedBit = 1u << (ExtraFlagsShift + 3), | |
// Whether the type is tentative | |
TentativeBit = 1u << (ExtraFlagsShift + 4), | |
Mask = ReferenceBit - 1, | |
}; | |
friend constexpr Flags operator|(Flags a, unsigned b) { | |
return static_cast<Flags>(static_cast<unsigned>(a) | static_cast<unsigned>(b)); | |
} | |
friend constexpr Flags operator|(MayBe a, unsigned b) { | |
return static_cast<Flags>(static_cast<unsigned>(a) | static_cast<unsigned>(b)); | |
} | |
friend constexpr Flags operator|(Flags a, Flags b) { | |
return static_cast<Flags>(static_cast<unsigned>(a) | static_cast<unsigned>(b)); | |
} | |
friend constexpr Flags operator&(Flags a, unsigned b) { | |
return static_cast<Flags>(static_cast<unsigned>(a) & static_cast<unsigned>(b)); | |
} | |
friend constexpr Flags operator&(MayBe a, unsigned b) { | |
return static_cast<Flags>(static_cast<unsigned>(a) & static_cast<unsigned>(b)); | |
} | |
friend constexpr Flags operator&(Flags a, Flags b) { | |
return static_cast<Flags>(static_cast<unsigned>(a) & static_cast<unsigned>(b)); | |
} | |
friend constexpr Flags operator~(Flags a) { return static_cast<Flags>(~static_cast<unsigned>(a)); } | |
friend constexpr Flags &operator|=(Flags &a, Flags b) { return a = a | static_cast<unsigned>(b); } | |
enum class ComplexKind : std::uint32_t { List = 1, LiteralName = 2, Name = 3 }; | |
enum class ComplexKind2 : std::uint32_t { List, LiteralName, Name }; | |
// using FlagsField = IsField::Next<Flags, 8>; | |
// using ExtraField = FlagsField::Next<Flags, 8>; | |
using UnionField = MayBeFields::Next<bool, 1>; | |
using IntersectionField = UnionField::Next<bool, 1>; | |
using ArenaField = IntersectionField::Next<bool, 1>; | |
using IterableField = ArenaField::Next<bool, 1>; | |
using ListField = IterableField::Next<bool, 1>; | |
using LiteralNameField = ListField::Next<bool, 1>; | |
using NameField = LiteralNameField::Next<bool, 1>; | |
using SendModeField = NameField::Next<SendMode, 2>; | |
using ReferenceField = NameField::Next<uint8_t, 2>; | |
using VariadicField = ReferenceField::Next<bool, 1>; | |
using PromotedField = VariadicField::Next<bool, 1>; | |
using TentativeField = PromotedField::Next<bool, 1>; | |
using KindField = base::BitFieldUnion<ListField, NameField>; | |
using ListKindField = base::BitFieldUnion<UnionField, IntersectionField, ListField>; // ListField is always true | |
enum class ListKind : uint32_t { | |
Union = ListKindField::encode(true, false, true), | |
Intersection = ListKindField::encode(false, true, true), | |
}; | |
// List, Name, ListName | |
using ComplexField = IterableField::Next<ComplexKind, 2>; | |
using ComplexField2 = IterableField::Next<ComplexKind2, 2>; | |
// static_assert(ComplexField::kMask == int(Flags::KindMask)); | |
static_assert(int(Flags::KindMask) == (ListField::kMask | NameField::kMask)); | |
static_assert(KindField::kMask == int(Flags::KindMask)); | |
static_assert(UnionField::kMask == int(Flags::UnionBit)); | |
static_assert(IntersectionField::kMask == int(Flags::IntersectionBit)); | |
static_assert(int(ListKind::Union) == int(Flags::Union)); | |
static_assert(int(ListKind::Intersection) == int(Flags::Intersection)); | |
struct Check; | |
friend constexpr MayBe operator|(MayBe a, MayBe b) { | |
return static_cast<MayBe>(static_cast<uint64_t>(a) | static_cast<uint64_t>(b)); | |
} | |
friend constexpr MayBe operator&(MayBe a, MayBe b) { | |
return static_cast<MayBe>(static_cast<uint64_t>(a) & static_cast<uint64_t>(b)); | |
} | |
friend constexpr MayBe operator~(MayBe a) { return static_cast<MayBe>(~static_cast<uint64_t>(a)); } | |
friend constexpr MayBe &operator|=(MayBe &a, MayBe b) { return a = a | b; } | |
friend constexpr Is operator|(Is a, Is b) { return static_cast<Is>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b)); } | |
friend constexpr Is operator&(Is a, Is b) { return static_cast<Is>(static_cast<uint8_t>(a) & static_cast<uint8_t>(b)); } | |
friend constexpr Is operator~(Is a) { return static_cast<Is>(~static_cast<uint8_t>(a)); } | |
friend constexpr Is operator|(Is a, uint8_t b) { return static_cast<Is>(static_cast<uint8_t>(a) | b); } | |
friend constexpr Is &operator|=(Is &a, Is b) { return a = a | b; } | |
friend constexpr Is &operator&=(Is &a, Is b) { return a = a & b; } | |
friend constexpr bool operator!(Is a) { return !static_cast<uint8_t>(a); } | |
friend constexpr bool operator==(Is a, Is b) { return static_cast<uint8_t>(a) == static_cast<uint8_t>(b); } | |
friend constexpr bool operator!=(Is a, Is b) { return static_cast<uint8_t>(a) != static_cast<uint8_t>(b); } | |
friend constexpr bool operator<(Is a, Is b) { return static_cast<uint8_t>(a) < static_cast<uint8_t>(b); } | |
friend constexpr bool operator>(Is a, Is b) { return static_cast<uint8_t>(a) > static_cast<uint8_t>(b); } | |
friend constexpr bool operator<=(Is a, Is b) { return static_cast<uint8_t>(a) <= static_cast<uint8_t>(b); } | |
friend constexpr bool operator>=(Is a, Is b) { return static_cast<uint8_t>(a) >= static_cast<uint8_t>(b); } | |
friend constexpr Is operator<<(Is a, unsigned b) { return static_cast<Is>(static_cast<uint8_t>(a) << b); } | |
friend constexpr Is operator>>(Is a, unsigned b) { return static_cast<Is>(static_cast<uint8_t>(a) >> b); } | |
friend constexpr auto operator<<(unsigned a, Is b) { return static_cast<unsigned>(a << static_cast<uint8_t>(b)); } | |
friend constexpr auto operator>>(unsigned a, Is b) { return static_cast<unsigned>(a >> static_cast<uint8_t>(b)); } | |
constexpr uint64_t type_mask() const { return bits_; } | |
constexpr auto ptr() const { return addr_; } | |
template <Flags... f> | |
constexpr static uint64_t Has(unsigned mask) { | |
return mask & (static_cast<uint64_t>(f) | ...); | |
} | |
template <MayBe... f> | |
constexpr static uint64_t Has(unsigned mask) { | |
return (MayBeFields::kMask & mask) & (static_cast<uint64_t>(f) | ...); | |
} | |
template <Is... f> | |
constexpr static bool Has(unsigned mask) { | |
return ((MayBeFields::kMask & mask) & (MayBeTypeMask(f) | ...)) != 0; | |
} | |
template <Flags f> | |
constexpr static uint64_t Add(unsigned mask) { | |
return mask | static_cast<uint64_t>(f); | |
} | |
template <Flags f> | |
constexpr static uint64_t Remove(unsigned mask) { | |
return mask & ~static_cast<uint64_t>(f); | |
} | |
template <Flags... f> | |
constexpr uint64_t Has() const { | |
return Has<f...>(type_mask()); | |
} | |
template <MayBe... f> | |
constexpr uint64_t Has() const { | |
return Has<f...>(type_mask()); | |
} | |
template <Is... f> | |
constexpr bool Has() const { | |
return Has<f...>(type_mask()); | |
} | |
template <Flags f> | |
constexpr void Add() { | |
type_mask() = Add<f>(type_mask()); | |
} | |
template <Flags f> | |
constexpr void Remove() { | |
type_mask() = Remove<f>(type_mask()); | |
} | |
static constexpr uint64_t Bits(Type t) { return t.type_mask(); } | |
static constexpr uint64_t PureMask(unsigned mask) { return mask & MayBeFields::kMask; } | |
static constexpr uint64_t FullMaskWithoutNull(unsigned mask) { return mask & ~MayBeFields::Null::kMask; } | |
static constexpr uint64_t PureMaskWithoutNull(unsigned mask) { return FullMaskWithoutNull(PureMask(mask)); } | |
static constexpr bool ContainsCode(uint64_t mask, uint32_t code) { return (mask & (1u << code)) != 0; } | |
static constexpr bool ContainsCode(uint64_t mask, Type::Is code) { return ContainsCode(mask, static_cast<uint64_t>(code)); } | |
static constexpr bool AllowsNull(unsigned mask) { return MayBeFields::Null::decode(mask); } | |
static constexpr bool IsSet(unsigned mask) { return Has<Flags::Mask>(mask); } | |
static constexpr bool IsComplex(unsigned mask) { return Has<Flags::KindMask>(mask); } | |
static constexpr bool IsUnion(unsigned mask) { return UnionField::decode(mask); } | |
static constexpr bool IsIntersection(unsigned mask) { return IntersectionField::decode(mask); } | |
static constexpr bool IsVariadic(unsigned mask) { return VariadicField::decode(mask); } | |
static constexpr bool IsPromoted(unsigned mask) { return PromotedField::decode(mask); } | |
static constexpr bool IsTentative(unsigned mask) { return TentativeField::decode(mask); } | |
static constexpr bool UsesArena(unsigned mask) { return ArenaField::decode(mask); } | |
static constexpr bool HasName(unsigned mask) { return NameField::decode(mask); } | |
static constexpr bool HasLiteralName(unsigned mask) { return LiteralNameField::decode(mask); } | |
static constexpr bool HasList(unsigned mask) { return ListField::decode(mask); } | |
static constexpr bool IsIterableFallback(unsigned mask) { return IterableField::decode(mask); } | |
static constexpr uint64_t PureMask(Type t) { return PureMask(t.type_mask()); } | |
static constexpr uint64_t FullMaskWithoutNull(Type t) { return FullMaskWithoutNull(t.type_mask()); } | |
static constexpr uint64_t PureMaskWithoutNull(Type t) { return PureMaskWithoutNull(t.type_mask()); } | |
static constexpr bool ContainsCode(Type t, uint32_t code) { return ContainsCode(t.type_mask(), code); } | |
static constexpr bool AllowsNull(Type t) { return AllowsNull(t.type_mask()); } | |
static constexpr bool IsSet(Type t) { return IsSet(t.type_mask()); } | |
static constexpr bool IsComplex(Type t) { return IsComplex(t.type_mask()); } | |
static constexpr bool IsUnion(Type t) { return IsUnion(t.type_mask()); } | |
static constexpr bool IsIntersection(Type t) { return IsIntersection(t.type_mask()); } | |
static constexpr bool IsVariadic(Type t) { return IsVariadic(t.type_mask()); } | |
static constexpr bool IsPromoted(Type t) { return IsPromoted(t.type_mask()); } | |
static constexpr bool IsTentative(Type t) { return IsTentative(t.type_mask()); } | |
static constexpr bool UsesArena(Type t) { return UsesArena(t.type_mask()); } | |
static constexpr bool HasName(Type t) { return HasName(t.type_mask()); } | |
static constexpr bool HasLiteralName(Type t) { return HasLiteralName(t.type_mask()); } | |
static constexpr bool HasList(Type t) { return HasList(t.type_mask()); } | |
static constexpr bool IsIterableFallback(Type t) { return IsIterableFallback(t.type_mask()); } | |
constexpr uint64_t PureMask() const { return PureMask(type_mask()); } | |
constexpr uint64_t FullMaskWithoutNull() const { return FullMaskWithoutNull(type_mask()); } | |
constexpr uint64_t PureMaskWithoutNull() const { return PureMaskWithoutNull(type_mask()); } | |
constexpr bool ContainsCode(uint32_t code) const { return (type_mask() & (1u << code)) != 0; } | |
constexpr bool ContainsCode(Type::Is code) const { return ContainsCode(static_cast<uint64_t>(code)); } | |
constexpr bool AllowsNull() const { return AllowsNull(type_mask()); } | |
constexpr bool IsSet() const { return IsSet(type_mask()); } | |
constexpr bool IsComplex() const { return IsComplex(type_mask()); } | |
constexpr bool IsUnion() const { return IsUnion(type_mask()); } | |
constexpr bool IsIntersection() const { return IsIntersection(type_mask()); } | |
constexpr bool IsVariadic() const { return IsVariadic(type_mask()); } | |
constexpr bool IsPromoted() const { return IsPromoted(type_mask()); } | |
constexpr bool IsTentative() const { return IsTentative(type_mask()); } | |
constexpr bool UsesArena() const { return UsesArena(type_mask()); } | |
constexpr bool HasName() const { return HasName(type_mask()); } | |
constexpr bool HasLiteralName() const { return HasLiteralName(type_mask()); } | |
constexpr bool HasList() const { return HasList(type_mask()); } | |
constexpr bool IsIterableFallback() const { return IsIterableFallback(type_mask()); } | |
bool operator==(const Type &other) const; | |
bool operator==(Is t) const { return type_mask() == static_cast<uint64_t>(t); } | |
bool operator==(MayBe t) const { return PureMask() == static_cast<uint64_t>(t); } | |
bool operator!=(const Type &other) const; | |
bool operator<(const Type &other) const; | |
bool operator>(const Type &other) const; | |
bool operator<=(const Type &other) const; | |
bool operator>=(const Type &other) const; | |
bool IsOnlyMask() const { return (IsSet() && ValueFactory::IsEmpty(ptr())); } | |
Handle<TString> Name() const { return Handle<TString>::New(ptr()); } | |
Handle<const TypeList> List() const { return Handle<const TypeList>::New(ptr()); } | |
const char *LiteralName() const { return (const char *) ptr(); } | |
void SetPtr(void *p) { set_ptr(p); } | |
template <typename Field> | |
void SetField(bool value) { | |
bits_ = Field::update(bits_, value); | |
} | |
template <typename Field> | |
void ToggleField() { | |
bits_ = SetField<Field>(!Field::decode(bits_)); | |
} | |
void SetPtrAndKind(void *p, uint32_t kind_bit) { | |
set_ptr(p); | |
bits_ &= ~KindField::kMask; | |
bits_ |= kind_bit; | |
} | |
void SetList(TypeList *list) { | |
set_ptr(list); | |
bits_ &= ~KindField::kMask; | |
SetField<ListField>(true); | |
} | |
static constexpr bool MayBePacked(MayBe t) { return (t & MayBe::ArrayPacked) != MayBe::None; } | |
static constexpr bool MayBeHash(MayBe t) { return (t & (MayBe::ArrayNumericHash | MayBe::ArrayKeyString)) != MayBe::None; } | |
static constexpr bool MayBePackedOnly(MayBe t) { return MayBePacked(t) && !MayBeHash(t); } | |
static constexpr bool MayBeHashOnly(MayBe t) { return MayBeHash(t) && !MayBePacked(t); } | |
static constexpr std::string_view GetCName(Is type) { | |
switch (type) { | |
case Is::False: | |
case Is::True: | |
case Is::Bool: return "bool"; | |
case Is::Long: return "int"; | |
case Is::Double: return "float"; | |
case Is::String: return "string"; | |
case Is::Object: return "object"; | |
case Is::Resource: return "resource"; | |
case Is::Null: return "null"; | |
case Is::Callable: return "callable"; | |
case Is::Iterable: return "iterable"; | |
case Is::Array: return "array"; | |
case Is::Void: return "void"; | |
case Is::Mixed: return "mixed"; | |
case Is::Number: return "int|float"; | |
case Is::Never: return "never"; | |
default: // should never happen | |
return "unknown"; | |
} | |
} | |
// concatenate strings with | | |
constexpr std::string_view GetCName() const { return GetCName(static_cast<Is>(type_mask() & kMask)); } | |
static constexpr uint64_t ResolveFlags(SendMode send_mode, | |
bool allow_variadic, | |
bool allow_tentative, | |
bool allow_null, | |
bool is_promoted = false) { | |
return SendModeField::encode(send_mode) | VariadicField::encode(allow_variadic) | | |
TentativeField::encode(allow_tentative) | MayBeFields::Null::encode(allow_null) | | |
PromotedField::encode(is_promoted); | |
} | |
static constexpr uint64_t ResolveFlags(bool allow_reference, | |
bool allow_variadic, | |
bool allow_tentative, | |
bool allow_null, | |
bool is_promoted = false) { | |
return ResolveFlags(SendMode(allow_reference), allow_variadic, allow_tentative, allow_null, is_promoted); | |
} | |
static constexpr MayBe Encode(Is t) { | |
return (t) == Is::Bool ? MayBe::Bool | |
: ((t) == Is::Iterable ? static_cast<MayBe>(IterableField::encode(true)) | |
: ((t) == Is::Mixed ? MayBe::Any : static_cast<MayBe>(1 << t))); | |
} | |
static constexpr uint64_t MayBeTypeMask(Is t) { return uint64_t(Encode(t)); } | |
template <Is t> | |
static constexpr MayBe Encode() { | |
return Encode(t); | |
} | |
struct ConfigObject { | |
bool allow_reference{false}; | |
bool allow_variadic{false}; | |
bool allow_tentative{false}; | |
bool allow_null{false}; | |
bool is_promoted{false}; | |
constexpr ConfigObject(bool allow_reference = false, | |
bool allow_variadic = false, | |
bool allow_tentative = false, | |
bool allow_null = false, | |
bool is_promoted = false) | |
: allow_reference(allow_reference), | |
allow_variadic(allow_variadic), | |
allow_tentative(allow_tentative), | |
allow_null(allow_null), | |
is_promoted(is_promoted) {} | |
constexpr operator uint64_t() const { | |
return ResolveFlags(allow_reference, allow_variadic, allow_tentative, allow_null, is_promoted); | |
} | |
}; | |
template <size_t N> | |
constexpr Type(const StaticString<N> &p, ConfigObject config = {}) | |
: // addr_(p.address()), | |
ptr_(p.void_ptr()), | |
bits_(LiteralNameField::encode(true) | config) {} | |
constexpr Type(const TypeList *list, ConfigObject config = {}) | |
: // addr_(std::bit_cast<i::Address>(const_cast<TypeList *>(list))), | |
ptr_(static_cast<void *>(const_cast<TypeList *>(list))), | |
bits_(config) {} | |
template <size_t N> | |
constexpr Type(const char (&p)[N], ConfigObject config = {}) | |
: addr_(AsAddress(p)), bits_(LiteralNameField::encode(true) | config) {} | |
constexpr Type(const char *p, ConfigObject config = {}) | |
: addr_(AsAddress(p)), bits_(LiteralNameField::encode(true) | config) {} | |
constexpr Type(TString *p, ConfigObject config = {}) : addr_(AsAddress(p)), bits_(NameField::encode(true) | config) {} | |
constexpr Type(ClassEntry *p, ConfigObject config = {}) : addr_(AsAddress(p)), bits_(NameField::encode(true) | config) {} | |
constexpr Type(Handle<TString> p) : addr_(p.ptr()), bits_(NameField::encode(true)) {} | |
constexpr Type(Handle<ClassEntry> p) : addr_(p.ptr()), bits_(NameField::encode(true)) {} | |
constexpr Type(std::nullptr_t, ConfigObject config = {}) : addr_(ValueFactory::kEmpty), bits_(config) {} | |
constexpr Type(ConfigObject config) : addr_(ValueFactory::kEmpty), bits_(config) {} | |
constexpr Type(MayBe t, ConfigObject config = {}) : addr_(ValueFactory::kEmpty), bits_(static_cast<uint64_t>(t) | config) {} | |
constexpr Type(Is t, ConfigObject config = {}) : Type(Encode(t), config) {} | |
constexpr Type(Flags flags) : addr_(ValueFactory::kEmpty), bits_(static_cast<uint64_t>(flags)) {} | |
constexpr Type(Is t, Flags flags, i::Address ptr = ValueFactory::kEmpty) | |
: addr_(ptr), bits_(MayBeTypeMask(t) | static_cast<uint64_t>(flags)) {} | |
constexpr Type(MayBe t, Flags flags, i::Address ptr = ValueFactory::kEmpty) | |
: addr_(ptr), bits_(static_cast<uint64_t>(t) | static_cast<uint64_t>(flags)) {} | |
constexpr Type() : Type(Is::Undefined) {} | |
template <Is t, Flags... flags> | |
static constexpr Type Create() { | |
return Type(t, (static_cast<uint64_t>(flags) | ...)); | |
} | |
template <Flags... flags> | |
static constexpr Type Union(TypeList list) { | |
return FromList<Flags::UnionBit, flags...>(list); | |
} | |
template <Flags... flags> | |
static constexpr Type Intersection(TypeList list) { | |
return FromList<Flags::IntersectionBit, flags...>(list); | |
} | |
// static constexpr Type LiteralName(std::string_view name, uint32_t flags) { | |
// return {name.data(), LiteralNameField::encode(true) | flags}; | |
// } | |
template <size_t N> | |
static constexpr Type FromLiteralName(const char (&name)[N], uint32_t flags) { | |
return {name, LiteralNameField::encode(true) | flags}; | |
} | |
// class Iterator; | |
using iterator = TypeList::iterator; | |
inline iterator begin() const { | |
if (HasList()) { return List()->begin(); } | |
return iterator(this); | |
} | |
inline iterator end() const { | |
if (HasList()) { return List()->end(); } | |
return iterator(this); | |
} | |
union { | |
void *ptr_; | |
internal::Address addr_{ValueFactory::kEmpty}; | |
}; | |
uint64_t bits_; | |
private: | |
using ValueFactory = internal::ValueHelper; | |
template <typename T> | |
inline static i::Address AsAddress(T *ptr) { | |
return ValueFactory::ValueAsAddress<T>(ptr); | |
} | |
template <class T> | |
void set_ptr(T *ptr) { | |
addr_ = AsAddress(ptr); | |
} | |
}; | |
LEND_EXPORT inline constexpr auto GetTypeByConst(Type::Is type) { | |
using namespace base::literals; | |
switch (type) { | |
case Type::Is::False: | |
case Type::Is::True: | |
case Type::Is::Bool: return "bool"_csv; | |
case Type::Is::Long: return "int"_csv; | |
case Type::Is::Double: return "float"_csv; | |
case Type::Is::String: return "string"_csv; | |
case Type::Is::Object: return "object"_csv; | |
case Type::Is::Resource: return "resource"_csv; | |
case Type::Is::Null: return "null"_csv; | |
case Type::Is::Callable: return "callable"_csv; | |
case Type::Is::Iterable: return "iterable"_csv; | |
case Type::Is::Array: return "array"_csv; | |
case Type::Is::Void: return "void"_csv; | |
case Type::Is::Mixed: return "mixed"_csv; | |
case Type::Is::Number: return "int|float"_csv; | |
case Type::Is::Undefined: return "undefined"_csv; | |
case Type::Is::Reference: return "reference"_csv; | |
case Type::Is::ConstantAst: return "ast"_csv; | |
case Type::Is::Static: return "static"_csv; | |
case Type::Is::Never: return "never"_csv; | |
default: return "unknown"_csv; | |
} | |
} | |
inline std::ostream &operator<<(std::ostream &os, const Type &type) { | |
if (type.HasName()) { | |
os << type.Name(); | |
} else if (type.HasLiteralName()) { | |
os << type.LiteralName(); | |
} else if (type.HasList()) { | |
os << "["; | |
for (auto &t : type) { os << t << ", "; } | |
os << "]"; | |
} else { | |
os << GetTypeByConst(static_cast<Type::Is>(type.type_mask() & Type::kMask)).data(); | |
} | |
return os; | |
} | |
} // namespace lend |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment