#pragma once #include #include #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 concept InlineValueTypes = std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; template concept HandleTypes = std::is_same_v>> || std::is_same_v> || std::is_same_v> || std::is_same_v> || std::is_same_v> || std::is_same_v> || std::is_same_v> || std::is_same_v> || std::is_same_v> || std::is_same_v>; template concept TValIsh = InlineValueTypes || HandleTypes; } // 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; using FlagsField = IsField::template Next; 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 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 { static constexpr uint64_t value = uint64_t(Is::InternedString); static constexpr Is type = Is::InternedString; }; template static constexpr Is Initialize() { return Metadata::value; } template static constexpr uint64_t Update(uint64_t bits) { return Metadata::value | (bits & FlagsField::kMask); } enum class IsExtended : uint32_t { Array = Metadata::value, Object = Metadata::value, String = Metadata::value, InternedString = Metadata::value, Resource = Metadata::value, Reference = Metadata::value, ConstantAst = Metadata::value, BigInt = Metadata::value, }; template using NextBitField = FlagsField::template Next; 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; using HighBits = LowBits::template Next; static_assert(HighBits::kSize == 32 && HighBits::kMax == std::numeric_limits::max()); uint32_t *high_bits_ptr_raw() { return reinterpret_cast(&type_info_) + 1; } Handle high_bits_ptr() { return Handle::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()) { storage_.long_ = that.storage_.long_; } else if (that.Is()) { 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 requires(std::is_enum_v && !std::is_same_v && !std::is_same_v) constexpr Value(E enum_value) : storage_{static_cast(enum_value)} { Type(Type::Is::Long); } constexpr Value(double floating_point) : storage_{.double_ = floating_point} { Type(Type::Is::Double); } constexpr Value(Handle> string) { if (string->IsInterned()) { Set(string); } else { Set(string); } } constexpr Value(Handle> array) { Set(array); } constexpr Value(Handle> object) { Set(object); } constexpr Value(Handle> resource) { Set(resource); } constexpr Value(Handle> reference) { Set(reference); } constexpr Value(Handle> ast) { Set(ast); } constexpr Value(Handle string) : Value(string.As>()) {} constexpr Value(Handle array) { Set(array); } constexpr Value(Handle object) { Set(object); } constexpr Value(Handle resource) { Set(resource); } constexpr Value(Handle reference) { Set(reference); } constexpr Value(Handle ast) { Set(ast); } template constexpr Value(Handle h) { Set(h); } template LEND_NODISCARD constexpr bool Is() const { return base::IsOneOf(Type(), First, Rest...); } template LEND_NODISCARD constexpr bool IsNot() const { return Type() != T; } constexpr Value(const Value &that) // : type_info_(that.type_info_) { CopyLowBits(that); if (that.Is()) { storage_.long_ = that.storage_.long_; } else if (that.Is()) { storage_.double_ = that.storage_.double_; } else { storage_.address_ = that.storage_.address_; } } // constexpr Value(Handle v) : type_info_(HighBits::update(v->value_.type_info_, 0)) { // if (v->Is()) { // long_ = v->Long(); // } else if (v->Is()) { // double_ = v->Double(); // } else { // address_ = v->value_.address_; // } // } constexpr Value(Handle v) : Value(v->value_) {} constexpr Value(TVal that) : Value(that.value_) {} constexpr Value(Value &&that) : type_info_(that.type_info_) { if (that.Is()) { storage_.long_ = that.storage_.long_; } else if (that.Is()) { storage_.double_ = that.storage_.double_; } else { storage_.address_ = that.storage_.address_; } } constexpr Value &operator=(const Value &that) { if (that.Is()) { storage_.long_ = that.storage_.long_; } else if (that.Is()) { 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 Handle ToHandle() { return Handle::New(ptr()); } template Handle ToHandle() const { return Handle::New(ptr()); } template T *As() { return reinterpret_cast(ptr()); } template T *As() const { return reinterpret_cast(ptr()); } template void Set(T *p) { storage_.address_ = internal::ValueHelper::ValueAsAddress(p); type_info_ = LowBits::update(type_info_, TypeInfo::Metadata::value); } template void Set(Handle p) { storage_.address_ = p.ptr(); type_info_ = LowBits::update(type_info_, TypeInfo::Metadata::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 String() { return value_.ToHandle(); } LEND_NODISCARD Handle String() const { return value_.ToHandle(); } template Handle> Object() { return value_.ToHandle>(); } template Handle> Object() const { return value_.ToHandle>(); } LEND_NODISCARD Handle Indirect() const { return value_.ToHandle(); } template LEND_NODISCARD T *Pointer() const { return value_.template As(); } template LEND_NODISCARD T *Pointer() { return value_.template As(); } LEND_NODISCARD internal::Address Address() const { return value_.ptr(); } template LEND_NODISCARD Handle PointerAsHandle() const { return value_.ToHandle(); } LEND_NODISCARD auto Counted() const { return PointerAsHandle(); } LEND_NODISCARD auto Array() const { return PointerAsHandle(); } LEND_NODISCARD auto Resource() const { return PointerAsHandle(); } LEND_NODISCARD auto Reference() const { return PointerAsHandle(); } LEND_NODISCARD auto Ast() const { return PointerAsHandle(); } LEND_NODISCARD auto ClassEntry() const { return PointerAsHandle(); } LEND_NODISCARD auto Function() const { return PointerAsHandle(); } LEND_NODISCARD auto Counted() { return PointerAsHandle(); } LEND_NODISCARD auto Array() { return PointerAsHandle(); } LEND_NODISCARD auto Resource() { return PointerAsHandle(); } LEND_NODISCARD auto Reference() { return PointerAsHandle(); } LEND_NODISCARD auto Ast() { return PointerAsHandle(); } LEND_NODISCARD auto ClassEntry() { return PointerAsHandle(); } LEND_NODISCARD auto Function() { return PointerAsHandle(); } LEND_INLINE void UnwrapReference(); LEND_NODISCARD Handle 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(); } LEND_NODISCARD bool IsCopyableType() const { return Is(); } LEND_NODISCARD bool IsImmutableType() const { return Is(); } LEND_NODISCARD bool IsNumber() const { return Is() || Is(); } LEND_NODISCARD bool IsTrue() { return Is(); } LEND_NODISCARD bool IsFalse() { return Is(); } LEND_NODISCARD bool IsReference() { return Is(); } LEND_NODISCARD bool IsUndefined() { return Is(); } LEND_NODISCARD bool IsReference() const { return Is(); } LEND_NODISCARD bool IsUndefined() const { return Is(); } LEND_NODISCARD bool IsNull() { return Is(); } LEND_NODISCARD bool IsError() { return Is(); } 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 that) { value_ = Value(that); } void CopyValue(Handle 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 that); void Duplicate(Handle 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 that); LEND_INLINE void CopyOrDuplicate(const TVal &that) { CopyOrDuplicate(that.ToHandle()); } void CopyValueProperty(const TVal *that) { *this = *that; } void CopyValueProperty(Handle that) { CopyValueProperty(*that); } void CopyProperty(TVal &that) { Copy(that.ToHandle()); PropFlag(that.PropFlag()); } void CopyProperty(Handle that) { Copy(that); PropFlag(that->PropFlag()); } void CopyOrDuplicateProperty(Handle that) { CopyOrDuplicate(that); PropFlag(that->PropFlag()); } LEND_NODISCARD bool MayModifyArgInPlace() const { return IsHeapObject() && !(Counted()->IsImmutable() || Counted()->IsPersistent()) && Refcount() == 1; } template LEND_NODISCARD constexpr bool Is() const { return value_.template Is(); } template LEND_NODISCARD constexpr bool IsNot() const { return GetType() != T; } Handle ToHandle() { return Handle::New(this); } Handle ToHandle() const { return Handle::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 r); void NewReference(uint32_t refcount); void NewPersistentReference(Handle 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 TypeName(); constexpr Vector ValueName(); constexpr Vector TypeName() const; constexpr Vector ValueName() const; LEND_INLINE void InternedString(TString *s) { value_.Set(s); } LEND_INLINE void NewString(TString *s) { value_.Set(s); } LEND_INLINE void InternedString(Handle s) { value_.Set(s); } LEND_INLINE void NewString(Handle s) { value_.Set(s); } LEND_INLINE void Resource(class Resource *r) { value_.Set(r); } LEND_INLINE void BigInt(internal::BigIntBase *b) { value_.Set(b); } LEND_INLINE void Reference(class Reference *r) { value_.Set(r); } LEND_INLINE void Array(Handle a) { value_.Set(a); } LEND_INLINE void Array(const PHPArray &a) { value_.Set(&a); } LEND_INLINE void Ast(AstReference *ast) { value_.Set(ast); } LEND_INLINE void Indirect(Handle v) { value_.Set(v); } LEND_INLINE void Indirect(const TVal &v) { value_.Set(&v); } template LEND_INLINE void Object(PHPObjectBase *o) { value_.Set(o); } template LEND_INLINE void Object(Handle> o) { value_.Set(o); } template LEND_INLINE void Pointer(T *p) { value_.Set(p); } template LEND_INLINE void AliasPointer(T *p) { value_.Set(p); } template LEND_INLINE void Pointer(Handle p) { value_.Set(p); } template LEND_INLINE void AliasPointer(Handle p) { value_.Set(p); } LEND_INLINE void Function(interpreter::Function *f) { value_.Set(f); } LEND_INLINE void ClassEntry(lend::ClassEntry *c) { value_.Set(c); } LEND_INLINE void String(Handle> s) { value_.Set(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 LEND_INLINE Vector StringBytes(); template LEND_INLINE Vector StringBytes() const; LEND_INLINE void EmptyArray(); LEND_INLINE void EmptyString(); LEND_INLINE void EmptyPersistentString(); LEND_INLINE void String(Vector); void String(const char *); void String(const char *, size_t); LEND_INLINE void PersistentString(Vector); LEND_INLINE void FastString(Vector); 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 s); LEND_INLINE void NewPersistentArray(); // LEND_INLINE void CopyObject(PHPObject *o); template LEND_INLINE void CopyObject(Handle>> o) { Object(o); o->IncRef(1); } template LEND_INLINE void CopyObject(Handle> o) { Object(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 v); LEND_INLINE void DetachString(); LEND_INLINE void DetachArray(); LEND_INLINE void DetachNoRef(); LEND_INLINE void Detach(); LEND_INLINE Handle ReferencedVal(); LEND_INLINE Handle ReferencedVal() const; constexpr void Undefine() { SetType(Type::Is::Undefined); } constexpr TVal() { Undefine(); } constexpr TVal(const TVal &that) { CopyValue(that); } constexpr TVal(Maybe &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 requires(std::is_enum_v && !std::is_same_v && !std::is_same_v) LEND_INLINE explicit constexpr TVal(E e) : TVal(static_cast(e)) {} LEND_INLINE explicit constexpr TVal(double floating_point) : value_(floating_point) {} LEND_INLINE explicit constexpr TVal(Handle string) : value_(string.As>()) {} 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 v); LEND_INLINE explicit constexpr TVal(PHPArray *array); LEND_INLINE explicit constexpr TVal(PHPObject *object); LEND_INLINE explicit constexpr TVal(Handle array); LEND_INLINE explicit constexpr TVal(Handle 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 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 void InitObject(); Result InitObject(Handle ce, Handle properties = {}) { return InitObjectImpl(ce, properties); } LEND_INLINE void AddAssociation(Vector key) { AddAssociationImpl(key, TVal()); } template LEND_INLINE void AddAssociation(Vector key, T value) { AddAssociationImpl(key, TVal(value)); } template LEND_INLINE void AddAssociation(Vector key, T &&value) { AddAssociationImpl(key, TVal(std::forward(value))); } LEND_INLINE void AddIndex(lend::ULong, const char *, size_t); LEND_INLINE Result AddIndex(lend::ULong, lend::Handle value); LEND_INLINE Result AddIndex(lend::ULong, TVal &&value); template LEND_INLINE void AddIndex(lend::ULong idx, T value) { AddIndex(idx, TVal(value)); } template LEND_INLINE Result Push(T value); Result array_set_zval_key(PHPArray *ht, lend::Handle key, lend::Handle value); LEND_INLINE void AddProperty(Vector); template LEND_INLINE void AddProperty(Vector key, T value) { AddPropertyImpl(key, TVal(value)); } template LEND_INLINE void AddProperty(Vector key, T &&value) { AddPropertyImpl(key, TVal(std::forward(value))); } template LEND_INLINE void AddProperty(const char *key, T value) { AddPropertyImpl(base::CStrVector(key), TVal(value)); } template LEND_INLINE void AddProperty(const char *key, T &&value) { AddPropertyImpl(base::CStrVector(key), TVal(std::forward(value))); } bool IsIdentical(Handle 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 AsStringImpl() const; Handle AsString() const; LEND_INLINE auto AsLong(bool is_strict = false) const { return (Is()) ? Long() : AsLongImpl(is_strict); } LEND_INLINE auto AsDouble() const { return (Is()) ? Double() : AsDoubleImpl(); } LEND_INLINE auto AsBool() const { return (Is()) ? true : (Is()) ? false : IsTruthy(); } private: void CopyCtor(); Result InitObjectImpl(Handle ce, Handle properties = {}); void AddPropertyImpl(Vector key, lend::Handle value); void AddPropertyImpl(Vector key, TVal value); void AddAssociationImpl(Vector key, lend::TVal value); friend class Operations; friend class Converter; friend Type::Is IsNumericString(Vector str, Handle 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