Skip to content

Instantly share code, notes, and snippets.

@oplanre
Last active April 8, 2024 14:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save oplanre/0b3a91b621000f17e7fa8304363f52c8 to your computer and use it in GitHub Desktop.
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
#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
#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
#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