/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ECMASCRIPT_JSOBJECT_H #define ECMASCRIPT_JSOBJECT_H #include "ecmascript/ecma_macros.h" #include "ecmascript/ecma_string.h" #include "ecmascript/ic/property_box.h" #include "ecmascript/js_handle.h" #include "ecmascript/js_hclass.h" #include "ecmascript/js_native_pointer.h" #include "ecmascript/js_tagged_value.h" #include "ecmascript/mem/slots.h" #include "ecmascript/mem/visitor.h" #include "ecmascript/method.h" #include "ecmascript/object_operator.h" #include "ecmascript/property_attributes.h" #include "ecmascript/tagged_array.h" namespace panda { namespace ecmascript { class ObjectOperator; class JSFunction; class AccessorData; class JSArray; class JSForInIterator; class LexicalEnv; class GlobalEnv; // Integrity level for objects enum IntegrityLevel { SEALED, FROZEN }; enum PositionKind { UNKNOWN = 0, INDEXED_PROPERTY = 1, INLINE_NAMED_PROPERTY = 2, OUT_NAMED_PROPERTY = 3 }; enum PropertyKind { KEY = 0, VALUE, KEY_VALUE }; // ecma6.0 6.2.4 The Property Descriptor Specification Type class PropertyDescriptor final { public: PropertyDescriptor() = delete; ~PropertyDescriptor() = default; DEFAULT_NOEXCEPT_MOVE_SEMANTIC(PropertyDescriptor); DEFAULT_COPY_SEMANTIC(PropertyDescriptor); explicit PropertyDescriptor(const JSThread *thread) : thread_(thread) {} PropertyDescriptor(const JSThread *thread, JSHandle v) : thread_(thread), value_(v) {} PropertyDescriptor(const JSThread *thread, JSHandle v, bool w, bool e, bool c) : thread_(thread), writable_(w), enumerable_(e), configurable_(c), hasWritable_(true), hasEnumerable_(true), hasConfigurable_(true), value_(v) { } PropertyDescriptor(const JSThread *thread, bool w, bool e, bool c) : PropertyDescriptor(thread, JSHandle(), w, e, c) { } inline JSHandle GetValue() const { if (value_.IsEmpty()) { return JSHandle(thread_, JSTaggedValue::Undefined()); } return value_; } inline void SetValue(JSHandle value) { value_ = value; } inline bool IsWritable() const { return writable_; } inline void SetWritable(bool flag) { writable_ = flag; hasWritable_ = true; } inline bool IsEnumerable() const { return enumerable_; } inline void SetEnumerable(bool flag) { enumerable_ = flag; hasEnumerable_ = true; } inline bool IsConfigurable() const { return configurable_; } inline void SetConfigurable(bool flag) { configurable_ = flag; hasConfigurable_ = true; } inline bool HasValue() const { return !value_.IsEmpty(); } inline bool HasWritable() const { return hasWritable_; } inline bool HasConfigurable() const { return hasConfigurable_; } inline bool HasEnumerable() const { return hasEnumerable_; } inline bool HasGetter() const { return !getter_.IsEmpty(); } inline bool HasSetter() const { return !setter_.IsEmpty(); } inline JSHandle GetGetter() const { if (getter_->IsNull()) { return JSHandle(thread_, JSTaggedValue::Undefined()); } return getter_; } inline JSHandle GetSetter() const { if (setter_->IsNull()) { return JSHandle(thread_, JSTaggedValue::Undefined()); } return setter_; } inline void SetGetter(JSHandle value) { getter_ = value; } inline void SetSetter(JSHandle value) { setter_ = value; } // 6.2.4.1 inline bool IsAccessorDescriptor() const { // 2. If both Desc.[[Get]] and Desc.[[Set]] are absent, return false. return !(getter_.IsEmpty() && setter_.IsEmpty()); } inline bool IsDataDescriptor() const { // 2. If both Desc.[[Value]] and Desc.[[Writable]] are absent, return false. return !(value_.IsEmpty() && !hasWritable_); } inline bool IsGenericDescriptor() const { // 2. If IsAccessorDescriptor(Desc) and IsDataDescriptor(Desc) are both false, return true return !IsAccessorDescriptor() && !IsDataDescriptor(); } inline bool IsEmpty() const { return !hasWritable_ && !hasEnumerable_ && !hasConfigurable_ && !HasValue() && !HasGetter() && !HasSetter(); } static void CompletePropertyDescriptor(const JSThread *thread, PropertyDescriptor &desc); private: const JSThread *thread_{nullptr}; bool writable_ {false}; bool enumerable_ {false}; bool configurable_ {false}; bool hasWritable_ {false}; bool hasEnumerable_ {false}; bool hasConfigurable_ {false}; JSHandle value_ {}; JSHandle getter_ {}; JSHandle setter_ {}; }; enum class ElementTypes { ALLTYPES, STRING_AND_SYMBOL }; class PropertyMetaData { public: using IsFoundField = BitField; using IsInlinedPropsField = IsFoundField::NextFlag; using RepresentationField = IsInlinedPropsField::NextField; using OffsetField = RepresentationField::NextField; explicit PropertyMetaData(uint32_t metaData) : metaData_(metaData) {} ~PropertyMetaData() = default; DEFAULT_NOEXCEPT_MOVE_SEMANTIC(PropertyMetaData); DEFAULT_COPY_SEMANTIC(PropertyMetaData); explicit PropertyMetaData(bool isFound) { SetFound(isFound); } inline bool IsFound() const { return IsFoundField::Get(metaData_); } inline void SetFound(bool flag) { IsFoundField::Set(flag, &metaData_); } inline bool GetIsInlinedProps() const { return IsInlinedPropsField::Get(metaData_); } inline void SetIsInlinedProps(bool flag) { IsInlinedPropsField::Set(flag, &metaData_); } inline Representation GetRepresentation() const { return RepresentationField::Get(metaData_); } inline void SetRepresentation(Representation representation) { RepresentationField::Set(representation, &metaData_); } inline void SetOffset(uint32_t offset) { OffsetField::Set(offset, &metaData_); } inline uint32_t GetOffset() const { return OffsetField::Get(metaData_); } private: uint32_t metaData_{0}; }; class OperationResult { public: OperationResult(const JSThread *thread, JSTaggedValue value, PropertyMetaData metaData) : metaData_(metaData) { thread_ = thread; value_ = JSHandle(thread_, value); } ~OperationResult() = default; DEFAULT_NOEXCEPT_MOVE_SEMANTIC(OperationResult); DEFAULT_COPY_SEMANTIC(OperationResult); JSHandle GetValue() const { if (value_->IsPropertyBox()) { return JSHandle(thread_, PropertyBox::Cast(value_.GetTaggedValue().GetTaggedObject())->GetValue()); } return value_; } JSHandle GetRawValue() const { return value_; } const PropertyMetaData &GetPropertyMetaData() const { return metaData_; } private: const JSThread *thread_ {nullptr}; JSHandle value_ {}; PropertyMetaData metaData_ {0U}; }; class ECMAObject : public TaggedObject { public: static constexpr int HASH_INDEX = 0; static constexpr int FUNCTION_EXTRA_INDEX = 1; static constexpr int RESOLVED_MAX_SIZE = 2; CAST_CHECK(ECMAObject, IsECMAObject); void SetCallable(bool flag); bool IsCallable() const; Method *GetCallTarget() const; static constexpr size_t HASH_OFFSET = TaggedObjectSize(); static constexpr size_t SIZE = HASH_OFFSET + sizeof(JSTaggedType); void SetHash(int32_t hash); int32_t GetHash() const; bool HasHash() const; void InitializeHash() { Barriers::SetPrimitive(this, ECMAObject::HASH_OFFSET, JSTaggedValue(0).GetRawData()); } void* GetNativePointerField(int32_t index) const; void SetNativePointerField(int32_t index, void *nativePointer, const DeleteEntryPoint &callBack, void *data, size_t nativeBindingsize = 0); int32_t GetNativePointerFieldCount() const; void SetNativePointerFieldCount(int32_t count); DECL_VISIT_OBJECT(HASH_OFFSET, SIZE); void VisitObjects(const EcmaObjectRangeVisitor &visitor) { // no field in this object VisitRangeSlot(visitor); } }; class JSObject : public ECMAObject { public: static constexpr int MIN_ELEMENTS_LENGTH = 3; static constexpr int MIN_PROPERTIES_LENGTH = JSHClass::DEFAULT_CAPACITY_OF_IN_OBJECTS; static constexpr int PROPERTIES_GROW_SIZE = 4; static constexpr int FAST_ELEMENTS_FACTOR = 3; static constexpr int MIN_GAP = 256; static constexpr int MAX_GAP = 1_KB; static constexpr uint32_t MAX_ELEMENT_INDEX = std::numeric_limits::max(); CAST_CHECK(JSObject, IsECMAObject); // ecma6.0 6.2.4.4 static JSHandle FromPropertyDescriptor(JSThread *thread, const PropertyDescriptor &desc); // ecma6.0 6.2.4.5 ToPropertyDescriptor ( Obj ) static void ToPropertyDescriptor(JSThread *thread, const JSHandle &obj, PropertyDescriptor &desc); static bool ToPropertyDescriptorFast(JSThread *thread, const JSHandle &obj, PropertyDescriptor &desc); // ecma6 7.3 Operations on Objects static JSHandle GetMethod(JSThread *thread, const JSHandle &obj, const JSHandle &key); static bool CreateDataProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value); static bool CreateDataProperty(JSThread *thread, const JSHandle &obj, uint32_t index, const JSHandle &value); static bool CreateMethodProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value); static bool CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value); static bool CreateDataPropertyOrThrow(JSThread *thread, const JSHandle &obj, uint32_t index, const JSHandle &value); static JSHandle EnumerableOwnNames(JSThread *thread, const JSHandle &obj); // 7.3.23 EnumerableOwnPropertyNames ( O, kind ) static JSHandle EnumerableOwnPropertyNames(JSThread *thread, const JSHandle &obj, PropertyKind kind); static void EnumerableOwnPropertyNamesHelper(JSThread *thread, const JSHandle &obj, const JSHandle &arr, JSHandle &properties, uint32_t &index, bool &fastMode, PropertyKind kind); static JSHandle GetFunctionRealm(JSThread *thread, const JSHandle &object); static bool SetIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level); static bool TestIntegrityLevel(JSThread *thread, const JSHandle &obj, IntegrityLevel level); static JSHandle SpeciesConstructor(JSThread *thread, const JSHandle &obj, const JSHandle &defaultConstructort); // 7.3.17 template static JSHandle CreateListFromArrayLike(JSThread *thread, const JSHandle &obj); // ecma6 9.1 // [[GetPrototypeOf]] static JSTaggedValue GetPrototype(const JSHandle &obj); // [[SetPrototypeOf]] static bool SetPrototype(JSThread *thread, const JSHandle &obj, const JSHandle &proto); // [[IsExtensible]] bool IsExtensible() const; // [[PreventExtensions]] static bool PreventExtensions(JSThread *thread, const JSHandle &obj); // [[GetOwnProperty]] -> Undefined | Property Descriptor static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, PropertyDescriptor &desc); static bool GlobalGetOwnProperty(JSThread *thread, const JSHandle &key, PropertyDescriptor &desc); static bool OrdinaryGetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, PropertyDescriptor &desc); // [[DefineOwnProperty]] static bool DefineOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, const PropertyDescriptor &desc); static bool DefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, const PropertyDescriptor &desc); static bool OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, const PropertyDescriptor &desc); static bool OrdinaryDefineOwnProperty(JSThread *thread, const JSHandle &obj, uint32_t index, const PropertyDescriptor &desc); static bool IsCompatiblePropertyDescriptor(bool extensible, const PropertyDescriptor &desc, const PropertyDescriptor ¤t); static bool ValidateAndApplyPropertyDescriptor(ObjectOperator *op, bool extensible, const PropertyDescriptor &desc, const PropertyDescriptor ¤t); static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &receiver); static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); static OperationResult GetProperty(JSThread *thread, const JSHandle &obj, uint32_t index); static OperationResult GetPropertyFromGlobal(JSThread *thread, const JSHandle &key); static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value, bool mayThrow = false); static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value, bool mayThrow = false); static bool SetProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value, const JSHandle &receiver, bool mayThrow = false); static bool SetProperty(JSThread *thread, const JSHandle &obj, uint32_t index, const JSHandle &value, bool mayThrow = false); static bool GlobalSetProperty(JSThread *thread, const JSHandle &key, const JSHandle &value, bool mayThrow); // [[HasProperty]] static bool HasProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); static bool HasProperty(JSThread *thread, const JSHandle &obj, uint32_t index); // 9.1.10 [[Delete]] static bool DeleteProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key); // [[OwnPropertyKeys]] static JSHandle GetOwnPropertyKeys(JSThread *thread, const JSHandle &obj); static JSHandle GetOwnEnumPropertyKeys(JSThread *thread, const JSHandle &obj); // 9.1.13 ObjectCreate static JSHandle ObjectCreate(JSThread *thread, const JSHandle &proto); // 12.9.4 Runtime Semantics: InstanceofOperator(O, C) static bool InstanceOf(JSThread *thread, const JSHandle &object, const JSHandle &target); // 13.7.5.15 EnumerateObjectProperties ( O ); same as [[Enumerate]] static JSHandle EnumerateObjectProperties(JSThread *thread, const JSHandle &obj); static bool IsRegExp(JSThread *thread, const JSHandle &argument); static JSTaggedValue CallGetter(JSThread *thread, const AccessorData *accessor, const JSHandle &receiver); static bool CallSetter(JSThread *thread, const AccessorData &accessor, const JSHandle &receiver, const JSHandle &value, bool mayThrow = false); void FillElementsWithHoles(const JSThread *thread, uint32_t start, uint32_t end); JSHClass *GetJSHClass() const; bool IsJSGlobalObject() const; bool IsConstructor() const; bool IsECMAObject() const; bool IsJSError() const; bool IsArguments() const; bool IsDate() const; bool IsJSArray() const; bool IsJSMap() const; bool IsJSSet() const; bool IsJSRegExp() const; bool IsJSFunction() const; bool IsBoundFunction() const; bool IsJSIntlBoundFunction() const; bool IsProxyRevocFunction() const; bool IsAccessorData() const; bool IsJSGlobalEnv() const; bool IsJSProxy() const; bool IsGeneratorObject() const; bool IsAsyncGeneratorObject() const; bool IsForinIterator() const; bool IsJSSetIterator() const; bool IsJSRegExpIterator() const; bool IsJSMapIterator() const; bool IsJSArrayIterator() const; bool IsJSAPIArrayListIterator() const; bool IsJSAPIStackIterator() const; bool IsJSAPIVectorIterator() const; bool IsJSAPILinkedListIterator() const; bool IsJSAPIListIterator() const; bool IsJSPrimitiveRef() const; bool IsElementDict() const; bool IsPropertiesDict() const; bool IsTypedArray() const; static void DefinePropertyByLiteral(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value, bool useForClass = false); static void DefineSetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value); static void DefineGetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value); static JSHandle CreateObjectFromProperties(const JSThread *thread, const JSHandle &properties); static void GetAllKeys(const JSThread *thread, const JSHandle &obj, int offset, const JSHandle &keyArray); static void GetAllKeys(const JSHandle &obj, std::vector &keyVector); static void GetAllElementKeys(JSThread *thread, const JSHandle &obj, int offset, const JSHandle &keyArray); static void GetALLElementKeysIntoVector(const JSThread *thread, const JSHandle &obj, std::vector &keyVector); uint32_t GetNumberOfKeys(); uint32_t GetNumberOfElements(); static JSHandle GetEnumElementKeys(JSThread *thread, const JSHandle &obj, int offset, uint32_t numOfElements, uint32_t *keys); static void GetEnumElementKeys(JSThread *thread, const JSHandle &obj, int offset, const JSHandle &keyArray); static JSHandle GetAllEnumKeys(const JSThread *thread, const JSHandle &obj, int offset, uint32_t numOfKeys, uint32_t *keys); static void GetAllEnumKeys(const JSThread *thread, const JSHandle &obj, int offset, const JSHandle &keyArray); static void AddAccessor(JSThread *thread, const JSHandle &obj, const JSHandle &key, const JSHandle &value, PropertyAttributes attr); static constexpr size_t PROPERTIES_OFFSET = ECMAObject::SIZE; ACCESSORS(Properties, PROPERTIES_OFFSET, ELEMENTS_OFFSET); ACCESSORS(Elements, ELEMENTS_OFFSET, SIZE); DECL_VISIT_OBJECT_FOR_JS_OBJECT(ECMAObject, PROPERTIES_OFFSET, SIZE) DECL_DUMP() static JSHandle TransitionToDictionary(const JSThread *thread, const JSHandle &receiver); inline void SetPropertyInlinedProps(const JSThread *thread, uint32_t index, JSTaggedValue value); inline void SetPropertyInlinedProps(const JSThread *thread, const JSHClass *hclass, uint32_t index, JSTaggedValue value); inline JSTaggedValue GetPropertyInlinedProps(uint32_t index) const; inline JSTaggedValue GetPropertyInlinedProps(const JSHClass *hclass, uint32_t index) const; inline JSTaggedValue GetProperty(const JSHClass *hclass, PropertyAttributes attr) const; inline void SetProperty(const JSThread *thread, const JSHClass *hclass, PropertyAttributes attr, JSTaggedValue value); static bool IsArrayLengthWritable(JSThread *thread, const JSHandle &receiver); bool UpdatePropertyInDictionary(const JSThread *thread, JSTaggedValue key, JSTaggedValue value); static bool ShouldTransToDict(uint32_t capacity, uint32_t index); static JSHandle GrowElementsCapacity(const JSThread *thread, const JSHandle &obj, uint32_t capacity); static JSHandle IterableToList(JSThread *thread, const JSHandle &items, JSTaggedValue method = JSTaggedValue::Undefined()); protected: static void ElementsToDictionary(const JSThread *thread, JSHandle obj); private: friend class ObjectOperator; friend class LoadICRuntime; friend class StoreICRuntime; friend class ObjectFastOperator; friend class ICRuntimeStub; friend class RuntimeStubs; static bool AddElementInternal( JSThread *thread, const JSHandle &receiver, uint32_t index, const JSHandle &value, PropertyAttributes attr = PropertyAttributes(PropertyAttributes::GetDefaultAttributes())); static JSTaggedValue GetProperty(JSThread *thread, ObjectOperator *op); static bool SetProperty(ObjectOperator *op, const JSHandle &value, bool mayThrow); static void DeletePropertyInternal(JSThread *thread, const JSHandle &obj, const JSHandle &key, uint32_t index); int FindProperty(const JSHandle &key); static uint32_t ComputeElementCapacity(uint32_t oldCapacity); static uint32_t ComputePropertyCapacity(uint32_t oldCapacity); static JSTaggedValue ShouldGetValueFromBox(ObjectOperator *op); static std::pair, JSHandle> GetOwnEnumerableNamesInFastMode( JSThread *thread, const JSHandle &obj, uint32_t *copyLengthOfKeys, uint32_t *copyLengthOfElements); static bool CheckHClassHit(const JSHandle &obj, const JSHandle &cls); static uint32_t SetValuesOrEntries(JSThread *thread, const JSHandle &prop, uint32_t index, const JSHandle &key, const JSHandle &value, PropertyKind kind); }; } // namespace ecmascript } // namespace panda #endif