1 /* 2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef ECMASCRIPT_OBJECT_OPERATOR_H 17 #define ECMASCRIPT_OBJECT_OPERATOR_H 18 19 #include "ecmascript/js_handle.h" 20 #include "ecmascript/js_object.h" 21 #include "ecmascript/js_thread.h" 22 #include "ecmascript/property_attributes.h" 23 24 #include "ecmascript/ecma_string.h" 25 #include "libpandabase/utils/bit_field.h" 26 27 namespace panda::ecmascript { 28 class PropertyDescriptor; 29 class JSObject; 30 using SCheckMode = JSShared::SCheckMode; 31 32 enum class OperatorType : uint8_t { 33 PROTOTYPE_CHAIN, 34 OWN, 35 }; 36 37 class ObjectOperator final { 38 public: 39 ObjectOperator() = default; 40 41 ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &key, 42 OperatorType type = OperatorType::PROTOTYPE_CHAIN); 43 44 ObjectOperator(JSThread *thread, const JSHandle<JSObject> &holder, const JSHandle<JSTaggedValue> &key, 45 OperatorType type = OperatorType::PROTOTYPE_CHAIN); 46 47 ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder, const JSHandle<JSTaggedValue> &key, 48 OperatorType type = OperatorType::PROTOTYPE_CHAIN); 49 50 ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder, 51 const JSHandle<JSTaggedValue> &receiver, const JSHandle<JSTaggedValue> &key, 52 OperatorType type = OperatorType::PROTOTYPE_CHAIN); 53 54 ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder, uint32_t index, 55 OperatorType type = OperatorType::PROTOTYPE_CHAIN); 56 // op for fast path, name can only string and symbol, and can't be number. 57 ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, 58 OperatorType type = OperatorType::PROTOTYPE_CHAIN); 59 // op for fast add 60 ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, 61 const PropertyAttributes &attr); 62 63 static void FastAdd(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name, 64 const JSHandle<JSTaggedValue> &value, const PropertyAttributes &attr); 65 66 void UpdateDetector(); 67 static void PUBLIC_API UpdateDetector(const JSThread *thread, JSTaggedValue receiver, JSTaggedValue key); 68 static void UpdateDetectorOnSetPrototype(const JSThread *thread, JSTaggedValue receiver); 69 static bool IsDetectorName(const JSThread *thread, JSTaggedValue key); 70 71 NO_COPY_SEMANTIC(ObjectOperator); 72 DEFAULT_NOEXCEPT_MOVE_SEMANTIC(ObjectOperator); 73 ~ObjectOperator() = default; 74 75 /** 76 * Create ObjectOperator instance by new operator is forbidden, for the member holder is a JSHandle type. it must 77 * be created and destroyed on stack 78 */ 79 void *operator new([[maybe_unused]] size_t t) = delete; 80 void operator delete([[maybe_unused]] void *ptr) = delete; 81 IsFound()82 inline bool IsFound() const 83 { 84 return index_ != NOT_FOUND_INDEX; 85 } 86 IsFastMode()87 inline bool IsFastMode() const 88 { 89 return IsFastModeField::Get(metaData_); 90 } 91 SetFastMode(bool flag)92 inline void SetFastMode(bool flag) 93 { 94 IsFastModeField::Set(flag, &metaData_); 95 } 96 SetFoundDict(bool flag)97 inline void SetFoundDict(bool flag) 98 { 99 IsFoundDictField::Set(flag, &metaData_); 100 } 101 IsFoundDict()102 inline bool IsFoundDict() const 103 { 104 return IsFoundDictField::Get(metaData_); 105 } 106 IsElement()107 inline bool IsElement() const 108 { 109 return key_.IsEmpty(); 110 } 111 CheckValidIndexOrKeyIsLength()112 inline bool CheckValidIndexOrKeyIsLength() const 113 { 114 // if property key is element, check element index < strLength 115 if (key_.IsEmpty()) { 116 uint32_t len = EcmaStringAccessor(holder_->GetTaggedObject()).GetLength(); 117 return elementIndex_ < len; 118 } 119 // if property key is string, check key is 'length' of str 120 if (key_->IsString() && receiver_->IsString()) { 121 JSTaggedValue lenKey = thread_->GlobalConstants()->GetLengthString(); 122 EcmaString *proKey = EcmaString::Cast(key_->GetTaggedObject()); 123 return EcmaStringAccessor::StringsAreEqual(proKey, EcmaString::Cast(lenKey.GetTaggedObject())); 124 } 125 return false; 126 } 127 IsOnPrototype()128 inline bool IsOnPrototype() const 129 { 130 return IsOnPrototypeField::Get(metaData_); 131 } 132 SetIsOnPrototype(bool flag)133 inline void SetIsOnPrototype(bool flag) 134 { 135 IsOnPrototypeField::Set(flag, &metaData_); 136 } 137 HasReceiver()138 inline bool HasReceiver() const 139 { 140 return HasReceiverField::Get(metaData_); 141 } 142 SetHasReceiver(bool flag)143 inline void SetHasReceiver(bool flag) 144 { 145 HasReceiverField::Set(flag, &metaData_); 146 } 147 IsTransition()148 inline bool IsTransition() const 149 { 150 return IsTransitionField::Get(metaData_); 151 } 152 SetIsTransition(bool flag)153 inline void SetIsTransition(bool flag) 154 { 155 IsTransitionField::Set(flag, &metaData_); 156 } 157 GetAttr()158 inline PropertyAttributes GetAttr() const 159 { 160 return attributes_; 161 } 162 SetAttr(uint64_t attr)163 inline void SetAttr(uint64_t attr) 164 { 165 attributes_ = PropertyAttributes(attr); 166 } 167 SetAttr(const PropertyAttributes & attr)168 inline void SetAttr(const PropertyAttributes &attr) 169 { 170 attributes_ = attr; 171 } 172 IsPrimitiveAttr()173 inline bool IsPrimitiveAttr() const 174 { 175 return !attributes_.GetValue(); 176 } 177 IsWritable()178 inline bool IsWritable() const 179 { 180 return GetAttr().IsWritable(); 181 } 182 IsEnumerable()183 inline bool IsEnumerable() const 184 { 185 return GetAttr().IsEnumerable(); 186 } 187 IsConfigurable()188 inline bool IsConfigurable() const 189 { 190 return GetAttr().IsConfigurable(); 191 } 192 IsAccessorDescriptor()193 inline bool IsAccessorDescriptor() const 194 { 195 return GetAttr().IsAccessor(); 196 } 197 IsInlinedProps()198 inline bool IsInlinedProps() const 199 { 200 return GetAttr().IsInlinedProps(); 201 } 202 SetIsInlinedProps(bool flag)203 inline void SetIsInlinedProps(bool flag) 204 { 205 attributes_.SetIsInlinedProps(flag); 206 } 207 GetRepresentation()208 inline Representation GetRepresentation() const 209 { 210 return GetAttr().GetRepresentation(); 211 } 212 GetValue()213 inline JSTaggedValue GetValue() const 214 { 215 if (value_.IsEmpty()) { 216 return JSTaggedValue::Undefined(); 217 } 218 return value_.GetTaggedValue(); 219 } 220 221 JSHandle<JSTaggedValue> FastGetValue(); SetValue(JSTaggedValue value)222 inline void SetValue(JSTaggedValue value) 223 { 224 if (value_.IsEmpty()) { 225 value_ = JSMutableHandle<JSTaggedValue>(thread_, value); 226 } 227 value_.Update(value); 228 } 229 SetIndex(uint32_t index)230 inline void SetIndex(uint32_t index) 231 { 232 index_ = index; 233 } 234 GetIndex()235 inline uint32_t GetIndex() const 236 { 237 return index_; 238 } 239 SetElementOutOfBounds(bool val)240 inline void SetElementOutOfBounds(bool val) 241 { 242 elementsOutOfBounds_ = val; 243 } 244 GetElementOutOfBounds()245 inline bool GetElementOutOfBounds() const 246 { 247 return elementsOutOfBounds_; 248 } 249 HasHolder()250 inline bool HasHolder() const 251 { 252 return !holder_.IsEmpty(); 253 } 254 GetHolder()255 inline JSHandle<JSTaggedValue> GetHolder() const 256 { 257 return holder_; 258 } 259 GetReceiver()260 inline JSHandle<JSTaggedValue> GetReceiver() const 261 { 262 return receiver_; 263 } 264 GetKey()265 inline JSHandle<JSTaggedValue> GetKey() const 266 { 267 if (key_.IsEmpty()) { 268 return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined()); 269 } 270 return key_; 271 } 272 KeyFromStringType()273 inline bool KeyFromStringType() const 274 { 275 return keyFromStringType_; 276 } 277 GetElementIndex()278 inline uint32_t GetElementIndex() const 279 { 280 return elementIndex_; 281 } 282 GetThread()283 inline JSThread *GetThread() const 284 { 285 return thread_; 286 } 287 288 void ToPropertyDescriptor(PropertyDescriptor &desc) const; 289 SharedFieldType GetSharedFieldType() const; 290 void LookupProperty(); 291 void GlobalLookupProperty(); ReLookupPropertyInReceiver()292 inline void ReLookupPropertyInReceiver() 293 { 294 ResetState(); 295 if (IsElement()) { 296 LookupElementInlinedProps(JSHandle<JSObject>(receiver_)); 297 } else { 298 LookupPropertyInlinedProps(JSHandle<JSObject>(receiver_)); 299 } 300 } SetAsDefaultAttr()301 inline void SetAsDefaultAttr() 302 { 303 SetFound(NOT_FOUND_INDEX, JSTaggedValue::Undefined(), PropertyAttributes::GetDefaultAttributes(), false, false); 304 } 305 bool UpdateDataValue(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value, 306 bool isInternalAccessor, bool mayThrow = false); WriteDataPropertyInHolder(const PropertyDescriptor & desc)307 bool WriteDataPropertyInHolder(const PropertyDescriptor &desc) 308 { 309 JSHandle<JSObject> receiver(holder_); 310 return WriteDataProperty(receiver, desc); 311 } 312 bool WriteDataProperty(const JSHandle<JSObject> &receiver, const PropertyDescriptor &desc); 313 bool AddProperty(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value, PropertyAttributes attr); AddPropertyInHolder(const JSHandle<JSTaggedValue> & value,PropertyAttributes attr)314 inline bool AddPropertyInHolder(const JSHandle<JSTaggedValue> &value, PropertyAttributes attr) 315 { 316 JSHandle<JSObject> obj(holder_); 317 return AddProperty(obj, value, attr); 318 } 319 void DeletePropertyInHolder(); 320 static constexpr uint32_t NOT_FOUND_INDEX = std::numeric_limits<uint32_t>::max(); 321 static JSTaggedValue ToHolder(const JSHandle<JSTaggedValue> &holder); 322 void AddPropertyInternal(const JSHandle<JSTaggedValue> &value); 323 void DefineSetter(const JSHandle<JSTaggedValue> &value); 324 void DefineGetter(const JSHandle<JSTaggedValue> &value); 325 326 private: 327 using IsFastModeField = BitField<bool, 0, 1>; 328 using IsOnPrototypeField = IsFastModeField::NextFlag; // 1: on prototype 329 using HasReceiverField = IsOnPrototypeField::NextFlag; 330 using IsTransitionField = HasReceiverField::NextFlag; 331 // found dictionary obj between receriver and holder 332 using IsFoundDictField = IsTransitionField::NextFlag; 333 334 void UpdateHolder(); 335 void StartLookUp(OperatorType type); 336 void StartGlobalLookUp(OperatorType type); 337 bool TryFastHandleStringKey(const JSHandle<JSTaggedValue> &key); 338 void HandleKey(const JSHandle<JSTaggedValue> &key); 339 uint32_t ComputeElementCapacity(uint32_t oldCapacity); 340 void SetFound(uint32_t index, JSTaggedValue value, uint64_t attr, bool mode, bool transition = false); 341 void UpdateFound(uint32_t index, uint64_t attr, bool mode, bool transition); 342 void ResetState(); 343 void ResetStateForAddProperty(); LookupPropertyInHolder()344 inline void LookupPropertyInHolder() 345 { 346 JSHandle<JSObject> obj(holder_); 347 IsElement() 348 ? LookupElementInlinedProps(obj) 349 : LookupPropertyInlinedProps(obj); 350 } GlobalLookupPropertyInHolder()351 inline void GlobalLookupPropertyInHolder() 352 { 353 JSHandle<JSObject> obj(holder_); 354 LookupGlobal(obj); 355 } 356 template<bool isElement> void TryLookupInProtoChain(); 357 void LookupGlobal(const JSHandle<JSObject> &obj); 358 void LookupPropertyInlinedProps(const JSHandle<JSObject> &obj); 359 void LookupElementInlinedProps(const JSHandle<JSObject> &obj); 360 void WriteElement(const JSHandle<JSObject> &receiver, const PropertyDescriptor &desc); 361 void WriteElement(const JSHandle<JSObject> &receiver, JSHandle<JSTaggedValue> value) const; 362 void DeleteElementInHolder() const; 363 bool UpdateValueAndDetails(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value, 364 PropertyAttributes attr, bool attrChanged); 365 void TransitionForAttributeChanged(const JSHandle<JSObject> &receiver, PropertyAttributes attr); 366 bool SetTypedArrayPropByIndex(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value); 367 JSThread *thread_{nullptr}; 368 JSMutableHandle<JSTaggedValue> value_{}; 369 JSMutableHandle<JSTaggedValue> holder_{}; 370 JSMutableHandle<JSTaggedValue> receiver_{}; 371 JSHandle<JSTaggedValue> key_{}; 372 uint32_t elementIndex_{NOT_FOUND_INDEX}; 373 uint32_t index_{NOT_FOUND_INDEX}; 374 PropertyAttributes attributes_; 375 uint32_t metaData_{0}; 376 int receiverHoleEntry_{-1}; 377 bool keyFromStringType_{false}; 378 bool elementsOutOfBounds_{false}; 379 }; 380 } // namespace panda::ecmascript 381 #endif // ECMASCRIPT_OBJECT_OPERATOR_H 382