| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_DEVELOPER_SHELL_INTERPRETER_SRC_VALUE_H_ |
| #define SRC_DEVELOPER_SHELL_INTERPRETER_SRC_VALUE_H_ |
| |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include <cstdint> |
| #include <string> |
| |
| namespace shell { |
| namespace interpreter { |
| |
| template <typename Type, typename ContainerType> |
| class Container; |
| |
| class Interpreter; |
| class ObjectSchema; |
| class ObjectFieldSchema; |
| class Schema; |
| class Thread; |
| class TypeObject; |
| class TypeString; |
| class Value; |
| |
| enum class ValueType { |
| // Value is not defined. This is, for example, the case when we try to load a global which doesn't |
| // exist. |
| kUndef, |
| // The value is a 8 bit signed integer. |
| kInt8, |
| // The value is a 8 bit unsigned integer. |
| kUint8, |
| // The value is a 16 bit signed integer. |
| kInt16, |
| // The value is a 16 bit unsigned integer. |
| kUint16, |
| // The value is a 32 bit signed integer. |
| kInt32, |
| // The value is a 32 bit unsigned integer. |
| kUint32, |
| // The value is a 64 bit signed integer. |
| kInt64, |
| // The value is a 64 bit unsigned integer. |
| kUint64, |
| // The value is a string. |
| kString, |
| // The value is an object. |
| kObject, |
| }; |
| |
| // Base class for all reference counted objects. |
| class ReferenceCountedBase { |
| friend class ExecutionScope; |
| friend class TypeObject; |
| friend class TypeString; |
| friend void StringConcatenation(Thread* thread, uint64_t count); |
| |
| public: |
| size_t reference_count() const { return reference_count_; } |
| |
| protected: |
| explicit ReferenceCountedBase(Interpreter* interpreter) : interpreter_(interpreter) {} |
| ReferenceCountedBase(const ReferenceCountedBase&) = delete; |
| ReferenceCountedBase(ReferenceCountedBase&&) = delete; |
| virtual ~ReferenceCountedBase() = default; |
| |
| ReferenceCountedBase& operator=(const ReferenceCountedBase&) = delete; |
| ReferenceCountedBase& operator=(ReferenceCountedBase&&) = delete; |
| |
| protected: |
| // Adds a reference to this value. |
| void Use() { |
| // reference_count_ is initalized at one (the reference for the creator). That means it can |
| // never be zero. |
| FX_DCHECK(reference_count_ > 0); |
| ++reference_count_; |
| } |
| |
| // Releases a reference to this value. When the count is zero, the value is destroyed. |
| void Release() { |
| FX_DCHECK(reference_count_ > 0); |
| if (--reference_count_ == 0) { |
| Free(); |
| } |
| } |
| |
| // Reference count for the value. When the count reaches zero, the value is destroyed. |
| size_t reference_count_ = 1; |
| |
| // Does the hard work of freeing this object. Override if you have special semantics when |
| // freeing. |
| virtual void Free() { delete this; } |
| |
| Interpreter* const interpreter_; |
| }; |
| |
| // Base class for all reference counted objects. |
| template <typename Type> |
| class ReferenceCounted : public ReferenceCountedBase { |
| template <typename ObjectType, typename ContainerType> |
| friend class Container; |
| friend class TypeObject; |
| friend class TypeString; |
| friend class StringContainer; |
| |
| friend class Value; |
| |
| protected: |
| explicit ReferenceCounted(Interpreter* interpreter) : ReferenceCountedBase(interpreter) {} |
| ReferenceCounted(const ReferenceCounted<Type>&) = delete; |
| ReferenceCounted(ReferenceCounted<Type>&&) = delete; |
| |
| ReferenceCounted<Type>& operator=(const ReferenceCounted<Type>&) = delete; |
| ReferenceCounted<Type>& operator=(ReferenceCounted<Type>&&) = delete; |
| |
| private: |
| // Adds a reference to this value. |
| Type* Use() { |
| ReferenceCountedBase::Use(); |
| return reinterpret_cast<Type*>(this); |
| } |
| |
| // Reference count for the value. When the count reaches zero, the value is destroyed. |
| size_t reference_count_ = 1; |
| }; |
| |
| // Helper class for reference counted objects. This automatically manages the references. |
| template <typename Type, typename ContainerType> |
| class Container { |
| protected: |
| explicit Container(Type* data) : data_(data) {} |
| Container(const ContainerType& from) : data_(from.data_->Use()) {} |
| Container(ContainerType&& from) : data_(std::move(from.data_)) {} |
| ~Container() { data_->Release(); } |
| |
| public: |
| void operator=(const ContainerType& from) { |
| Type* data = from->data_->Use(); |
| data_->Release(); |
| data_ = data; |
| } |
| |
| void operator=(ContainerType&& from) { data_ = std::move(from->data_); } |
| |
| Type* data() const { return data_; } |
| |
| private: |
| Type* data_; |
| }; |
| |
| // Defines a string. The value is not mutable. |
| class String : public ReferenceCounted<String> { |
| public: |
| String(Interpreter* interpreter, std::string_view value); |
| String(Interpreter* interpreter, std::string&& value); |
| virtual ~String(); |
| |
| const std::string& value() const { return value_; } |
| size_t size() const { return value_.size(); } |
| |
| private: |
| const std::string value_; |
| }; |
| |
| class Object : public ReferenceCounted<Object> { |
| public: |
| Object(Interpreter* interpreter, const std::shared_ptr<ObjectSchema> schema); |
| virtual ~Object(); |
| |
| const std::shared_ptr<ObjectSchema> schema(); |
| |
| std::unique_ptr<Value> GetField(const ObjectFieldSchema*) const; |
| |
| void SetField(const ObjectFieldSchema*, uint64_t value); |
| |
| private: |
| virtual void Free() override; |
| const std::shared_ptr<ObjectSchema> schema_; |
| }; |
| |
| // Helper class for strings. This automatically manages the references. |
| class StringContainer : public Container<String, StringContainer> { |
| public: |
| StringContainer(Interpreter* interpreter, std::string_view value) |
| : Container(new String(interpreter, value)) {} |
| explicit StringContainer(String* string) : Container(string->Use()) {} |
| StringContainer(const StringContainer& from) : Container(from) {} |
| StringContainer(StringContainer&& from) : Container(from) {} |
| }; |
| |
| // Stores any value manageable by the interpreter. This is used when something has an undefined |
| // type. That means that we can assign any type of value to it (integer, string, ...). |
| // Currently, it's used when the client asks for the value of a global. |
| class Value { |
| public: |
| Value() = default; |
| ~Value() { Release(); } |
| |
| ValueType type() const { return type_; } |
| |
| int64_t GetInt8() const { |
| FX_DCHECK(type_ == ValueType::kInt8); |
| return int8_value_; |
| } |
| void SetInt8(int8_t value) { |
| Release(); |
| type_ = ValueType::kInt8; |
| int8_value_ = value; |
| } |
| |
| uint8_t GetUint8() const { |
| FX_DCHECK(type_ == ValueType::kUint8); |
| return uint8_value_; |
| } |
| void SetUint8(uint8_t value) { |
| Release(); |
| type_ = ValueType::kUint8; |
| uint8_value_ = value; |
| } |
| |
| int16_t GetInt16() const { |
| FX_DCHECK(type_ == ValueType::kInt16); |
| return int16_value_; |
| } |
| void SetInt16(int16_t value) { |
| Release(); |
| type_ = ValueType::kInt16; |
| int16_value_ = value; |
| } |
| |
| uint16_t GetUint16() const { |
| FX_DCHECK(type_ == ValueType::kUint16); |
| return uint16_value_; |
| } |
| void SetUint16(uint16_t value) { |
| Release(); |
| type_ = ValueType::kUint16; |
| uint16_value_ = value; |
| } |
| |
| int32_t GetInt32() const { |
| FX_DCHECK(type_ == ValueType::kInt32); |
| return int32_value_; |
| } |
| void SetInt32(int32_t value) { |
| Release(); |
| type_ = ValueType::kInt32; |
| int32_value_ = value; |
| } |
| |
| uint32_t GetUint32() const { |
| FX_DCHECK(type_ == ValueType::kUint32); |
| return uint32_value_; |
| } |
| void SetUint32(uint32_t value) { |
| Release(); |
| type_ = ValueType::kUint32; |
| uint32_value_ = value; |
| } |
| |
| int64_t GetInt64() const { |
| FX_DCHECK(type_ == ValueType::kInt64); |
| return int64_value_; |
| } |
| void SetInt64(int64_t value) { |
| Release(); |
| type_ = ValueType::kInt64; |
| int64_value_ = value; |
| } |
| |
| uint64_t GetUint64() const { |
| FX_DCHECK(type_ == ValueType::kUint64); |
| return uint64_value_; |
| } |
| void SetUint64(uint64_t value) { |
| Release(); |
| type_ = ValueType::kUint64; |
| uint64_value_ = value; |
| } |
| |
| String* GetString() const { |
| FX_DCHECK(type_ == ValueType::kString); |
| return string_; |
| } |
| void SetString(Interpreter* interpreter, std::string_view value) { |
| // Creates the new value before releasing the old one to avoid potential use after free problem. |
| String* string = new String(interpreter, value); |
| Release(); |
| type_ = ValueType::kString; |
| string_ = string; |
| } |
| void SetString(String* value) { |
| // Take a new reference to the value before releasing the old one to avoid potential use after |
| // free problem. |
| String* string = value->Use(); |
| Release(); |
| type_ = ValueType::kString; |
| string_ = string; |
| } |
| |
| Object* GetObject() const { |
| FX_DCHECK(type_ == ValueType::kObject); |
| return object_; |
| } |
| void SetObject(Object* value) { |
| Object* object = value->Use(); |
| Release(); |
| type_ = ValueType::kObject; |
| object_ = object; |
| } |
| |
| void Set(const Value& value); |
| |
| private: |
| // Release the data for this value. This is used when the value is destroyed or when is value is |
| // modified. |
| void Release(); |
| |
| // Current type for the value. |
| ValueType type_ = ValueType::kUndef; |
| union { |
| // Integer value when type is kInt8. |
| int8_t int8_value_; |
| // Integer value when type is kUint8. |
| uint8_t uint8_value_; |
| // Integer value when type is kInt16. |
| int16_t int16_value_; |
| // Integer value when type is kUint16. |
| uint16_t uint16_value_; |
| // Integer value when type is kInt32. |
| int32_t int32_value_; |
| // Integer value when type is kUint32. |
| uint32_t uint32_value_; |
| // Integer value when type is kInt64. |
| int64_t int64_value_; |
| // Integer value when type is kUint64. |
| uint64_t uint64_value_; |
| // String value when type is kString. |
| String* string_; |
| // Object value when type is kString. |
| Object* object_; |
| }; |
| }; |
| |
| } // namespace interpreter |
| } // namespace shell |
| |
| #endif // SRC_DEVELOPER_SHELL_INTERPRETER_SRC_VALUE_H_ |