mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-11-27 12:10:47 +00:00
a3e553891a
Issue: https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/IAJ0YZ?from=project-issue Signed-off-by: 刘智杰 <liuzhijie9@huawei.com> Change-Id: I6118689f0c766c5adfd855981adec895ad879292
1112 lines
43 KiB
C++
1112 lines
43 KiB
C++
/*
|
|
* 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.
|
|
*/
|
|
|
|
#include "ecmascript/object_operator.h"
|
|
|
|
#include "ecmascript/global_dictionary-inl.h"
|
|
#include "ecmascript/js_primitive_ref.h"
|
|
#include "ecmascript/object_fast_operator-inl.h"
|
|
#include "ecmascript/property_detector-inl.h"
|
|
|
|
namespace panda::ecmascript {
|
|
void ObjectOperator::HandleKey(const JSHandle<JSTaggedValue> &key)
|
|
{
|
|
if (key->IsInt()) {
|
|
int32_t keyInt = key->GetInt();
|
|
if (keyInt >= 0) {
|
|
elementIndex_ = static_cast<uint32_t>(keyInt);
|
|
return;
|
|
}
|
|
key_ = JSHandle<JSTaggedValue>::Cast(base::NumberHelper::NumberToString(thread_, JSTaggedValue(keyInt)));
|
|
return;
|
|
}
|
|
|
|
if (key->IsString()) {
|
|
keyFromStringType_ = true;
|
|
uint32_t index = 0;
|
|
if (JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index)) {
|
|
ASSERT(index < JSObject::MAX_ELEMENT_INDEX);
|
|
elementIndex_ = index;
|
|
return;
|
|
}
|
|
if (EcmaStringAccessor(key->GetTaggedObject()).IsInternString()) {
|
|
key_ = key;
|
|
return;
|
|
}
|
|
key_ = JSHandle<JSTaggedValue>(thread_, thread_->GetEcmaVM()->GetFactory()->InternString(key));
|
|
return;
|
|
}
|
|
|
|
if (key->IsDouble()) {
|
|
double number = key->GetDouble();
|
|
if (number >= 0 && number < JSObject::MAX_ELEMENT_INDEX) {
|
|
auto integer = static_cast<uint32_t>(number);
|
|
if (integer == number) {
|
|
elementIndex_ = static_cast<uint32_t>(number);
|
|
return;
|
|
}
|
|
}
|
|
key_ = JSHandle<JSTaggedValue>::Cast(base::NumberHelper::NumberToString(thread_, key.GetTaggedValue()));
|
|
if (!EcmaStringAccessor(key_.GetTaggedValue()).IsInternString()) {
|
|
key_ = JSHandle<JSTaggedValue>(thread_, thread_->GetEcmaVM()->GetFactory()->InternString(key_));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (key->IsSymbol()) {
|
|
key_ = key;
|
|
return;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::ToPrimitive(thread_, key, PREFER_STRING));
|
|
RETURN_IF_ABRUPT_COMPLETION(thread_);
|
|
if (key->IsSymbol()) {
|
|
key_ = keyHandle;
|
|
return;
|
|
}
|
|
key_ = JSHandle<JSTaggedValue>(thread_,
|
|
thread_->GetEcmaVM()->GetFactory()->InternString(
|
|
JSHandle<JSTaggedValue>::Cast(JSTaggedValue::ToString(thread_, keyHandle))));
|
|
}
|
|
|
|
void ObjectOperator::UpdateHolder()
|
|
{
|
|
if (holder_->IsString() && (GetThroughElement() || GetStringLength())) {
|
|
JSHandle<JSTaggedValue> undefined = thread_->GlobalConstants()->GetHandledUndefined();
|
|
holder_.Update(JSPrimitiveRef::StringCreate(thread_, holder_, undefined).GetTaggedValue());
|
|
} else {
|
|
if (holder_->IsString() || holder_->IsNumber()) {
|
|
SetIsOnPrototype(true);
|
|
}
|
|
holder_.Update(JSTaggedValue::ToPrototypeOrObj(thread_, holder_).GetTaggedValue());
|
|
}
|
|
}
|
|
|
|
void ObjectOperator::UpdateIsTSHClass()
|
|
{
|
|
if (!holder_->IsECMAObject()) {
|
|
SetIsTSHClass(false);
|
|
return;
|
|
}
|
|
auto hclass = JSHandle<JSObject>::Cast(holder_)->GetClass();
|
|
if (hclass->IsTS()) {
|
|
SetIsTSHClass(true);
|
|
}
|
|
}
|
|
|
|
void ObjectOperator::StartLookUp(OperatorType type)
|
|
{
|
|
UpdateHolder();
|
|
|
|
if (type == OperatorType::OWN) {
|
|
LookupPropertyInHolder();
|
|
} else {
|
|
LookupProperty();
|
|
}
|
|
}
|
|
|
|
void ObjectOperator::StartGlobalLookUp(OperatorType type)
|
|
{
|
|
UpdateHolder();
|
|
|
|
if (type == OperatorType::OWN) {
|
|
GlobalLookupPropertyInHolder();
|
|
} else {
|
|
GlobalLookupProperty();
|
|
}
|
|
}
|
|
|
|
ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &key, OperatorType type)
|
|
: thread_(thread),
|
|
holder_(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject()),
|
|
receiver_(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject())
|
|
{
|
|
HandleKey(key);
|
|
StartGlobalLookUp(type);
|
|
}
|
|
|
|
ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSObject> &holder, const JSHandle<JSTaggedValue> &key,
|
|
OperatorType type)
|
|
: thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, holder.GetTaggedValue())
|
|
{
|
|
HandleKey(key);
|
|
StartLookUp(type);
|
|
}
|
|
|
|
ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder,
|
|
const JSHandle<JSTaggedValue> &key, OperatorType type)
|
|
: thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, holder.GetTaggedValue())
|
|
{
|
|
HandleKey(key);
|
|
StartLookUp(type);
|
|
}
|
|
|
|
ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder, uint32_t index,
|
|
OperatorType type)
|
|
: thread_(thread),
|
|
holder_(thread, holder.GetTaggedValue()),
|
|
receiver_(thread, holder.GetTaggedValue()),
|
|
elementIndex_(index)
|
|
{
|
|
StartLookUp(type);
|
|
}
|
|
|
|
ObjectOperator::ObjectOperator(JSThread *thread, const JSHandle<JSTaggedValue> &holder,
|
|
const JSHandle<JSTaggedValue> &receiver, const JSHandle<JSTaggedValue> &key,
|
|
OperatorType type)
|
|
: thread_(thread), holder_(thread, holder.GetTaggedValue()), receiver_(thread, receiver.GetTaggedValue())
|
|
{
|
|
SetHasReceiver(true);
|
|
HandleKey(key);
|
|
StartLookUp(type);
|
|
}
|
|
|
|
// op for fast path
|
|
ObjectOperator::ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
|
|
OperatorType type)
|
|
: thread_(thread), holder_(thread, receiver), receiver_(thread, receiver), key_(thread, name)
|
|
{
|
|
ASSERT(name.IsStringOrSymbol());
|
|
StartLookUp(type);
|
|
}
|
|
JSHandle<JSTaggedValue> ObjectOperator::FastGetValue()
|
|
{
|
|
ASSERT(IsFound() && !value_.IsEmpty());
|
|
if (value_->IsPropertyBox()) {
|
|
value_.Update(PropertyBox::Cast(value_->GetTaggedObject())->GetValue());
|
|
}
|
|
if (!IsAccessorDescriptor()) {
|
|
return value_;
|
|
}
|
|
AccessorData *accessor = AccessorData::Cast(value_->GetTaggedObject());
|
|
ASSERT(!accessor->IsInternal());
|
|
// 8. Return Call(getter, Receiver).
|
|
return JSHandle<JSTaggedValue>(thread_, JSObject::CallGetter(thread_, accessor, receiver_));
|
|
}
|
|
ObjectOperator::ObjectOperator(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
|
|
const PropertyAttributes &attr)
|
|
: thread_(thread), receiver_(thread, receiver), key_(thread, name)
|
|
{
|
|
SetAttr(attr);
|
|
}
|
|
void ObjectOperator::FastAdd(JSThread *thread, const JSTaggedValue &receiver, const JSTaggedValue &name,
|
|
const JSHandle<JSTaggedValue> &value, const PropertyAttributes &attr)
|
|
{
|
|
ObjectOperator op(thread, receiver, name, attr);
|
|
op.AddPropertyInternal(value);
|
|
}
|
|
|
|
// static
|
|
void ObjectOperator::UpdateDetectorOnSetPrototype(const JSThread *thread, JSTaggedValue receiver)
|
|
{
|
|
// skip env prepare
|
|
if (!thread->IsReadyToUpdateDetector()) {
|
|
return;
|
|
}
|
|
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
|
|
JSHClass *hclass = receiver.GetTaggedObject()->GetClass();
|
|
JSType type = hclass->GetObjectType();
|
|
switch (type) {
|
|
case JSType::JS_REG_EXP: {
|
|
if (PropertyDetector::IsRegExpReplaceDetectorValid(env)) {
|
|
PropertyDetector::InvalidateRegExpReplaceDetector(env);
|
|
}
|
|
if (PropertyDetector::IsRegExpFlagsDetectorValid(env)) {
|
|
PropertyDetector::InvalidateRegExpFlagsDetector(env);
|
|
}
|
|
return;
|
|
}
|
|
case JSType::JS_MAP: {
|
|
if (PropertyDetector::IsMapIteratorDetectorValid(env)) {
|
|
PropertyDetector::InvalidateMapIteratorDetector(env);
|
|
}
|
|
return;
|
|
}
|
|
case JSType::JS_SET: {
|
|
if (PropertyDetector::IsSetIteratorDetectorValid(env)) {
|
|
PropertyDetector::InvalidateSetIteratorDetector(env);
|
|
}
|
|
return;
|
|
}
|
|
case JSType::JS_PRIMITIVE_REF: {
|
|
if (JSPrimitiveRef::Cast(receiver.GetTaggedObject())->IsString() &&
|
|
PropertyDetector::IsStringIteratorDetectorValid(env)) {
|
|
PropertyDetector::InvalidateStringIteratorDetector(env);
|
|
}
|
|
return;
|
|
}
|
|
case JSType::JS_ARRAY: {
|
|
if (PropertyDetector::IsArrayIteratorDetectorValid(env)) {
|
|
PropertyDetector::InvalidateArrayIteratorDetector(env);
|
|
}
|
|
return;
|
|
}
|
|
case JSType::JS_INT8_ARRAY:
|
|
case JSType::JS_UINT8_ARRAY:
|
|
case JSType::JS_UINT8_CLAMPED_ARRAY:
|
|
case JSType::JS_INT16_ARRAY:
|
|
case JSType::JS_UINT16_ARRAY:
|
|
case JSType::JS_INT32_ARRAY:
|
|
case JSType::JS_UINT32_ARRAY:
|
|
case JSType::JS_FLOAT32_ARRAY:
|
|
case JSType::JS_FLOAT64_ARRAY:
|
|
case JSType::JS_BIGINT64_ARRAY:
|
|
case JSType::JS_BIGUINT64_ARRAY: {
|
|
if (PropertyDetector::IsTypedArrayIteratorDetectorValid(env)) {
|
|
PropertyDetector::InvalidateTypedArrayIteratorDetector(env);
|
|
}
|
|
if (PropertyDetector::IsTypedArraySpeciesProtectDetectorValid(env)) {
|
|
PropertyDetector::InvalidateTypedArraySpeciesProtectDetector(env);
|
|
}
|
|
return;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (hclass->IsPrototype() &&
|
|
(receiver == env->GetTaggedInt8ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedUint8ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedUint8ClampedArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedInt16ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedUint16ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedInt32ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedUint32ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedFloat32ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedFloat64ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedBigInt64ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedBigUint64ArrayFunctionPrototype()) &&
|
|
PropertyDetector::IsTypedArrayIteratorDetectorValid(env)) {
|
|
PropertyDetector::InvalidateTypedArrayIteratorDetector(env);
|
|
return;
|
|
}
|
|
if ((PropertyDetector::IsNumberStringNotRegexpLikeDetectorValid(env) &&
|
|
JSObject::Cast(receiver)->GetJSHClass()->IsPrototype() && receiver.IsJSPrimitive())) {
|
|
PropertyDetector::InvalidateNumberStringNotRegexpLikeDetector(env);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ObjectOperator::UpdateDetector()
|
|
{
|
|
if (IsElement()) {
|
|
return;
|
|
}
|
|
ObjectOperator::UpdateDetector(thread_, holder_.GetTaggedValue(), key_.GetTaggedValue());
|
|
}
|
|
|
|
// static
|
|
void ObjectOperator::UpdateDetector(const JSThread *thread, JSTaggedValue receiver, JSTaggedValue key)
|
|
{
|
|
// skip env prepare
|
|
if (!thread->IsReadyToUpdateDetector()) {
|
|
return;
|
|
}
|
|
bool maybeDetector = IsDetectorName(thread, key);
|
|
if (!maybeDetector) {
|
|
return;
|
|
}
|
|
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
|
|
auto globalConst = thread->GlobalConstants();
|
|
if (key == env->GetTaggedReplaceSymbol() &&
|
|
(receiver.IsJSRegExp() || receiver == env->GetTaggedRegExpPrototype())) {
|
|
if (!PropertyDetector::IsRegExpReplaceDetectorValid(env)) {
|
|
return;
|
|
}
|
|
PropertyDetector::InvalidateRegExpReplaceDetector(env);
|
|
} else if (key == env->GetTaggedIteratorSymbol()) {
|
|
if (receiver.IsJSMap() || receiver == env->GetTaggedMapPrototype()) {
|
|
if (!PropertyDetector::IsMapIteratorDetectorValid(env)) {
|
|
return;
|
|
}
|
|
PropertyDetector::InvalidateMapIteratorDetector(env);
|
|
} else if (receiver.IsJSSet() || receiver == env->GetTaggedSetPrototype()) {
|
|
if (!PropertyDetector::IsSetIteratorDetectorValid(env)) {
|
|
return;
|
|
}
|
|
PropertyDetector::InvalidateSetIteratorDetector(env);
|
|
} else if ((receiver.IsJSPrimitiveRef() && JSPrimitiveRef::Cast(receiver.GetTaggedObject())->IsString()) ||
|
|
receiver == env->GetTaggedStringPrototype()) {
|
|
if (!PropertyDetector::IsStringIteratorDetectorValid(env)) {
|
|
return;
|
|
}
|
|
PropertyDetector::InvalidateStringIteratorDetector(env);
|
|
} else if (receiver.IsJSArray() || receiver == env->GetTaggedArrayPrototype()) {
|
|
if (!PropertyDetector::IsArrayIteratorDetectorValid(env)) {
|
|
return;
|
|
}
|
|
PropertyDetector::InvalidateArrayIteratorDetector(env);
|
|
} else if (receiver.IsTypedArray() ||
|
|
receiver == env->GetTaggedArrayPrototype() ||
|
|
receiver == env->GetTaggedInt8ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedUint8ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedUint8ClampedArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedInt16ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedUint16ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedInt32ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedUint32ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedFloat32ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedFloat64ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedBigInt64ArrayFunctionPrototype() ||
|
|
receiver == env->GetTaggedBigUint64ArrayFunctionPrototype()) {
|
|
if (!PropertyDetector::IsTypedArrayIteratorDetectorValid(env)) {
|
|
return;
|
|
}
|
|
PropertyDetector::InvalidateTypedArrayIteratorDetector(env);
|
|
}
|
|
} else if (key == env->GetTaggedSpeciesSymbol()) {
|
|
if (receiver == env->GetTypedArrayFunction().GetTaggedValue()) {
|
|
if (!PropertyDetector::IsTypedArraySpeciesProtectDetectorValid(env)) {
|
|
return;
|
|
}
|
|
PropertyDetector::InvalidateTypedArraySpeciesProtectDetector(env);
|
|
}
|
|
if (receiver == env->GetRegExpFunction().GetTaggedValue()) {
|
|
if (!PropertyDetector::IsRegExpSpeciesDetectorValid(env)) {
|
|
return;
|
|
}
|
|
PropertyDetector::InvalidateRegExpSpeciesDetector(env);
|
|
}
|
|
} else if ((key == env->GetTaggedReplaceSymbol()) ||
|
|
(key == env->GetTaggedSplitSymbol()) ||
|
|
(key == env->GetTaggedMatchAllSymbol())) {
|
|
if (!PropertyDetector::IsNumberStringNotRegexpLikeDetectorValid(env)) {
|
|
return;
|
|
}
|
|
// check String.prototype or Number.prototype or Object.prototype
|
|
if ((JSObject::Cast(receiver)->GetJSHClass()->IsPrototype() &&
|
|
(receiver.IsJSPrimitive() || receiver == env->GetTaggedObjectFunctionPrototype()))) {
|
|
PropertyDetector::InvalidateNumberStringNotRegexpLikeDetector(env);
|
|
}
|
|
} else if (key == globalConst->GetHandledFlagsString().GetTaggedValue() &&
|
|
(receiver.IsJSRegExp() || receiver == env->GetTaggedRegExpPrototype())) {
|
|
if (!PropertyDetector::IsRegExpFlagsDetectorValid(env)) {
|
|
return;
|
|
}
|
|
PropertyDetector::InvalidateRegExpFlagsDetector(env);
|
|
}
|
|
}
|
|
|
|
// static
|
|
bool ObjectOperator::IsDetectorName(const JSThread *thread, JSTaggedValue key)
|
|
{
|
|
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
|
|
uintptr_t start = GlobalEnv::GetFirstDetectorSymbolAddr(*env);
|
|
uintptr_t end = GlobalEnv::GetLastDetectorSymbolAddr(*env);
|
|
uintptr_t addr = key.GetRawData();
|
|
if ((start <= addr) && (addr <= end)) {
|
|
return true;
|
|
}
|
|
if (key == thread->GlobalConstants()->GetHandledFlagsString().GetTaggedValue()) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
SharedFieldType ObjectOperator::GetSharedFieldType() const
|
|
{
|
|
return JSObject::Cast(holder_->GetTaggedObject())->GetJSHClass()->IsDictionaryMode()
|
|
? attributes_.GetDictSharedFieldType()
|
|
: attributes_.GetSharedFieldType();
|
|
}
|
|
|
|
void ObjectOperator::ToPropertyDescriptor(PropertyDescriptor &desc) const
|
|
{
|
|
DISALLOW_GARBAGE_COLLECTION;
|
|
if (!IsFound()) {
|
|
return;
|
|
}
|
|
|
|
if (!IsAccessorDescriptor()) {
|
|
desc.SetWritable(IsWritable());
|
|
JSTaggedValue val = GetValue();
|
|
desc.SetValue(JSHandle<JSTaggedValue>(thread_, val));
|
|
desc.SetSharedFieldType(GetSharedFieldType());
|
|
} else {
|
|
auto result = GetValue();
|
|
bool isPropertyBox = result.IsPropertyBox();
|
|
if (isPropertyBox) {
|
|
result = PropertyBox::Cast(result.GetTaggedObject())->GetValue();
|
|
}
|
|
AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject());
|
|
|
|
if (UNLIKELY(accessor->IsInternal())) {
|
|
desc.SetWritable(IsWritable());
|
|
auto val = accessor->CallInternalGet(thread_, JSHandle<JSObject>::Cast(GetHolder()));
|
|
JSMutableHandle<JSTaggedValue> value(thread_, val);
|
|
if (isPropertyBox) {
|
|
JSHandle<PropertyBox> cell(value_);
|
|
cell->SetValue(thread_, val);
|
|
value.Update(cell);
|
|
}
|
|
desc.SetValue(value);
|
|
} else {
|
|
desc.SetGetter(JSHandle<JSTaggedValue>(thread_, accessor->GetGetter()));
|
|
desc.SetSetter(JSHandle<JSTaggedValue>(thread_, accessor->GetSetter()));
|
|
}
|
|
}
|
|
|
|
desc.SetEnumerable(IsEnumerable());
|
|
desc.SetConfigurable(IsConfigurable());
|
|
}
|
|
|
|
void ObjectOperator::GlobalLookupProperty()
|
|
{
|
|
GlobalLookupPropertyInHolder();
|
|
if (IsFound()) {
|
|
return;
|
|
}
|
|
JSTaggedValue proto = JSTaggedValue::GetPrototype(thread_, holder_);
|
|
RETURN_IF_ABRUPT_COMPLETION(thread_);
|
|
if (!proto.IsHeapObject()) {
|
|
return;
|
|
}
|
|
holder_.Update(proto);
|
|
if (holder_->IsJSProxy()) {
|
|
return;
|
|
}
|
|
SetIsOnPrototype(true);
|
|
LookupProperty();
|
|
}
|
|
|
|
void ObjectOperator::LookupProperty()
|
|
{
|
|
while (true) {
|
|
if (holder_->IsJSProxy()) {
|
|
return;
|
|
}
|
|
UpdateIsTSHClass();
|
|
LookupPropertyInHolder();
|
|
if (IsFound()) {
|
|
return;
|
|
}
|
|
|
|
JSTaggedValue proto = JSTaggedValue::GetPrototype(thread_, holder_);
|
|
RETURN_IF_ABRUPT_COMPLETION(thread_);
|
|
if (!proto.IsHeapObject()) {
|
|
return;
|
|
}
|
|
|
|
holder_.Update(proto);
|
|
SetIsOnPrototype(true);
|
|
}
|
|
}
|
|
|
|
void ObjectOperator::LookupGlobal(const JSHandle<JSObject> &obj)
|
|
{
|
|
ASSERT(obj->IsJSGlobalObject());
|
|
if (IsElement()) {
|
|
LookupElementInlinedProps(obj);
|
|
return;
|
|
}
|
|
TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
|
|
if (array->GetLength() == 0) {
|
|
return;
|
|
}
|
|
GlobalDictionary *dict = GlobalDictionary::Cast(array);
|
|
int entry = dict->FindEntry(key_.GetTaggedValue());
|
|
if (entry == -1) {
|
|
return;
|
|
}
|
|
JSTaggedValue value(dict->GetBox(entry));
|
|
auto attr = dict->GetAttributes(entry).GetValue();
|
|
SetFound(entry, value, attr, true);
|
|
}
|
|
|
|
void ObjectOperator::LookupPropertyInlinedProps(const JSHandle<JSObject> &obj)
|
|
{
|
|
if (IsElement()) {
|
|
LookupElementInlinedProps(obj);
|
|
return;
|
|
}
|
|
|
|
if (!obj.GetTaggedValue().IsJSObject()) {
|
|
return;
|
|
}
|
|
|
|
if (obj->IsJSGlobalObject()) {
|
|
DISALLOW_GARBAGE_COLLECTION;
|
|
TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
|
|
if (array->GetLength() == 0) {
|
|
return;
|
|
}
|
|
|
|
GlobalDictionary *dict = GlobalDictionary::Cast(array);
|
|
int entry = dict->FindEntry(key_.GetTaggedValue());
|
|
if (entry == -1) {
|
|
return;
|
|
}
|
|
|
|
JSTaggedValue value(dict->GetBox(entry));
|
|
auto attr = dict->GetAttributes(entry).GetValue();
|
|
SetFound(entry, value, attr, !IsFoundDict());
|
|
return;
|
|
}
|
|
|
|
TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
|
|
if (!array->IsDictionaryMode()) {
|
|
JSHClass *jshclass = obj->GetJSHClass();
|
|
int entry = JSHClass::FindPropertyEntry(thread_, jshclass, key_.GetTaggedValue());
|
|
if (entry == -1) {
|
|
return;
|
|
}
|
|
JSTaggedValue attrs = jshclass->GetLayout();
|
|
LayoutInfo *layoutInfo = LayoutInfo::Cast(attrs.GetTaggedObject());
|
|
PropertyAttributes attr(layoutInfo->GetAttr(entry));
|
|
ASSERT(entry == static_cast<int>(attr.GetOffset()));
|
|
JSTaggedValue value;
|
|
if (attr.IsInlinedProps()) {
|
|
value = obj->GetPropertyInlinedPropsWithRep(entry, attr);
|
|
if (value.IsHole()) {
|
|
if (receiverHoleEntry_ == -1 && receiver_ == holder_) {
|
|
receiverHoleEntry_ = entry;
|
|
}
|
|
return;
|
|
}
|
|
} else {
|
|
entry -= static_cast<int>(jshclass->GetInlinedProperties());
|
|
value = array->Get(entry);
|
|
}
|
|
|
|
SetFound(entry, value, attr.GetValue(), !IsFoundDict());
|
|
return;
|
|
}
|
|
SetFoundDict(true);
|
|
NameDictionary *dict = NameDictionary::Cast(array);
|
|
int entry = dict->FindEntry(key_.GetTaggedValue());
|
|
if (entry == -1) {
|
|
return;
|
|
}
|
|
|
|
JSTaggedValue value = dict->GetValue(entry);
|
|
auto attr = dict->GetAttributes(entry).GetValue();
|
|
SetFound(entry, value, attr, false);
|
|
}
|
|
|
|
void ObjectOperator::TransitionForAttributeChanged(const JSHandle<JSObject> &receiver, PropertyAttributes attr)
|
|
{
|
|
if (IsElement()) {
|
|
uint32_t index = GetIndex();
|
|
if (!receiver->GetJSHClass()->IsDictionaryElement()) {
|
|
JSObject::ElementsToDictionary(thread_, receiver);
|
|
RETURN_IF_ABRUPT_COMPLETION(thread_);
|
|
auto dict = NumberDictionary::Cast(receiver->GetElements().GetTaggedObject());
|
|
index = static_cast<uint32_t>(dict->FindEntry(JSTaggedValue(index)));
|
|
PropertyAttributes origin = dict->GetAttributes(index);
|
|
attr.SetDictionaryOrder(origin.GetDictionaryOrder());
|
|
dict->SetAttributes(thread_, index, attr);
|
|
} else {
|
|
auto dict = NumberDictionary::Cast(receiver->GetElements().GetTaggedObject());
|
|
dict->SetAttributes(thread_, index, attr);
|
|
}
|
|
// update found result
|
|
UpdateFound(index, attr.GetValue(), false, true);
|
|
} else if (receiver->IsJSGlobalObject()) {
|
|
uint32_t index = GetIndex();
|
|
JSHandle<GlobalDictionary> dictHandle(thread_, receiver->GetProperties());
|
|
dictHandle->SetAttributes(thread_, index, attr);
|
|
GlobalDictionary::InvalidatePropertyBox(thread_, dictHandle, index);
|
|
} else {
|
|
uint32_t index = GetIndex();
|
|
if (!receiver->GetJSHClass()->IsDictionaryMode()) {
|
|
JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread_, receiver));
|
|
RETURN_IF_ABRUPT_COMPLETION(thread_);
|
|
index = static_cast<uint32_t>(dict->FindEntry(key_.GetTaggedValue()));
|
|
PropertyAttributes origin = dict->GetAttributes(index);
|
|
attr.SetDictSharedFieldType(attr.GetSharedFieldType());
|
|
attr.SetDictionaryOrder(origin.GetDictionaryOrder());
|
|
dict->SetAttributes(thread_, index, attr);
|
|
} else {
|
|
auto dict = NameDictionary::Cast(receiver->GetProperties().GetTaggedObject());
|
|
dict->SetAttributes(thread_, index, attr);
|
|
}
|
|
// update found result
|
|
UpdateFound(index, attr.GetValue(), false, true);
|
|
}
|
|
}
|
|
|
|
bool ObjectOperator::UpdateValueAndDetails(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
|
|
PropertyAttributes attr, bool attrChanged)
|
|
{
|
|
auto valueAccessor = GetValue();
|
|
if (valueAccessor.IsPropertyBox()) {
|
|
valueAccessor = PropertyBox::Cast(valueAccessor.GetTaggedObject())->GetValue();
|
|
}
|
|
bool isInternalAccessor = IsAccessorDescriptor()
|
|
&& AccessorData::Cast(valueAccessor.GetTaggedObject())->IsInternal();
|
|
if (!attrChanged) {
|
|
return UpdateDataValue(receiver, value, isInternalAccessor);
|
|
}
|
|
if (attr.IsWritable()) {
|
|
TransitionForAttributeChanged(receiver, attr);
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
|
|
return UpdateDataValue(receiver, value, isInternalAccessor);
|
|
}
|
|
bool res = UpdateDataValue(receiver, value, isInternalAccessor);
|
|
if (res) {
|
|
TransitionForAttributeChanged(receiver, attr);
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool ObjectOperator::UpdateDataValue(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
|
|
bool isInternalAccessor, bool mayThrow)
|
|
{
|
|
if (IsElement()) {
|
|
TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
|
|
if (!elements->IsDictionaryMode()) {
|
|
if (receiver.GetTaggedValue().IsJSCOWArray()) {
|
|
JSArray::CheckAndCopyArray(thread_, JSHandle<JSArray>(receiver));
|
|
} else if (receiver->IsTypedArray()) {
|
|
JSTaggedValue holder = receiver.GetTaggedValue();
|
|
JSType jsType = holder.GetTaggedObject()->GetClass()->GetObjectType();
|
|
JSTaggedValue typedArrayProperty = JSTypedArray::FastSetPropertyByIndex(thread_,
|
|
receiver.GetTaggedValue(), GetIndex(), value.GetTaggedValue(), jsType);
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
|
|
if (typedArrayProperty.IsHole()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
ElementsKind oldKind = receiver->GetClass()->GetElementsKind();
|
|
if (JSHClass::TransitToElementsKind(thread_, receiver, value)) {
|
|
SetIsTransition(true);
|
|
ElementsKind newKind = receiver->GetClass()->GetElementsKind();
|
|
// newKind != currentKind, we need to convert the whole array to the newKind.
|
|
Elements::MigrateArrayWithKind(thread_, receiver, oldKind, newKind);
|
|
}
|
|
ElementAccessor::Set(thread_, receiver, GetIndex(), value, false);
|
|
return true;
|
|
}
|
|
|
|
NumberDictionary *dict = NumberDictionary::Cast(elements);
|
|
dict->UpdateValue(thread_, GetIndex(), value.GetTaggedValue());
|
|
return true;
|
|
}
|
|
|
|
if (receiver->IsJSGlobalObject()) {
|
|
// need update cell type ?
|
|
auto *dict = GlobalDictionary::Cast(receiver->GetProperties().GetTaggedObject());
|
|
if (isInternalAccessor && !value->IsAccessor()) {
|
|
PropertyAttributes attr = dict->GetAttributes(GetIndex());
|
|
attr.SetIsAccessor(false);
|
|
dict->SetAttributes(thread_, GetIndex(), attr);
|
|
}
|
|
PropertyBox *cell = dict->GetBox(GetIndex());
|
|
cell->SetValue(thread_, value.GetTaggedValue());
|
|
return true;
|
|
}
|
|
|
|
if (isInternalAccessor) {
|
|
auto accessor = AccessorData::Cast(GetValue().GetTaggedObject());
|
|
if (accessor->HasSetter()) {
|
|
bool res = accessor->CallInternalSet(thread_, JSHandle<JSObject>(receiver), value, mayThrow);
|
|
if (receiver->GetJSHClass()->IsDictionaryMode()) {
|
|
SetIsInlinedProps(false);
|
|
SetFastMode(false);
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
|
|
JSMutableHandle<TaggedArray> properties(thread_, TaggedArray::Cast(receiver->GetProperties().GetTaggedObject()));
|
|
if (!properties->IsDictionaryMode()) {
|
|
PropertyAttributes attr = GetAttr();
|
|
uint32_t offset = index_;
|
|
if (!attr.IsInlinedProps()) {
|
|
auto *hclass = receiver_->GetTaggedObject()->GetClass();
|
|
offset += hclass->GetInlinedProperties();
|
|
}
|
|
attr.SetOffset(offset);
|
|
JSHandle<JSObject> objHandle(receiver_);
|
|
ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
|
|
auto actualValue =
|
|
JSHClass::ConvertOrTransitionWithRep(thread_, objHandle, key_, value, attr);
|
|
JSObject::TryMigrateToGenericKindForJSObject(thread_, objHandle, oldKind);
|
|
if (actualValue.isTransition) {
|
|
SetIsTransition(true);
|
|
}
|
|
attributes_.SetRepresentation(attr.GetRepresentation());
|
|
|
|
if (attr.IsInlinedProps()) {
|
|
receiver->SetPropertyInlinedPropsWithRep(thread_, GetIndex(), actualValue.value);
|
|
} else {
|
|
if (receiver.GetTaggedValue().IsJSCOWArray()) {
|
|
JSArray::CheckAndCopyArray(thread_, JSHandle<JSArray>(receiver));
|
|
properties.Update(JSHandle<JSArray>(receiver)->GetProperties());
|
|
}
|
|
if (actualValue.isTagged) {
|
|
properties->Set<true>(thread_, GetIndex(), value.GetTaggedValue());
|
|
} else {
|
|
properties->Set<false>(thread_, GetIndex(), actualValue.value);
|
|
}
|
|
}
|
|
} else {
|
|
properties.GetObject<NameDictionary>()->UpdateValue(thread_, GetIndex(), value.GetTaggedValue());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ObjectOperator::WriteDataProperty(const JSHandle<JSObject> &receiver, const PropertyDescriptor &desc)
|
|
{
|
|
PropertyAttributes attr = GetAttr();
|
|
bool attrChanged = false;
|
|
|
|
// composed new attribute from desc
|
|
if (desc.HasConfigurable() && attr.IsConfigurable() != desc.IsConfigurable()) {
|
|
attr.SetConfigurable(desc.IsConfigurable());
|
|
attrChanged = true;
|
|
}
|
|
if (desc.HasEnumerable() && attr.IsEnumerable() != desc.IsEnumerable()) {
|
|
attr.SetEnumerable(desc.IsEnumerable());
|
|
attrChanged = true;
|
|
}
|
|
|
|
if (!desc.IsAccessorDescriptor()) {
|
|
if (desc.HasWritable() && attr.IsWritable() != desc.IsWritable()) {
|
|
attr.SetWritable(desc.IsWritable());
|
|
attrChanged = true;
|
|
}
|
|
if (!desc.HasValue()) {
|
|
if (attrChanged) {
|
|
TransitionForAttributeChanged(receiver, attr);
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (IsAccessorDescriptor()) {
|
|
TaggedObject *obj = GetValue().GetTaggedObject();
|
|
if (receiver->IsJSGlobalObject()) {
|
|
JSTaggedValue val = GetValue();
|
|
if (val.IsPropertyBox()) {
|
|
PropertyBox *cell = PropertyBox::Cast(val.GetTaggedObject());
|
|
obj = cell->GetValue().GetTaggedObject();
|
|
}
|
|
}
|
|
auto accessor = AccessorData::Cast(obj);
|
|
if (!accessor->IsInternal() || !accessor->HasSetter()) {
|
|
attr.SetIsAccessor(false);
|
|
attrChanged = true;
|
|
}
|
|
}
|
|
|
|
return UpdateValueAndDetails(receiver, desc.GetValue(), attr, attrChanged);
|
|
} else {
|
|
if (IsAccessorDescriptor() && !IsElement()) {
|
|
TaggedArray *properties = TaggedArray::Cast(receiver->GetProperties().GetTaggedObject());
|
|
if (attrChanged && !properties->IsDictionaryMode()) {
|
|
// as some accessorData is in globalEnv, we need to new accessorData.
|
|
JSHandle<AccessorData> accessor = thread_->GetEcmaVM()->GetFactory()->NewAccessorData();
|
|
|
|
if (desc.HasGetter()) {
|
|
accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue());
|
|
} else {
|
|
accessor->SetGetter(thread_, JSHandle<AccessorData>::Cast(value_)->GetGetter());
|
|
}
|
|
if (desc.HasSetter()) {
|
|
accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue());
|
|
} else {
|
|
accessor->SetSetter(thread_, JSHandle<AccessorData>::Cast(value_)->GetSetter());
|
|
}
|
|
|
|
JSHandle<NameDictionary> dict(JSObject::TransitionToDictionary(thread_, receiver));
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
|
|
int entry = dict->FindEntry(key_.GetTaggedValue());
|
|
ASSERT(entry != -1);
|
|
dict->UpdateValueAndAttributes(thread_, entry, accessor.GetTaggedValue(), attr);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
auto valueAccessor = GetValue();
|
|
if (valueAccessor.IsPropertyBox()) {
|
|
valueAccessor = PropertyBox::Cast(valueAccessor.GetTaggedObject())->GetValue();
|
|
}
|
|
JSHandle<AccessorData> accessor =
|
|
(IsAccessorDescriptor() && !JSHandle<AccessorData>(thread_, valueAccessor)->IsInternal()) ?
|
|
JSHandle<AccessorData>(thread_, valueAccessor) :
|
|
thread_->GetEcmaVM()->GetFactory()->NewAccessorData();
|
|
if (desc.HasGetter()) {
|
|
accessor->SetGetter(thread_, desc.GetGetter().GetTaggedValue());
|
|
}
|
|
|
|
if (desc.HasSetter()) {
|
|
accessor->SetSetter(thread_, desc.GetSetter().GetTaggedValue());
|
|
}
|
|
|
|
if (!IsAccessorDescriptor()) {
|
|
attr.SetIsAccessor(true);
|
|
attrChanged = true;
|
|
}
|
|
|
|
JSHandle<JSTaggedValue> value = JSHandle<JSTaggedValue>::Cast(accessor);
|
|
bool success = UpdateValueAndDetails(receiver, value, attr, attrChanged);
|
|
if (success) {
|
|
JSHandle<JSObject> obj(receiver);
|
|
if (obj->GetJSHClass()->IsPrototype()) {
|
|
JSHandle<ProtoChangeMarker> markerHandle = thread_->GetEcmaVM()->GetFactory()->NewProtoChangeMarker();
|
|
obj->GetJSHClass()->SetProtoChangeMarker(thread_, markerHandle.GetTaggedValue());
|
|
}
|
|
JSHClass::NotifyAccessorChanged(thread_, JSHandle<JSHClass>(thread_, obj->GetJSHClass()));
|
|
}
|
|
return success;
|
|
}
|
|
}
|
|
|
|
void ObjectOperator::DeletePropertyInHolder()
|
|
{
|
|
if (IsElement()) {
|
|
return DeleteElementInHolder();
|
|
}
|
|
ObjectOperator::UpdateDetector(thread_, holder_.GetTaggedValue(), key_.GetTaggedValue());
|
|
JSObject::DeletePropertyInternal(thread_, JSHandle<JSObject>(holder_), key_, GetIndex());
|
|
}
|
|
|
|
bool ObjectOperator::AddProperty(const JSHandle<JSObject> &receiver, const JSHandle<JSTaggedValue> &value,
|
|
PropertyAttributes attr)
|
|
{
|
|
if (IsElement()) {
|
|
ElementsKind oldKind = receiver->GetClass()->GetElementsKind();
|
|
uint32_t oldLen = receiver.GetTaggedValue().IsJSArray() ?
|
|
JSArray::Cast(*receiver)->GetArrayLength() : 0;
|
|
bool ret = JSObject::AddElementInternal(thread_, receiver, elementIndex_, value, attr);
|
|
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread_, false);
|
|
ElementsKind newKind = receiver->GetClass()->GetElementsKind();
|
|
uint32_t newLen = receiver.GetTaggedValue().IsJSArray() ?
|
|
JSArray::Cast(*receiver)->GetArrayLength() : 0;
|
|
SetElementOutOfBounds(newLen > oldLen);
|
|
bool isTransited = false;
|
|
if (receiver.GetTaggedValue().IsJSArray() && (newKind != oldKind)) {
|
|
isTransited = true;
|
|
}
|
|
bool isDict = receiver->GetJSHClass()->IsDictionaryElement();
|
|
SetFound(elementIndex_, value.GetTaggedValue(), attr.GetValue(), !isDict, isTransited);
|
|
return ret;
|
|
}
|
|
|
|
ResetStateForAddProperty();
|
|
receiver_.Update(receiver.GetTaggedValue());
|
|
SetAttr(attr.GetValue());
|
|
AddPropertyInternal(value);
|
|
return true;
|
|
}
|
|
|
|
void ObjectOperator::WriteElement(const JSHandle<JSObject> &receiver, JSHandle<JSTaggedValue> value) const
|
|
{
|
|
ASSERT(IsElement() && GetIndex() < JSObject::MAX_ELEMENT_INDEX);
|
|
|
|
if (!ElementAccessor::IsDictionaryMode(receiver)) {
|
|
ElementAccessor::Set(thread_, receiver, index_, value, true);
|
|
return;
|
|
}
|
|
|
|
TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetTaggedObject());
|
|
NumberDictionary *dictionary = NumberDictionary::Cast(elements);
|
|
dictionary->UpdateValue(thread_, GetIndex(), value.GetTaggedValue());
|
|
}
|
|
|
|
void ObjectOperator::DeleteElementInHolder() const
|
|
{
|
|
JSHandle<JSObject> obj(holder_);
|
|
if (obj->IsJSSArray()) {
|
|
auto arrayHandler = JSHandle<JSSharedArray>::Cast(obj);
|
|
JSSharedArray::DeleteInElementMode(thread_, arrayHandler);
|
|
return;
|
|
}
|
|
JSHandle<JSTaggedValue> holeHandle(thread_, JSTaggedValue::Hole());
|
|
if (!ElementAccessor::IsDictionaryMode(obj)) {
|
|
ElementAccessor::Set(thread_, obj, index_, holeHandle, true, ElementsKind::HOLE);
|
|
JSObject::ElementsToDictionary(thread_, JSHandle<JSObject>(holder_));
|
|
RETURN_IF_ABRUPT_COMPLETION(thread_);
|
|
} else {
|
|
TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject());
|
|
JSHandle<NumberDictionary> dictHandle(thread_, elements);
|
|
JSHandle<NumberDictionary> newDict = NumberDictionary::Remove(thread_, dictHandle, GetIndex());
|
|
obj->SetElements(thread_, newDict);
|
|
}
|
|
}
|
|
|
|
void ObjectOperator::SetFound(uint32_t index, JSTaggedValue value, uint64_t attr, bool mode, bool transition)
|
|
{
|
|
SetIndex(index);
|
|
SetValue(value);
|
|
SetFastMode(mode);
|
|
SetIsTransition(transition);
|
|
SetAttr(attr);
|
|
}
|
|
|
|
void ObjectOperator::UpdateFound(uint32_t index, uint64_t attr, bool mode, bool transition)
|
|
{
|
|
SetIndex(index);
|
|
SetFastMode(mode);
|
|
SetIsTransition(transition);
|
|
SetAttr(attr);
|
|
}
|
|
|
|
void ObjectOperator::ResetState()
|
|
{
|
|
// index may used by element
|
|
SetIndex(NOT_FOUND_INDEX);
|
|
SetValue(JSTaggedValue::Undefined());
|
|
SetFastMode(false);
|
|
SetAttr(0);
|
|
SetIsOnPrototype(false);
|
|
SetHasReceiver(false);
|
|
SetIsTSHClass(false);
|
|
}
|
|
|
|
void ObjectOperator::ResetStateForAddProperty()
|
|
{
|
|
bool isOnPrototype = IsOnPrototype();
|
|
ResetState();
|
|
SetIsOnPrototype(isOnPrototype);
|
|
}
|
|
|
|
void ObjectOperator::LookupElementInlinedProps(const JSHandle<JSObject> &obj)
|
|
{
|
|
// if is js string, do special.
|
|
if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(obj.GetTaggedValue().GetTaggedObject())->IsString()) {
|
|
PropertyDescriptor desc(thread_);
|
|
bool status = JSPrimitiveRef::StringGetIndexProperty(thread_, obj, elementIndex_, &desc);
|
|
if (status) {
|
|
PropertyAttributes attr(desc);
|
|
SetFound(elementIndex_, desc.GetValue().GetTaggedValue(), attr.GetValue(), !IsFoundDict());
|
|
return;
|
|
}
|
|
}
|
|
{
|
|
DISALLOW_GARBAGE_COLLECTION;
|
|
if (obj->IsTypedArray()) {
|
|
JSTaggedValue val = JSTypedArray::FastElementGet(thread_,
|
|
JSHandle<JSTaggedValue>::Cast(obj), elementIndex_).GetValue().GetTaggedValue();
|
|
RETURN_IF_ABRUPT_COMPLETION(thread_);
|
|
if (!val.IsHole()) {
|
|
SetFound(elementIndex_, val, PropertyAttributes::GetDefaultAttributes(), !IsFoundDict());
|
|
}
|
|
return;
|
|
}
|
|
TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject());
|
|
if (elements->GetLength() == 0) {
|
|
return; // Empty Array
|
|
}
|
|
|
|
if (!elements->IsDictionaryMode()) {
|
|
if (elements->GetLength() <= elementIndex_) {
|
|
return;
|
|
}
|
|
|
|
JSTaggedValue value = ElementAccessor::Get(obj, elementIndex_);
|
|
if (value.IsHole()) {
|
|
return;
|
|
}
|
|
SetFound(elementIndex_, value, PropertyAttributes::GetDefaultAttributes(), !IsFoundDict());
|
|
} else {
|
|
SetFoundDict(true);
|
|
NumberDictionary *dictionary = NumberDictionary::Cast(obj->GetElements().GetTaggedObject());
|
|
JSTaggedValue key(static_cast<int>(elementIndex_));
|
|
int entry = dictionary->FindEntry(key);
|
|
if (entry == -1) {
|
|
return;
|
|
}
|
|
|
|
auto attr = dictionary->GetAttributes(entry).GetValue();
|
|
SetFound(entry, dictionary->GetValue(entry), attr, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ObjectOperator::AddPropertyInternal(const JSHandle<JSTaggedValue> &value)
|
|
{
|
|
ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
|
|
JSHandle<JSObject> obj(GetReceiver());
|
|
PropertyAttributes attr = GetAttr();
|
|
if (obj->IsJSGlobalObject()) {
|
|
JSMutableHandle<GlobalDictionary> dict(thread_, obj->GetProperties());
|
|
if (dict->GetLength() == 0) {
|
|
dict.Update(GlobalDictionary::Create(thread_));
|
|
}
|
|
|
|
// Add PropertyBox to global dictionary
|
|
JSHandle<PropertyBox> cellHandle = factory->NewPropertyBox(key_);
|
|
cellHandle->SetValue(thread_, value.GetTaggedValue());
|
|
PropertyBoxType cellType = value->IsUndefined() ? PropertyBoxType::UNDEFINED : PropertyBoxType::CONSTANT;
|
|
attr.SetBoxType(cellType);
|
|
|
|
JSHandle<GlobalDictionary> properties =
|
|
GlobalDictionary::PutIfAbsent(thread_, dict, key_, JSHandle<JSTaggedValue>(cellHandle), attr);
|
|
obj->SetProperties(thread_, properties);
|
|
// index and fastMode is not essential for global obj;
|
|
SetFound(0, cellHandle.GetTaggedValue(), attr.GetValue(), true);
|
|
return;
|
|
}
|
|
|
|
// The property has already existed whose value is hole, initialized by speculative hclass.
|
|
// Not need AddProperty,just SetProperty
|
|
if (receiverHoleEntry_ != -1) {
|
|
attr.SetOffset(receiverHoleEntry_);
|
|
JSHandle<JSObject> objHandle(receiver_);
|
|
ElementsKind oldKind = objHandle->GetJSHClass()->GetElementsKind();
|
|
auto actualValue =
|
|
JSHClass::ConvertOrTransitionWithRep(thread_, objHandle, key_, value, attr);
|
|
JSObject::TryMigrateToGenericKindForJSObject(thread_, objHandle, oldKind);
|
|
if (actualValue.isTransition) {
|
|
SetIsTransition(true);
|
|
}
|
|
attributes_.SetRepresentation(attr.GetRepresentation());
|
|
auto *hclass = receiver_->GetTaggedObject()->GetClass();
|
|
if (actualValue.isTagged) {
|
|
JSObject::Cast(receiver_.GetTaggedValue())->SetProperty<true>(thread_, hclass,
|
|
attr, value.GetTaggedValue());
|
|
} else {
|
|
JSObject::Cast(receiver_.GetTaggedValue())->SetProperty<false>(thread_, hclass, attr, actualValue.value);
|
|
}
|
|
uint32_t index = attr.IsInlinedProps() ? attr.GetOffset() :
|
|
attr.GetOffset() - obj->GetJSHClass()->GetInlinedProperties();
|
|
SetIsTSHClass(true);
|
|
SetIsOnPrototype(false);
|
|
SetFound(index, value.GetTaggedValue(), attr.GetValue(), true);
|
|
return;
|
|
}
|
|
|
|
attr = ObjectFastOperator::AddPropertyByName(thread_, obj, key_, value, attr);
|
|
RETURN_IF_ABRUPT_COMPLETION(thread_);
|
|
if (obj->GetJSHClass()->IsDictionaryMode()) {
|
|
SetFound(0, value.GetTaggedValue(), attr.GetValue(), false);
|
|
} else {
|
|
uint32_t index = attr.IsInlinedProps() ? attr.GetOffset() :
|
|
attr.GetOffset() - obj->GetJSHClass()->GetInlinedProperties();
|
|
SetFound(index, value.GetTaggedValue(), attr.GetValue(), true, true);
|
|
}
|
|
}
|
|
|
|
void ObjectOperator::DefineSetter(const JSHandle<JSTaggedValue> &value)
|
|
{
|
|
ASSERT(IsAccessorDescriptor());
|
|
JSHandle<AccessorData> accessor = JSHandle<AccessorData>::Cast(value_);
|
|
accessor->SetSetter(thread_, value.GetTaggedValue());
|
|
UpdateDataValue(JSHandle<JSObject>::Cast(receiver_), JSHandle<JSTaggedValue>::Cast(accessor), false);
|
|
}
|
|
|
|
void ObjectOperator::DefineGetter(const JSHandle<JSTaggedValue> &value)
|
|
{
|
|
ASSERT(IsAccessorDescriptor());
|
|
JSHandle<AccessorData> accessor = JSHandle<AccessorData>::Cast(value_);
|
|
accessor->SetGetter(thread_, value.GetTaggedValue());
|
|
UpdateDataValue(JSHandle<JSObject>::Cast(receiver_), JSHandle<JSTaggedValue>::Cast(accessor), false);
|
|
}
|
|
} // namespace panda::ecmascript
|