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 27namespace panda::ecmascript { 28class PropertyDescriptor; 29class JSObject; 30using SCheckMode = JSShared::SCheckMode; 31 32enum class OperatorType : uint8_t { 33 PROTOTYPE_CHAIN, 34 OWN, 35}; 36 37class ObjectOperator final { 38public: 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 82 inline bool IsFound() const 83 { 84 return index_ != NOT_FOUND_INDEX; 85 } 86 87 inline bool IsFastMode() const 88 { 89 return IsFastModeField::Get(metaData_); 90 } 91 92 inline void SetFastMode(bool flag) 93 { 94 IsFastModeField::Set(flag, &metaData_); 95 } 96 97 inline void SetFoundDict(bool flag) 98 { 99 IsFoundDictField::Set(flag, &metaData_); 100 } 101 102 inline bool IsFoundDict() 103 { 104 return IsFoundDictField::Get(metaData_); 105 } 106 107 inline bool IsElement() const 108 { 109 return key_.IsEmpty(); 110 } 111 112 inline bool GetThroughElement() const 113 { 114 uint32_t len = EcmaStringAccessor(holder_->GetTaggedObject()).GetLength(); 115 bool flag = elementIndex_ < len; 116 return key_.IsEmpty() && flag; 117 } 118 119 inline bool GetStringLength() const 120 { 121 JSTaggedValue lenKey = thread_->GlobalConstants()->GetLengthString(); 122 if (GetKey()->IsUndefined() || !GetKey()->IsString()) { 123 return false; 124 } 125 EcmaString *proKey = EcmaString::Cast(GetKey()->GetTaggedObject()); 126 return receiver_->IsString() && EcmaStringAccessor::StringsAreEqual(proKey, 127 EcmaString::Cast(lenKey.GetTaggedObject())); 128 } 129 130 inline bool IsOnPrototype() const 131 { 132 return IsOnPrototypeField::Get(metaData_); 133 } 134 135 inline void SetIsOnPrototype(bool flag) 136 { 137 IsOnPrototypeField::Set(flag, &metaData_); 138 } 139 140 inline bool HasReceiver() const 141 { 142 return HasReceiverField::Get(metaData_); 143 } 144 145 inline void SetHasReceiver(bool flag) 146 { 147 HasReceiverField::Set(flag, &metaData_); 148 } 149 150 inline bool IsTransition() const 151 { 152 return IsTransitionField::Get(metaData_); 153 } 154 155 inline void SetIsTransition(bool flag) 156 { 157 IsTransitionField::Set(flag, &metaData_); 158 } 159 160 inline bool IsTSHClass() const 161 { 162 return IsTSHClassField::Get(metaData_); 163 } 164 165 inline void SetIsTSHClass(bool flag) 166 { 167 IsTSHClassField::Set(flag, &metaData_); 168 } 169 170 inline PropertyAttributes GetAttr() const 171 { 172 return attributes_; 173 } 174 175 inline void SetAttr(uint64_t attr) 176 { 177 attributes_ = PropertyAttributes(attr); 178 } 179 180 inline void SetAttr(const PropertyAttributes &attr) 181 { 182 attributes_ = attr; 183 } 184 185 inline bool IsPrimitiveAttr() const 186 { 187 return !attributes_.GetValue(); 188 } 189 190 inline bool IsWritable() const 191 { 192 return GetAttr().IsWritable(); 193 } 194 195 inline bool IsEnumerable() const 196 { 197 return GetAttr().IsEnumerable(); 198 } 199 200 inline bool IsConfigurable() const 201 { 202 return GetAttr().IsConfigurable(); 203 } 204 205 inline bool IsAccessorDescriptor() const 206 { 207 return GetAttr().IsAccessor(); 208 } 209 210 inline bool IsInlinedProps() const 211 { 212 return GetAttr().IsInlinedProps(); 213 } 214 215 inline void SetIsInlinedProps(bool flag) 216 { 217 attributes_.SetIsInlinedProps(flag); 218 } 219 220 inline Representation GetRepresentation() const 221 { 222 return GetAttr().GetRepresentation(); 223 } 224 225 inline JSTaggedValue GetValue() const 226 { 227 if (value_.IsEmpty()) { 228 return JSTaggedValue::Undefined(); 229 } 230 return value_.GetTaggedValue(); 231 } 232 233 JSHandle<JSTaggedValue> FastGetValue(); 234 inline void SetValue(JSTaggedValue value) 235 { 236 if (value_.IsEmpty()) { 237 value_ = JSMutableHandle<JSTaggedValue>(thread_, value); 238 } 239 value_.Update(value); 240 } 241 242 inline void SetIndex(uint32_t index) 243 { 244 index_ = index; 245 } 246 247 inline uint32_t GetIndex() const 248 { 249 return index_; 250 } 251 252 inline void SetElementOutOfBounds(bool val) 253 { 254 elementsOutOfBounds_ = val; 255 } 256 257 inline bool GetElementOutOfBounds() const 258 { 259 return elementsOutOfBounds_; 260 } 261 262 inline bool HasHolder() const 263 { 264 return !holder_.IsEmpty(); 265 } 266 267 inline JSHandle<JSTaggedValue> GetHolder() const 268 { 269 return holder_; 270 } 271 272 inline JSHandle<JSTaggedValue> GetReceiver() const 273 { 274 return receiver_; 275 } 276 277 inline JSHandle<JSTaggedValue> GetKey() const 278 { 279 if (key_.IsEmpty()) { 280 return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined()); 281 } 282 return key_; 283 } 284 285 inline bool KeyFromStringType() const 286 { 287 return keyFromStringType_; 288 } 289 290 inline uint32_t GetElementIndex() const 291 { 292 return elementIndex_; 293 } 294 295 inline JSThread *GetThread() const 296 { 297 return thread_; 298 } 299 300 void ToPropertyDescriptor(PropertyDescriptor &desc) const; 301 SharedFieldType GetSharedFieldType() const; 302 void LookupProperty(); 303 void GlobalLookupProperty(); 304 inline void ReLookupPropertyInReceiver() 305 { 306 ResetState(); 307 return LookupPropertyInlinedProps(JSHandle<JSObject>(receiver_)); 308 } 309 inline void SetAsDefaultAttr() 310 { 311 SetFound(NOT_FOUND_INDEX, JSTaggedValue::Undefined(), PropertyAttributes::GetDefaultAttributes(), false, false); 312 } 313 bool UpdateDataValue(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value, 314 bool isInternalAccessor, bool mayThrow = false); 315 bool WriteDataPropertyInHolder(const PropertyDescriptor &desc) 316 { 317 JSHandle<JSObject> receiver(holder_); 318 return WriteDataProperty(receiver, desc); 319 } 320 bool WriteDataProperty(const JSHandle<JSObject> &receiver, const PropertyDescriptor &desc); 321 bool AddProperty(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value, PropertyAttributes attr); 322 inline bool AddPropertyInHolder(const JSHandle<JSTaggedValue> &value, PropertyAttributes attr) 323 { 324 JSHandle<JSObject> obj(holder_); 325 return AddProperty(obj, value, attr); 326 } 327 void DeletePropertyInHolder(); 328 static constexpr uint32_t NOT_FOUND_INDEX = std::numeric_limits<uint32_t>::max(); 329 static JSTaggedValue ToHolder(const JSHandle<JSTaggedValue> &holder); 330 void AddPropertyInternal(const JSHandle<JSTaggedValue> &value); 331 void DefineSetter(const JSHandle<JSTaggedValue> &value); 332 void DefineGetter(const JSHandle<JSTaggedValue> &value); 333 334private: 335 static constexpr uint64_t ATTR_LENGTH = 5; 336 static constexpr uint64_t INDEX_LENGTH = 32; 337 338 using IsFastModeField = BitField<bool, 0, 1>; 339 using IsOnPrototypeField = IsFastModeField::NextFlag; // 1: on prototype 340 using HasReceiverField = IsOnPrototypeField::NextFlag; 341 using IsTransitionField = HasReceiverField::NextFlag; 342 using IsTSHClassField = IsTransitionField::NextFlag; 343 // found dictionary obj between receriver and holder 344 using IsFoundDictField = IsTSHClassField::NextFlag; 345 346 void UpdateHolder(); 347 void UpdateIsTSHClass(); 348 void StartLookUp(OperatorType type); 349 void StartGlobalLookUp(OperatorType type); 350 void HandleKey(const JSHandle<JSTaggedValue> &key); 351 uint32_t ComputeElementCapacity(uint32_t oldCapacity); 352 void SetFound(uint32_t index, JSTaggedValue value, uint64_t attr, bool mode, bool transition = false); 353 void UpdateFound(uint32_t index, uint64_t attr, bool mode, bool transition); 354 void ResetState(); 355 void ResetStateForAddProperty(); 356 inline void LookupPropertyInHolder() 357 { 358 JSHandle<JSObject> obj(holder_); 359 LookupPropertyInlinedProps(obj); 360 } 361 inline void GlobalLookupPropertyInHolder() 362 { 363 JSHandle<JSObject> obj(holder_); 364 LookupGlobal(obj); 365 } 366 void LookupGlobal(const JSHandle<JSObject> &obj); 367 void LookupPropertyInlinedProps(const JSHandle<JSObject> &obj); 368 void LookupElementInlinedProps(const JSHandle<JSObject> &obj); 369 void WriteElement(const JSHandle<JSObject> &receiver, const PropertyDescriptor &desc); 370 void WriteElement(const JSHandle<JSObject> &receiver, JSHandle<JSTaggedValue> value) const; 371 void DeleteElementInHolder() const; 372 bool UpdateValueAndDetails(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value, 373 PropertyAttributes attr, bool attrChanged); 374 void TransitionForAttributeChanged(const JSHandle<JSObject> &receiver, PropertyAttributes attr); 375 JSThread *thread_{nullptr}; 376 JSMutableHandle<JSTaggedValue> value_{}; 377 JSMutableHandle<JSTaggedValue> holder_{}; 378 JSMutableHandle<JSTaggedValue> receiver_{}; 379 JSHandle<JSTaggedValue> key_{}; 380 uint32_t elementIndex_{NOT_FOUND_INDEX}; 381 uint32_t index_{NOT_FOUND_INDEX}; 382 PropertyAttributes attributes_; 383 uint32_t metaData_{0}; 384 int receiverHoleEntry_{-1}; 385 bool keyFromStringType_{false}; 386 bool elementsOutOfBounds_{false}; 387}; 388} // namespace panda::ecmascript 389#endif // ECMASCRIPT_OBJECT_OPERATOR_H 390