14514f5e3Sopenharmony_ci/*
24514f5e3Sopenharmony_ci * Copyright (c) 2021 Huawei Device Co., Ltd.
34514f5e3Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
44514f5e3Sopenharmony_ci * you may not use this file except in compliance with the License.
54514f5e3Sopenharmony_ci * You may obtain a copy of the License at
64514f5e3Sopenharmony_ci *
74514f5e3Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
84514f5e3Sopenharmony_ci *
94514f5e3Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
104514f5e3Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
114514f5e3Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
124514f5e3Sopenharmony_ci * See the License for the specific language governing permissions and
134514f5e3Sopenharmony_ci * limitations under the License.
144514f5e3Sopenharmony_ci */
154514f5e3Sopenharmony_ci
164514f5e3Sopenharmony_ci#include "ecmascript/js_for_in_iterator.h"
174514f5e3Sopenharmony_ci
184514f5e3Sopenharmony_ci#include "ecmascript/base/builtins_base.h"
194514f5e3Sopenharmony_ci#include "ecmascript/ic/proto_change_details.h"
204514f5e3Sopenharmony_ci#include "ecmascript/js_object-inl.h"
214514f5e3Sopenharmony_ci
224514f5e3Sopenharmony_cinamespace panda::ecmascript {
234514f5e3Sopenharmony_ciusing BuiltinsBase = base::BuiltinsBase;
244514f5e3Sopenharmony_cibool JSForInIterator::IsEnumCacheValid(JSTaggedValue receiver, JSTaggedValue cachedHclass, EnumCacheKind kind)
254514f5e3Sopenharmony_ci{
264514f5e3Sopenharmony_ci    DISALLOW_GARBAGE_COLLECTION;
274514f5e3Sopenharmony_ci    JSHClass *hclass = receiver.GetTaggedObject()->GetClass();
284514f5e3Sopenharmony_ci    if (JSTaggedValue(hclass) != cachedHclass) {
294514f5e3Sopenharmony_ci        return false;
304514f5e3Sopenharmony_ci    }
314514f5e3Sopenharmony_ci    if (kind == EnumCacheKind::SIMPLE) {
324514f5e3Sopenharmony_ci        return true;
334514f5e3Sopenharmony_ci    }
344514f5e3Sopenharmony_ci    ASSERT(kind == EnumCacheKind::PROTOCHAIN);
354514f5e3Sopenharmony_ci    JSTaggedValue proto = hclass->GetPrototype();
364514f5e3Sopenharmony_ci    if (!proto.IsECMAObject()) {
374514f5e3Sopenharmony_ci        return false;
384514f5e3Sopenharmony_ci    }
394514f5e3Sopenharmony_ci    JSTaggedValue protoChangeMarker = proto.GetTaggedObject()->GetClass()->GetProtoChangeMarker();
404514f5e3Sopenharmony_ci    if (protoChangeMarker.IsProtoChangeMarker()) {
414514f5e3Sopenharmony_ci        if (!ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject())->GetHasChanged()) {
424514f5e3Sopenharmony_ci            return true;
434514f5e3Sopenharmony_ci        }
444514f5e3Sopenharmony_ci    }
454514f5e3Sopenharmony_ci    return false;
464514f5e3Sopenharmony_ci}
474514f5e3Sopenharmony_ci
484514f5e3Sopenharmony_cibool JSForInIterator::NeedCheckProperty(JSTaggedValue receiver)
494514f5e3Sopenharmony_ci{
504514f5e3Sopenharmony_ci    DISALLOW_GARBAGE_COLLECTION;
514514f5e3Sopenharmony_ci    JSTaggedValue current = receiver;
524514f5e3Sopenharmony_ci    while (current.IsHeapObject()) {
534514f5e3Sopenharmony_ci        if (!current.IsJSObject() || current.GetTaggedObject()->GetClass()->HasDeleteProperty()) {
544514f5e3Sopenharmony_ci            return true;
554514f5e3Sopenharmony_ci        }
564514f5e3Sopenharmony_ci        current = JSObject::GetPrototype(current);
574514f5e3Sopenharmony_ci    }
584514f5e3Sopenharmony_ci    return false;
594514f5e3Sopenharmony_ci}
604514f5e3Sopenharmony_ci
614514f5e3Sopenharmony_cibool JSForInIterator::HasProperty(JSThread *thread, JSHandle<JSTaggedValue> receiver, JSHandle<JSTaggedValue> key)
624514f5e3Sopenharmony_ci{
634514f5e3Sopenharmony_ci    JSMutableHandle<JSTaggedValue> current(thread, receiver.GetTaggedValue());
644514f5e3Sopenharmony_ci    while (current->IsHeapObject()) {
654514f5e3Sopenharmony_ci        PropertyDescriptor desc(thread);
664514f5e3Sopenharmony_ci        bool has = JSTaggedValue::GetOwnProperty(thread, current, key, desc);
674514f5e3Sopenharmony_ci        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
684514f5e3Sopenharmony_ci        if (has && desc.IsEnumerable()) {
694514f5e3Sopenharmony_ci            return true;
704514f5e3Sopenharmony_ci        }
714514f5e3Sopenharmony_ci        current.Update(JSTaggedValue::GetPrototype(thread, current));
724514f5e3Sopenharmony_ci        RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, false);
734514f5e3Sopenharmony_ci    }
744514f5e3Sopenharmony_ci    return false;
754514f5e3Sopenharmony_ci}
764514f5e3Sopenharmony_ci
774514f5e3Sopenharmony_ciJSTaggedValue JSForInIterator::NextInternal(JSThread *thread, const JSHandle<JSForInIterator> &it)
784514f5e3Sopenharmony_ci{
794514f5e3Sopenharmony_ci    uint32_t length = it->GetLength();
804514f5e3Sopenharmony_ci    uint32_t index = it->GetIndex();
814514f5e3Sopenharmony_ci    if (index >= length) {
824514f5e3Sopenharmony_ci        return JSTaggedValue::Undefined();
834514f5e3Sopenharmony_ci    }
844514f5e3Sopenharmony_ci    JSTaggedValue taggedKeys = it->GetKeys();
854514f5e3Sopenharmony_ci    JSTaggedValue receiver = it->GetObject();
864514f5e3Sopenharmony_ci    EnumCacheKind kind = JSObject::GetEnumCacheKind(thread, taggedKeys);
874514f5e3Sopenharmony_ci    TaggedArray *keys = TaggedArray::Cast(taggedKeys.GetTaggedObject());
884514f5e3Sopenharmony_ci    if (IsEnumCacheValid(receiver, it->GetCachedHclass(), kind)) {
894514f5e3Sopenharmony_ci        JSTaggedValue key = keys->Get(index);
904514f5e3Sopenharmony_ci        index++;
914514f5e3Sopenharmony_ci        it->SetIndex(index);
924514f5e3Sopenharmony_ci        return key;
934514f5e3Sopenharmony_ci    }
944514f5e3Sopenharmony_ci
954514f5e3Sopenharmony_ci    if (!NeedCheckProperty(receiver)) {
964514f5e3Sopenharmony_ci        JSTaggedValue key = keys->Get(index);
974514f5e3Sopenharmony_ci        index++;
984514f5e3Sopenharmony_ci        it->SetIndex(index);
994514f5e3Sopenharmony_ci        return key;
1004514f5e3Sopenharmony_ci    }
1014514f5e3Sopenharmony_ci    // slow path
1024514f5e3Sopenharmony_ci    return NextInternalSlowpath(thread, it);
1034514f5e3Sopenharmony_ci}
1044514f5e3Sopenharmony_ci
1054514f5e3Sopenharmony_ciJSTaggedValue JSForInIterator::NextInternalSlowpath(JSThread *thread, const JSHandle<JSForInIterator> &it)
1064514f5e3Sopenharmony_ci{
1074514f5e3Sopenharmony_ci    uint32_t length = it->GetLength();
1084514f5e3Sopenharmony_ci    uint32_t index = it->GetIndex();
1094514f5e3Sopenharmony_ci    if (index >= length) {
1104514f5e3Sopenharmony_ci        return JSTaggedValue::Undefined();
1114514f5e3Sopenharmony_ci    }
1124514f5e3Sopenharmony_ci    JSHandle<TaggedArray> keysHandle(thread, it->GetKeys());
1134514f5e3Sopenharmony_ci    JSHandle<JSTaggedValue> receiverHandle(thread, it->GetObject());
1144514f5e3Sopenharmony_ci    JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
1154514f5e3Sopenharmony_ci    bool has = false;
1164514f5e3Sopenharmony_ci    while (index < length) {
1174514f5e3Sopenharmony_ci        keyHandle.Update(keysHandle->Get(index));
1184514f5e3Sopenharmony_ci        if (keyHandle->IsUndefined()) {
1194514f5e3Sopenharmony_ci            has = false;
1204514f5e3Sopenharmony_ci            break;
1214514f5e3Sopenharmony_ci        }
1224514f5e3Sopenharmony_ci        has = HasProperty(thread, receiverHandle, keyHandle);
1234514f5e3Sopenharmony_ci        if (has) {
1244514f5e3Sopenharmony_ci            break;
1254514f5e3Sopenharmony_ci        }
1264514f5e3Sopenharmony_ci        index++;
1274514f5e3Sopenharmony_ci    }
1284514f5e3Sopenharmony_ci    if (!has) {
1294514f5e3Sopenharmony_ci        it->SetIndex(index);
1304514f5e3Sopenharmony_ci        return JSTaggedValue::Undefined();
1314514f5e3Sopenharmony_ci    }
1324514f5e3Sopenharmony_ci
1334514f5e3Sopenharmony_ci    JSTaggedValue key = keysHandle->Get(index);
1344514f5e3Sopenharmony_ci    index++;
1354514f5e3Sopenharmony_ci    it->SetIndex(index);
1364514f5e3Sopenharmony_ci    return key;
1374514f5e3Sopenharmony_ci}
1384514f5e3Sopenharmony_ci
1394514f5e3Sopenharmony_ci// 13.7.5.16.2.1 %ForInIteratorPrototype%.next ( )
1404514f5e3Sopenharmony_ciJSTaggedValue JSForInIterator::Next(EcmaRuntimeCallInfo *msg)
1414514f5e3Sopenharmony_ci{
1424514f5e3Sopenharmony_ci    ASSERT(msg);
1434514f5e3Sopenharmony_ci    JSThread *thread = msg->GetThread();
1444514f5e3Sopenharmony_ci    [[maybe_unused]] EcmaHandleScope handleScope(thread);
1454514f5e3Sopenharmony_ci    // 1. Let O be the this value.
1464514f5e3Sopenharmony_ci    JSHandle<JSForInIterator> it(BuiltinsBase::GetThis(msg));
1474514f5e3Sopenharmony_ci    ASSERT(JSHandle<JSTaggedValue>(it)->IsForinIterator());
1484514f5e3Sopenharmony_ci    JSTaggedValue res = NextInternal(thread, it);
1494514f5e3Sopenharmony_ci    return res;
1504514f5e3Sopenharmony_ci}
1514514f5e3Sopenharmony_ci}  // namespace panda::ecmascript
152