Optimize for-in loop

1. Add EnumCache for simple properties and no-elements properties
2. Add fastpath for non-special objects
3. Avoid duplicated copy for slowpath
4. Transition hclass when detele prop in TS

Issue:https://gitee.com/openharmony/arkcompiler_ets_runtime/issues/I84DMO?from=project-issue

Signed-off-by: zhangyukun8 <zhangyukun8@huawei.com>
Change-Id: I81648b44cc83a907c50ec90c9e8de3bbb104cef8
This commit is contained in:
zhangyukun8 2023-09-28 17:02:15 +08:00
parent a1adef8e9e
commit eef37db2c3
66 changed files with 2543 additions and 274 deletions

View File

@ -752,6 +752,7 @@ ecma_source = [
"ecmascript/pgo_profiler/pgo_utils.cpp",
"ecmascript/pgo_profiler/ap_file/pgo_method_type_set.cpp",
"ecmascript/pgo_profiler/types/pgo_profile_type.cpp",
"ecmascript/property_accessor.cpp",
"ecmascript/stackmap/ark_stackmap_builder.cpp",
"ecmascript/stackmap/ark_stackmap_parser.cpp",
"ecmascript/stackmap/llvm_stackmap_parser.cpp",

View File

@ -488,7 +488,7 @@ bool FastJsonStringifier::TryCacheSerializeKeys(const JSHandle<JSObject> &obj, b
if (!propertiesArr->IsDictionaryMode()) {
JSHandle<JSHClass> jsHclass(thread_, obj->GetJSHClass());
JSTaggedValue enumCache = jsHclass->GetEnumCache();
if (!enumCache.IsNull()) {
if (JSObject::GetEnumCacheKind(thread_, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) {
JSHandle<TaggedArray> cache(thread_, enumCache);
uint32_t length = cache->GetLength();
for (uint32_t i = 0; i < length; i++) {
@ -765,7 +765,7 @@ bool FastJsonStringifier::DefaultSerializeKeys(const JSHandle<JSObject> &obj, bo
if (!propertiesArr->IsDictionaryMode()) {
JSHandle<JSHClass> jsHclass(thread_, obj->GetJSHClass());
JSTaggedValue enumCache = jsHclass->GetEnumCache();
if (!enumCache.IsNull()) {
if (JSObject::GetEnumCacheKind(thread_, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) {
JSHandle<TaggedArray> cache(thread_, enumCache);
uint32_t length = cache->GetLength();
for (uint32_t i = 0; i < length; i++) {

View File

@ -616,7 +616,7 @@ bool JsonStringifier::SerializeKeys(const JSHandle<JSObject> &obj, const JSHandl
bool hasChangedToDictionaryMode = false;
JSHandle<JSHClass> jsHclass(thread_, obj->GetJSHClass());
JSTaggedValue enumCache = jsHclass->GetEnumCache();
if (!enumCache.IsNull()) {
if (JSObject::GetEnumCacheKind(thread_, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) {
JSHandle<TaggedArray> cache(thread_, enumCache);
uint32_t length = cache->GetLength();
for (uint32_t i = 0; i < length; i++) {

View File

@ -2049,6 +2049,36 @@ DEF_CALL_SIGNATURE(CreateStringBySingleCharCode)
callSign->SetCallConv(CallSignature::CallConv::CCallConv);
}
DEF_CALL_SIGNATURE(Getpropiterator)
{
// 2 : 2 input parameters
CallSignature signature("Getpropiterator", 0, 2,
ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY());
*callSign = signature;
// 2 : 2 input parameters
std::array<VariableType, 2> params = {
VariableType::NATIVE_POINTER(), // glue
VariableType::JS_ANY(), // object
};
callSign->SetParameters(params.data());
callSign->SetCallConv(CallSignature::CallConv::CCallConv);
}
DEF_CALL_SIGNATURE(Getnextpropname)
{
// 2 : 2 input parameters
CallSignature signature("Getnextpropname", 0, 2,
ArgumentsOrder::DEFAULT_ORDER, VariableType::JS_ANY());
*callSign = signature;
// 2 : 2 input parameters
std::array<VariableType, 2> params = {
VariableType::NATIVE_POINTER(), // glue
VariableType::JS_ANY(), // iter
};
callSign->SetParameters(params.data());
callSign->SetCallConv(CallSignature::CallConv::CCallConv);
}
DEF_CALL_SIGNATURE(FastStringEqual)
{
// 3 : 3 input parameters

View File

@ -464,6 +464,8 @@ private:
V(GetSingleCharCodeByIndex) \
V(CreateStringBySingleCharCode) \
V(FastStringEqual) \
V(Getpropiterator) \
V(Getnextpropname) \
V(JSHClassFindProtoTransitions)
#define DECL_CALL_SIGNATURE(name) \

View File

@ -56,7 +56,7 @@ GateRef CircuitBuilder::DoubleIsINF(GateRef x)
// Js World
// cast operation
GateRef CircuitBuilder::GetGlobalConstantString(ConstantIndex index)
GateRef CircuitBuilder::GetGlobalConstantOffset(ConstantIndex index)
{
return PtrMul(IntPtr(sizeof(JSTaggedValue)), IntPtr(static_cast<int>(index)));
}

View File

@ -235,7 +235,7 @@ public:
GateRef GetModuleFromFunction(GateRef function);
GateRef GetHomeObjectFromFunction(GateRef function);
inline GateRef GetExpectedNumOfArgs(GateRef method);
inline GateRef GetGlobalConstantString(ConstantIndex index); // shareir
inline GateRef GetGlobalConstantOffset(ConstantIndex index); // shareir
// Set
void SetLexicalEnvToFunction(GateRef glue, GateRef function, GateRef value);
@ -531,6 +531,7 @@ public:
inline GateRef TaggedIsString(GateRef obj);
inline GateRef TaggedIsStringOrSymbol(GateRef obj);
inline GateRef TaggedIsSymbol(GateRef obj);
inline GateRef TaggedIsProtoChangeMarker(GateRef obj);
inline GateRef TaggedGetInt(GateRef x);
inline GateRef TaggedObjectIsString(GateRef obj);
inline GateRef TaggedObjectBothAreString(GateRef x, GateRef y);

View File

@ -919,6 +919,23 @@ void FastStringEqualStubBuilder::GenerateCircuit()
Return(result);
}
void GetpropiteratorStubBuilder::GenerateCircuit()
{
GateRef glue = PtrArgument(0);
GateRef object = TaggedArgument(1);
NewObjectStubBuilder newBuilder(this);
GateRef result = newBuilder.EnumerateObjectProperties(glue, object);
Return(result);
}
void GetnextpropnameStubBuilder::GenerateCircuit()
{
GateRef glue = PtrArgument(0);
GateRef iter = TaggedArgument(1);
GateRef result = NextInternal(glue, iter);
Return(result);
}
CallSignature CommonStubCSigns::callSigns_[CommonStubCSigns::NUM_OF_STUBS];
void CommonStubCSigns::Initialize()

View File

@ -78,6 +78,8 @@ namespace panda::ecmascript::kungfu {
V(JsBoundCallInternal) \
V(JsProxyCallInternal) \
V(CreateStringBySingleCharCode) \
V(Getpropiterator) \
V(Getnextpropname) \
V(GetSingleCharCodeByIndex) \
V(FastStringEqual)

View File

@ -443,7 +443,8 @@ DECLARE_ASM_HANDLER(HandleDefinegettersetterbyvalueV8V8V8V8)
DECLARE_ASM_HANDLER(HandleGetpropiterator)
{
DEFVARIABLE(varAcc, VariableType::JS_ANY(), acc);
GateRef res = CallRuntime(glue, RTSTUB_ID(GetPropIterator), { *varAcc });
NewObjectStubBuilder newBuilder(this);
GateRef res = newBuilder.EnumerateObjectProperties(glue, *varAcc);
CHECK_EXCEPTION_WITH_VARACC(res, INT_PTR(GETPROPITERATOR));
}
@ -1147,7 +1148,7 @@ DECLARE_ASM_HANDLER(HandleGetnextpropnameV8)
{
GateRef v0 = ReadInst8_0(pc);
GateRef iter = GetVregValue(sp, ZExtInt8ToPtr(v0));
GateRef result = CallRuntime(glue, RTSTUB_ID(GetNextPropName), { iter });
GateRef result = NextInternal(glue, iter);
CHECK_EXCEPTION_WITH_ACC(result, INT_PTR(GETNEXTPROPNAME_V8));
}

View File

@ -164,6 +164,26 @@ GateRef CircuitBuilder::TaggedIsStringOrSymbol(GateRef obj)
return ret;
}
GateRef CircuitBuilder::TaggedIsProtoChangeMarker(GateRef obj)
{
Label entry(env_);
SubCfgEntry(&entry);
Label exit(env_);
DEFVAlUE(result, env_, VariableType::BOOL(), False());
Label isHeapObject(env_);
Branch(TaggedIsHeapObject(obj), &isHeapObject, &exit);
Bind(&isHeapObject);
{
GateRef objType = GetObjectType(LoadHClass(obj));
result = Equal(objType, Int32(static_cast<int32_t>(JSType::PROTO_CHANGE_MARKER)));
Jump(&exit);
}
Bind(&exit);
auto ret = *result;
SubCfgExit();
return ret;
}
inline GateRef CircuitBuilder::IsOptimizedAndNotFastCall(GateRef obj)
{
GateRef hClass = LoadHClass(obj);

View File

@ -207,6 +207,80 @@ GateRef NewObjectStubBuilder::NewTaggedArray(GateRef glue, GateRef len)
return ret;
}
GateRef NewObjectStubBuilder::NewJSForinIterator(GateRef glue, GateRef receiver, GateRef keys, GateRef cachedHclass)
{
auto env = GetEnvironment();
GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit()));
GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset);
GateRef hclass = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, GlobalEnv::FOR_IN_ITERATOR_CLASS_INDEX);
GateRef iter = NewJSObject(glue, hclass);
// init JSForinIterator
SetObjectOfForInIterator(glue, iter, receiver);
SetCachedHclassOFForInIterator(glue, iter, cachedHclass);
SetKeysOfForInIterator(glue, iter, keys);
SetIndexOfForInIterator(glue, iter, Int32(EnumCache::ENUM_CACHE_HEADER_SIZE));
GateRef length = GetLengthOfTaggedArray(keys);
SetLengthOfForInIterator(glue, iter, length);
return iter;
}
GateRef NewObjectStubBuilder::EnumerateObjectProperties(GateRef glue, GateRef obj)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(object, VariableType::JS_ANY(), Undefined());
Label isString(env);
Label isNotString(env);
Label afterObjectTransform(env);
Label slowpath(env);
Label empty(env);
Label tryGetEnumCache(env);
Label cacheHit(env);
Branch(TaggedIsString(obj), &isString, &isNotString);
Bind(&isString);
{
object = CallRuntime(glue, RTSTUB_ID(PrimitiveStringCreate), { obj });;
Jump(&afterObjectTransform);
}
Bind(&isNotString);
{
object = ToPrototypeOrObj(glue, obj);
Jump(&afterObjectTransform);
}
Bind(&afterObjectTransform);
Branch(TaggedIsUndefinedOrNull(*object), &empty, &tryGetEnumCache);
Bind(&tryGetEnumCache);
GateRef enumCache = TryGetEnumCache(glue, *object);
Branch(TaggedIsUndefined(enumCache), &slowpath, &cacheHit);
Bind(&cacheHit);
{
GateRef hclass = LoadHClass(obj);
result = NewJSForinIterator(glue, *object, enumCache, hclass);
Jump(&exit);
}
Bind(&empty);
{
GateRef emptyArray = GetEmptyArray(glue);
result = NewJSForinIterator(glue, Undefined(), emptyArray, Undefined());
Jump(&exit);
}
Bind(&slowpath);
{
result = CallRuntime(glue, RTSTUB_ID(GetPropIteratorSlowpath), { *object });
Jump(&exit);
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
void NewObjectStubBuilder::NewArgumentsList(Variable *result, Label *exit,
GateRef sp, GateRef startIdx, GateRef numArgs)
{

View File

@ -49,6 +49,8 @@ public:
GateRef NewJSArray(GateRef glue, GateRef hclass);
GateRef NewTaggedArray(GateRef glue, GateRef len);
GateRef NewJSArrayWithSize(GateRef hclass, GateRef size);
GateRef NewJSForinIterator(GateRef glue, GateRef receiver, GateRef keys, GateRef cachedHclass);
GateRef EnumerateObjectProperties(GateRef glue, GateRef obj);
void NewArgumentsList(Variable *result, Label *exit, GateRef sp, GateRef startIdx, GateRef numArgs);
void NewArgumentsObj(Variable *result, Label *exit, GateRef argumentsList, GateRef numArgs);
void AllocLineStringObject(Variable *result, Label *exit, GateRef length, bool compressed);

View File

@ -1269,10 +1269,10 @@ void SlowPathLowering::LowerGreaterEq(GateRef gate)
void SlowPathLowering::LowerGetPropIterator(GateRef gate)
{
const int id = RTSTUB_ID(GetPropIterator);
// 1: number of value inputs
ASSERT(acc_.GetNumValueIn(gate) == 1);
GateRef newGate = LowerCallRuntime(gate, id, {acc_.GetValueIn(gate, 0)});
GateRef object = {acc_.GetValueIn(gate, 0)};
GateRef newGate = builder_.CallStub(glue_, gate, CommonStubCSigns::Getpropiterator, {glue_, object});
ReplaceHirWithValue(gate, newGate);
}
@ -1793,11 +1793,10 @@ void SlowPathLowering::LowerConditionJump(GateRef gate, bool isEqualJump)
void SlowPathLowering::LowerGetNextPropName(GateRef gate)
{
const int id = RTSTUB_ID(GetNextPropName);
// 1: number of value inputs
ASSERT(acc_.GetNumValueIn(gate) == 1);
GateRef iter = acc_.GetValueIn(gate, 0);
GateRef newGate = LowerCallRuntime(gate, id, { iter });
GateRef newGate = builder_.CallStub(glue_, gate, CommonStubCSigns::Getnextpropname, {glue_, iter});
ReplaceHirWithValue(gate, newGate);
}
@ -2498,14 +2497,14 @@ void SlowPathLowering::LowerTypeof(GateRef gate)
GateRef gConstAddr = builder_.Load(VariableType::JS_POINTER(), glue_,
builder_.IntPtr(JSThread::GlueData::GetGlobalConstOffset(builder_.GetCompilationConfig()->Is32Bit())));
GateRef undefinedIndex = builder_.GetGlobalConstantString(ConstantIndex::UNDEFINED_STRING_INDEX);
GateRef undefinedIndex = builder_.GetGlobalConstantOffset(ConstantIndex::UNDEFINED_STRING_INDEX);
GateRef gConstUndefinedStr = builder_.Load(VariableType::JS_POINTER(), gConstAddr, undefinedIndex);
DEFVAlUE(result, (&builder_), VariableType::JS_POINTER(), gConstUndefinedStr);
Label objIsTrue(&builder_);
Label objNotTrue(&builder_);
Label defaultLabel(&builder_);
GateRef gConstBooleanStr = builder_.Load(VariableType::JS_POINTER(), gConstAddr,
builder_.GetGlobalConstantString(ConstantIndex::BOOLEAN_STRING_INDEX));
builder_.GetGlobalConstantOffset(ConstantIndex::BOOLEAN_STRING_INDEX));
builder_.Branch(builder_.TaggedIsTrue(obj), &objIsTrue, &objNotTrue);
builder_.Bind(&objIsTrue);
{
@ -2530,7 +2529,7 @@ void SlowPathLowering::LowerTypeof(GateRef gate)
builder_.Bind(&objIsNull);
{
result = builder_.Load(VariableType::JS_POINTER(), gConstAddr,
builder_.GetGlobalConstantString(ConstantIndex::OBJECT_STRING_INDEX));
builder_.GetGlobalConstantOffset(ConstantIndex::OBJECT_STRING_INDEX));
builder_.Jump(&exit);
}
builder_.Bind(&objNotNull);
@ -2541,7 +2540,7 @@ void SlowPathLowering::LowerTypeof(GateRef gate)
builder_.Bind(&objIsUndefined);
{
result = builder_.Load(VariableType::JS_POINTER(), gConstAddr,
builder_.GetGlobalConstantString(ConstantIndex::UNDEFINED_STRING_INDEX));
builder_.GetGlobalConstantOffset(ConstantIndex::UNDEFINED_STRING_INDEX));
builder_.Jump(&exit);
}
builder_.Bind(&objNotUndefined);
@ -2562,7 +2561,7 @@ void SlowPathLowering::LowerTypeof(GateRef gate)
builder_.Bind(&objIsString);
{
result = builder_.Load(VariableType::JS_POINTER(), gConstAddr,
builder_.GetGlobalConstantString(ConstantIndex::STRING_STRING_INDEX));
builder_.GetGlobalConstantOffset(ConstantIndex::STRING_STRING_INDEX));
builder_.Jump(&exit);
}
builder_.Bind(&objNotString);
@ -2573,7 +2572,7 @@ void SlowPathLowering::LowerTypeof(GateRef gate)
builder_.Bind(&objIsSymbol);
{
result = builder_.Load(VariableType::JS_POINTER(), gConstAddr,
builder_.GetGlobalConstantString(ConstantIndex::SYMBOL_STRING_INDEX));
builder_.GetGlobalConstantOffset(ConstantIndex::SYMBOL_STRING_INDEX));
builder_.Jump(&exit);
}
builder_.Bind(&objNotSymbol);
@ -2584,7 +2583,7 @@ void SlowPathLowering::LowerTypeof(GateRef gate)
builder_.Bind(&objIsCallable);
{
result = builder_.Load(VariableType::JS_POINTER(), gConstAddr,
builder_.GetGlobalConstantString(ConstantIndex::FUNCTION_STRING_INDEX));
builder_.GetGlobalConstantOffset(ConstantIndex::FUNCTION_STRING_INDEX));
builder_.Jump(&exit);
}
builder_.Bind(&objNotCallable);
@ -2595,13 +2594,13 @@ void SlowPathLowering::LowerTypeof(GateRef gate)
builder_.Bind(&objIsBigInt);
{
result = builder_.Load(VariableType::JS_POINTER(), gConstAddr,
builder_.GetGlobalConstantString(ConstantIndex::BIGINT_STRING_INDEX));
builder_.GetGlobalConstantOffset(ConstantIndex::BIGINT_STRING_INDEX));
builder_.Jump(&exit);
}
builder_.Bind(&objNotBigInt);
{
result = builder_.Load(VariableType::JS_POINTER(), gConstAddr,
builder_.GetGlobalConstantString(ConstantIndex::OBJECT_STRING_INDEX));
builder_.GetGlobalConstantOffset(ConstantIndex::OBJECT_STRING_INDEX));
builder_.Jump(&exit);
}
}
@ -2616,7 +2615,7 @@ void SlowPathLowering::LowerTypeof(GateRef gate)
builder_.Bind(&objIsNum);
{
result = builder_.Load(VariableType::JS_POINTER(), gConstAddr,
builder_.GetGlobalConstantString(ConstantIndex::NUMBER_STRING_INDEX));
builder_.GetGlobalConstantOffset(ConstantIndex::NUMBER_STRING_INDEX));
builder_.Jump(&exit);
}
builder_.Bind(&objNotNum);

View File

@ -30,6 +30,7 @@
#include "ecmascript/ic/proto_change_details.h"
#include "ecmascript/js_array.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_for_in_iterator.h"
#include "ecmascript/js_generator_object.h"
#include "ecmascript/js_object.h"
#include "ecmascript/js_tagged_value.h"
@ -1205,6 +1206,32 @@ inline GateRef StubBuilder::IsJsProxy(GateRef obj)
return Int32Equal(objectType, Int32(static_cast<int32_t>(JSType::JS_PROXY)));
}
inline GateRef StubBuilder::IsJSGlobalObject(GateRef obj)
{
GateRef objectType = GetObjectType(LoadHClass(obj));
return Int32Equal(objectType, Int32(static_cast<int32_t>(JSType::JS_GLOBAL_OBJECT)));
}
inline GateRef StubBuilder::IsModuleNamespace(GateRef obj)
{
GateRef objectType = GetObjectType(LoadHClass(obj));
return Int32Equal(objectType, Int32(static_cast<int32_t>(JSType::JS_MODULE_NAMESPACE)));
}
inline GateRef StubBuilder::ObjIsSpecialContainer(GateRef obj)
{
GateRef objectType = GetObjectType(LoadHClass(obj));
return BoolAnd(
Int32GreaterThanOrEqual(objectType, Int32(static_cast<int32_t>(JSType::JS_API_ARRAY_LIST))),
Int32LessThanOrEqual(objectType, Int32(static_cast<int32_t>(JSType::JS_API_QUEUE))));
}
inline GateRef StubBuilder::IsJSPrimitiveRef(GateRef obj)
{
GateRef objectType = GetObjectType(LoadHClass(obj));
return Int32Equal(objectType, Int32(static_cast<int32_t>(JSType::JS_PRIMITIVE_REF)));
}
inline GateRef StubBuilder::IsJsArray(GateRef obj)
{
GateRef objectType = GetObjectType(LoadHClass(obj));
@ -1421,6 +1448,86 @@ inline GateRef StubBuilder::HclassIsPropertyBox(GateRef hClass)
Int32(static_cast<int32_t>(JSType::PROPERTY_BOX)));
}
inline GateRef StubBuilder::TaggedIsProtoChangeMarker(GateRef obj)
{
return env_->GetBuilder()->TaggedIsProtoChangeMarker(obj);
}
inline GateRef StubBuilder::GetEmptyArray(GateRef glue)
{
GateRef gConstAddr = Load(VariableType::JS_ANY(), glue,
IntPtr(JSThread::GlueData::GetGlobalConstOffset(env_->Is32Bit())));
GateRef offset = GetGlobalConstantOffset(ConstantIndex::EMPTY_ARRAY_OBJECT_INDEX);
return Load(VariableType::JS_ANY(), gConstAddr, offset);
}
inline GateRef StubBuilder::GetLengthFromForInIterator(GateRef iter)
{
GateRef offset = IntPtr(JSForInIterator::LENGTH_OFFSET);
return Load(VariableType::INT32(), iter, offset);
}
inline GateRef StubBuilder::GetIndexFromForInIterator(GateRef iter)
{
GateRef offset = IntPtr(JSForInIterator::INDEX_OFFSET);
return Load(VariableType::INT32(), iter, offset);
}
inline GateRef StubBuilder::GetKeysFromForInIterator(GateRef iter)
{
GateRef offset = IntPtr(JSForInIterator::KEYS_OFFSET);
return Load(VariableType::JS_ANY(), iter, offset);
}
inline GateRef StubBuilder::GetObjectFromForInIterator(GateRef iter)
{
GateRef offset = IntPtr(JSForInIterator::OBJECT_OFFSET);
return Load(VariableType::JS_ANY(), iter, offset);
}
inline GateRef StubBuilder::GetCachedHclassFromForInIterator(GateRef iter)
{
GateRef offset = IntPtr(JSForInIterator::CACHED_HCLASS_OFFSET);
return Load(VariableType::JS_ANY(), iter, offset);
}
inline void StubBuilder::SetLengthOfForInIterator(GateRef glue, GateRef iter, GateRef length)
{
GateRef offset = IntPtr(JSForInIterator::LENGTH_OFFSET);
Store(VariableType::INT32(), glue, iter, offset, length);
}
inline void StubBuilder::SetIndexOfForInIterator(GateRef glue, GateRef iter, GateRef index)
{
GateRef offset = IntPtr(JSForInIterator::INDEX_OFFSET);
Store(VariableType::INT32(), glue, iter, offset, index);
}
inline void StubBuilder::SetKeysOfForInIterator(GateRef glue, GateRef iter, GateRef keys)
{
GateRef offset = IntPtr(JSForInIterator::KEYS_OFFSET);
Store(VariableType::JS_ANY(), glue, iter, offset, keys);
}
inline void StubBuilder::SetObjectOfForInIterator(GateRef glue, GateRef iter, GateRef object)
{
GateRef offset = IntPtr(JSForInIterator::OBJECT_OFFSET);
Store(VariableType::JS_ANY(), glue, iter, offset, object);
}
inline void StubBuilder::SetCachedHclassOFForInIterator(GateRef glue, GateRef iter, GateRef hclass)
{
GateRef offset = IntPtr(JSForInIterator::CACHED_HCLASS_OFFSET);
Store(VariableType::JS_ANY(), glue, iter, offset, hclass);
}
inline void StubBuilder::IncreaseInteratorIndex(GateRef glue, GateRef iter, GateRef index)
{
GateRef newIndex = Int32Add(index, Int32(1));
GateRef offset = IntPtr(JSForInIterator::INDEX_OFFSET);
Store(VariableType::INT32(), glue, iter, offset, newIndex);
}
inline GateRef StubBuilder::IsField(GateRef attr)
{
return Int32Equal(
@ -1590,6 +1697,18 @@ inline GateRef StubBuilder::GetPrototypeFromHClass(GateRef hClass)
return Load(VariableType::JS_ANY(), hClass, protoOffset);
}
inline GateRef StubBuilder::GetEnumCacheFromHClass(GateRef hClass)
{
GateRef offset = IntPtr(JSHClass::ENUM_CACHE_OFFSET);
return Load(VariableType::JS_ANY(), hClass, offset);
}
inline GateRef StubBuilder::GetProtoChangeMarkerFromHClass(GateRef hClass)
{
GateRef offset = IntPtr(JSHClass::PROTO_CHANGE_MARKER_OFFSET);
return Load(VariableType::JS_ANY(), hClass, offset);
}
inline GateRef StubBuilder::GetLayoutFromHClass(GateRef hClass)
{
GateRef attrOffset = IntPtr(JSHClass::LAYOUT_OFFSET);
@ -1747,6 +1866,15 @@ inline GateRef StubBuilder::GetNumberOfPropsFromHClass(GateRef hClass)
Int32((1LLU << JSHClass::NumberOfPropsBits::SIZE) - 1));
}
inline GateRef StubBuilder::HasDeleteProperty(GateRef hClass)
{
GateRef bitfield = Load(VariableType::INT32(), hClass, IntPtr(JSHClass::BIT_FIELD1_OFFSET));
return Int32NotEqual(
Int32And(Int32LSR(bitfield, Int32(JSHClass::HasDeletePropertyBit::START_BIT)),
Int32((1LLU << JSHClass::HasDeletePropertyBit::SIZE) - 1)),
Int32(0));
}
inline GateRef StubBuilder::IsTSHClass(GateRef hClass)
{
GateRef bitfield = Load(VariableType::INT32(), hClass, IntPtr(JSHClass::BIT_FIELD_OFFSET));
@ -2099,7 +2227,7 @@ inline GateRef StubBuilder::GetGlobalConstantAddr(GateRef index)
return Int64Mul(Int64(sizeof(JSTaggedValue)), index);
}
inline GateRef StubBuilder::GetGlobalConstantString(ConstantIndex index)
inline GateRef StubBuilder::GetGlobalConstantOffset(ConstantIndex index)
{
if (env_->Is32Bit()) {
return Int32Mul(Int32(sizeof(JSTaggedValue)), Int32(static_cast<int>(index)));

View File

@ -28,6 +28,7 @@
#include "ecmascript/js_api/js_api_arraylist.h"
#include "ecmascript/js_api/js_api_vector.h"
#include "ecmascript/js_object.h"
#include "ecmascript/js_primitive_ref.h"
#include "ecmascript/js_arguments.h"
#include "ecmascript/mem/remembered_set.h"
#include "ecmascript/message_string.h"
@ -3390,14 +3391,14 @@ GateRef StubBuilder::FastTypeOf(GateRef glue, GateRef obj)
GateRef gConstAddr = Load(VariableType::JS_ANY(), glue,
IntPtr(JSThread::GlueData::GetGlobalConstOffset(env_->Is32Bit())));
GateRef undefinedIndex = GetGlobalConstantString(ConstantIndex::UNDEFINED_STRING_INDEX);
GateRef undefinedIndex = GetGlobalConstantOffset(ConstantIndex::UNDEFINED_STRING_INDEX);
GateRef gConstUndefinedStr = Load(VariableType::JS_POINTER(), gConstAddr, undefinedIndex);
DEFVARIABLE(result, VariableType::JS_POINTER(), gConstUndefinedStr);
Label objIsTrue(env);
Label objNotTrue(env);
Label defaultLabel(env);
GateRef gConstBooleanStr = Load(VariableType::JS_POINTER(), gConstAddr,
GetGlobalConstantString(ConstantIndex::BOOLEAN_STRING_INDEX));
GetGlobalConstantOffset(ConstantIndex::BOOLEAN_STRING_INDEX));
Branch(TaggedIsTrue(obj), &objIsTrue, &objNotTrue);
Bind(&objIsTrue);
{
@ -3422,7 +3423,7 @@ GateRef StubBuilder::FastTypeOf(GateRef glue, GateRef obj)
Bind(&objIsNull);
{
result = Load(VariableType::JS_POINTER(), gConstAddr,
GetGlobalConstantString(ConstantIndex::OBJECT_STRING_INDEX));
GetGlobalConstantOffset(ConstantIndex::OBJECT_STRING_INDEX));
Jump(&exit);
}
Bind(&objNotNull);
@ -3433,7 +3434,7 @@ GateRef StubBuilder::FastTypeOf(GateRef glue, GateRef obj)
Bind(&objIsUndefined);
{
result = Load(VariableType::JS_POINTER(), gConstAddr,
GetGlobalConstantString(ConstantIndex::UNDEFINED_STRING_INDEX));
GetGlobalConstantOffset(ConstantIndex::UNDEFINED_STRING_INDEX));
Jump(&exit);
}
Bind(&objNotUndefined);
@ -3454,7 +3455,7 @@ GateRef StubBuilder::FastTypeOf(GateRef glue, GateRef obj)
Bind(&objIsString);
{
result = Load(VariableType::JS_POINTER(), gConstAddr,
GetGlobalConstantString(ConstantIndex::STRING_STRING_INDEX));
GetGlobalConstantOffset(ConstantIndex::STRING_STRING_INDEX));
Jump(&exit);
}
Bind(&objNotString);
@ -3465,7 +3466,7 @@ GateRef StubBuilder::FastTypeOf(GateRef glue, GateRef obj)
Bind(&objIsSymbol);
{
result = Load(VariableType::JS_POINTER(), gConstAddr,
GetGlobalConstantString(ConstantIndex::SYMBOL_STRING_INDEX));
GetGlobalConstantOffset(ConstantIndex::SYMBOL_STRING_INDEX));
Jump(&exit);
}
Bind(&objNotSymbol);
@ -3476,7 +3477,7 @@ GateRef StubBuilder::FastTypeOf(GateRef glue, GateRef obj)
Bind(&objIsCallable);
{
result = Load(VariableType::JS_POINTER(), gConstAddr,
GetGlobalConstantString(ConstantIndex::FUNCTION_STRING_INDEX));
GetGlobalConstantOffset(ConstantIndex::FUNCTION_STRING_INDEX));
Jump(&exit);
}
Bind(&objNotCallable);
@ -3487,13 +3488,13 @@ GateRef StubBuilder::FastTypeOf(GateRef glue, GateRef obj)
Bind(&objIsBigInt);
{
result = Load(VariableType::JS_POINTER(), gConstAddr,
GetGlobalConstantString(ConstantIndex::BIGINT_STRING_INDEX));
GetGlobalConstantOffset(ConstantIndex::BIGINT_STRING_INDEX));
Jump(&exit);
}
Bind(&objNotBigInt);
{
result = Load(VariableType::JS_POINTER(), gConstAddr,
GetGlobalConstantString(ConstantIndex::OBJECT_STRING_INDEX));
GetGlobalConstantOffset(ConstantIndex::OBJECT_STRING_INDEX));
Jump(&exit);
}
}
@ -3508,7 +3509,7 @@ GateRef StubBuilder::FastTypeOf(GateRef glue, GateRef obj)
Bind(&objIsNum);
{
result = Load(VariableType::JS_POINTER(), gConstAddr,
GetGlobalConstantString(ConstantIndex::NUMBER_STRING_INDEX));
GetGlobalConstantOffset(ConstantIndex::NUMBER_STRING_INDEX));
Jump(&exit);
}
Bind(&objNotNum);
@ -5232,6 +5233,490 @@ GateRef StubBuilder::JSAPIContainerGet(GateRef glue, GateRef receiver, GateRef i
return ret;
}
GateRef StubBuilder::GetEnumCacheKind(GateRef glue, GateRef enumCache)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::INT32(), Int32(static_cast<int32_t>(EnumCacheKind::NONE)));
Label enumCacheIsArray(env);
Label isEmptyArray(env);
Label notEmptyArray(env);
Branch(TaggedIsUndefinedOrNull(enumCache), &exit, &enumCacheIsArray);
Bind(&enumCacheIsArray);
GateRef emptyArray = GetEmptyArray(glue);
Branch(Int64Equal(enumCache, emptyArray), &isEmptyArray, &notEmptyArray);
Bind(&isEmptyArray);
{
result = Int32(static_cast<int32_t>(EnumCacheKind::SIMPLE));
Jump(&exit);
}
Bind(&notEmptyArray);
{
GateRef taggedKind = GetValueFromTaggedArray(enumCache, Int32(EnumCache::ENUM_CACHE_KIND_OFFSET));
result = TaggedGetInt(taggedKind);
Jump(&exit);
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef StubBuilder::IsEnumCacheValid(GateRef receiver, GateRef cachedHclass, GateRef kind)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::BOOL(), False());
Label isSameHclass(env);
Label isSimpleEnumCache(env);
Label notSimpleEnumCache(env);
Label prototypeIsEcmaObj(env);
Label isProtoChangeMarker(env);
Label protoNotChanged(env);
GateRef hclass = LoadHClass(receiver);
Branch(Int64Equal(hclass, cachedHclass), &isSameHclass, &exit);
Bind(&isSameHclass);
Branch(Int32Equal(kind, Int32(static_cast<int32_t>(EnumCacheKind::SIMPLE))),
&isSimpleEnumCache, &notSimpleEnumCache);
Bind(&isSimpleEnumCache);
{
result = True();
Jump(&exit);
}
Bind(&notSimpleEnumCache);
GateRef prototype = GetPrototypeFromHClass(hclass);
Branch(IsEcmaObject(prototype), &prototypeIsEcmaObj, &exit);
Bind(&prototypeIsEcmaObj);
GateRef protoChangeMarker = GetProtoChangeMarkerFromHClass(hclass);
Branch(TaggedIsProtoChangeMarker(protoChangeMarker), &isProtoChangeMarker, &exit);
Bind(&isProtoChangeMarker);
Branch(GetHasChanged(protoChangeMarker), &exit, &protoNotChanged);
Bind(&protoNotChanged);
{
result = True();
Jump(&exit);
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef StubBuilder::NeedCheckProperty(GateRef receiver)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
Label loopHead(env);
Label loopEnd(env);
Label afterLoop(env);
Label isJSObject(env);
Label hasNoDeleteProperty(env);
DEFVARIABLE(result, VariableType::BOOL(), True());
DEFVARIABLE(current, VariableType::JS_ANY(), receiver);
Branch(TaggedIsHeapObject(*current), &loopHead, &afterLoop);
LoopBegin(&loopHead);
{
Branch(IsJSObject(*current), &isJSObject, &exit);
Bind(&isJSObject);
GateRef hclass = LoadHClass(*current);
Branch(HasDeleteProperty(hclass), &exit, &hasNoDeleteProperty);
Bind(&hasNoDeleteProperty);
current = GetPrototypeFromHClass(hclass);
Branch(TaggedIsHeapObject(*current), &loopEnd, &afterLoop);
}
Bind(&loopEnd);
LoopEnd(&loopHead);
Bind(&afterLoop);
{
result = False();
Jump(&exit);
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef StubBuilder::NextInternal(GateRef glue, GateRef iter)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::JS_ANY(), Undefined());
Label notFinish(env);
Label notEnumCacheValid(env);
Label fastGetKey(env);
Label slowpath(env);
GateRef index = GetIndexFromForInIterator(iter);
GateRef length = GetLengthFromForInIterator(iter);
Branch(Int32GreaterThanOrEqual(index, length), &exit, &notFinish);
Bind(&notFinish);
GateRef keys = GetKeysFromForInIterator(iter);
GateRef receiver = GetObjectFromForInIterator(iter);
GateRef cachedHclass = GetCachedHclassFromForInIterator(iter);
GateRef kind = GetEnumCacheKind(glue, keys);
Branch(IsEnumCacheValid(receiver, cachedHclass, kind), &fastGetKey, &notEnumCacheValid);
Bind(&notEnumCacheValid);
Branch(NeedCheckProperty(receiver), &slowpath, &fastGetKey);
Bind(&fastGetKey);
{
result = GetValueFromTaggedArray(keys, index);
IncreaseInteratorIndex(glue, iter, index);
Jump(&exit);
}
Bind(&slowpath);
{
result = CallRuntime(glue, RTSTUB_ID(GetNextPropNameSlowpath), { iter });
Jump(&exit);
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef StubBuilder::GetFunctionPrototype(GateRef glue, size_t index)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::JS_ANY(), Undefined());
Label isHeapObject(env);
Label isJSHclass(env);
GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env_->Is32Bit()));
GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset);
GateRef func = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, index);
GateRef protoOrHclass = Load(VariableType::JS_ANY(), func, IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET));
result = protoOrHclass;
Branch(TaggedIsHeapObject(protoOrHclass), &isHeapObject, &exit);
Bind(&isHeapObject);
Branch(IsJSHClass(protoOrHclass), &isJSHclass, &exit);
Bind(&isJSHclass);
{
result = GetPrototypeFromHClass(protoOrHclass);
Jump(&exit);
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef StubBuilder::ToPrototypeOrObj(GateRef glue, GateRef obj)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::JS_ANY(), obj);
Label isNumber(env);
Label notNumber(env);
Label isBoolean(env);
Label notBoolean(env);
Label isString(env);
Label notString(env);
Label isSymbol(env);
Label notSymbol(env);
Label isBigInt(env);
Branch(TaggedIsNumber(obj), &isNumber, &notNumber);
Bind(&isNumber);
{
result = GetFunctionPrototype(glue, GlobalEnv::NUMBER_FUNCTION_INDEX);
Jump(&exit);
}
Bind(&notNumber);
Branch(TaggedIsBoolean(obj), &isBoolean, &notBoolean);
Bind(&isBoolean);
{
result = GetFunctionPrototype(glue, GlobalEnv::BOOLEAN_FUNCTION_INDEX);
Jump(&exit);
}
Bind(&notBoolean);
Branch(TaggedIsString(obj), &isString, &notString);
Bind(&isString);
{
result = GetFunctionPrototype(glue, GlobalEnv::STRING_FUNCTION_INDEX);
Jump(&exit);
}
Bind(&notString);
Branch(TaggedIsSymbol(obj), &isSymbol, &notSymbol);
Bind(&isSymbol);
{
result = GetFunctionPrototype(glue, GlobalEnv::SYMBOL_FUNCTION_INDEX);
Jump(&exit);
}
Bind(&notSymbol);
Branch(TaggedIsBigInt(obj), &isBigInt, &exit);
Bind(&isBigInt);
{
result = GetFunctionPrototype(glue, GlobalEnv::BIGINT_FUNCTION_INDEX);
Jump(&exit);
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef StubBuilder::IsSpecialKeysObject(GateRef obj)
{
return BoolOr(BoolOr(IsTypedArray(obj), IsModuleNamespace(obj)), ObjIsSpecialContainer(obj));
}
GateRef StubBuilder::IsSlowKeysObject(GateRef obj)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::BOOL(), False());
Label isHeapObject(env);
Branch(TaggedIsHeapObject(obj), &isHeapObject, &exit);
Bind(&isHeapObject);
{
result = BoolOr(BoolOr(IsJSGlobalObject(obj), IsJsProxy(obj)), IsSpecialKeysObject(obj));
Jump(&exit);
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef StubBuilder::GetNumberOfElements(GateRef obj)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(numOfElements, VariableType::INT32(), Int32(0));
DEFVARIABLE(i, VariableType::INT32(), Int32(0));
Label isJSPrimitiveRef(env);
Label isPrimitiveString(env);
Label notPrimitiveString(env);
Label isDictMode(env);
Label notDictMode(env);
Branch(IsJSPrimitiveRef(obj), &isJSPrimitiveRef, &notPrimitiveString);
Bind(&isJSPrimitiveRef);
GateRef value = Load(VariableType::JS_ANY(), obj, IntPtr(JSPrimitiveRef::VALUE_OFFSET));
Branch(TaggedIsString(value), &isPrimitiveString, &notPrimitiveString);
Bind(&isPrimitiveString);
{
numOfElements = GetLengthFromString(value);
Jump(&notPrimitiveString);
}
Bind(&notPrimitiveString);
GateRef elements = GetElementsArray(obj);
Branch(IsDictionaryMode(elements), &isDictMode, &notDictMode);
Bind(&notDictMode);
{
Label loopHead(env);
Label loopEnd(env);
Label iLessLength(env);
Label notHole(env);
GateRef elementsLen = GetLengthOfTaggedArray(elements);
Jump(&loopHead);
LoopBegin(&loopHead);
{
Branch(Int32UnsignedLessThan(*i, elementsLen), &iLessLength, &exit);
Bind(&iLessLength);
{
GateRef element = GetValueFromTaggedArray(elements, *i);
Branch(TaggedIsHole(element), &loopEnd, &notHole);
Bind(&notHole);
numOfElements = Int32Add(*numOfElements, Int32(1));
Jump(&loopEnd);
}
Bind(&loopEnd);
i = Int32Add(*i, Int32(1));
LoopEnd(&loopHead);
}
}
Bind(&isDictMode);
{
GateRef entryCount = TaggedGetInt(
GetValueFromTaggedArray(elements, Int32(TaggedHashTable<NumberDictionary>::NUMBER_OF_ENTRIES_INDEX)));
numOfElements = Int32Add(*numOfElements, entryCount);
Jump(&exit);
}
Bind(&exit);
auto ret = *numOfElements;
env->SubCfgExit();
return ret;
}
GateRef StubBuilder::IsSimpleEnumCacheValid(GateRef obj)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::BOOL(), False());
DEFVARIABLE(current, VariableType::JS_ANY(), Undefined());
Label receiverHasNoElements(env);
GateRef numOfElements = GetNumberOfElements(obj);
Branch(Int32GreaterThan(numOfElements, Int32(0)), &exit, &receiverHasNoElements);
Bind(&receiverHasNoElements);
{
Label loopHead(env);
Label loopEnd(env);
Label afterLoop(env);
Label currentHasNoElements(env);
Label enumCacheIsUndefined(env);
current = GetPrototypeFromHClass(LoadHClass(obj));
Branch(TaggedIsHeapObject(*current), &loopHead, &afterLoop);
LoopBegin(&loopHead);
{
GateRef numOfCurrentElements = GetNumberOfElements(*current);
Branch(Int32GreaterThan(numOfCurrentElements, Int32(0)), &exit, &currentHasNoElements);
Bind(&currentHasNoElements);
GateRef hclass = LoadHClass(*current);
GateRef protoEnumCache = GetEnumCacheFromHClass(hclass);
Branch(TaggedIsUndefined(protoEnumCache), &enumCacheIsUndefined, &exit);
Bind(&enumCacheIsUndefined);
current = GetPrototypeFromHClass(hclass);
Branch(TaggedIsHeapObject(*current), &loopEnd, &afterLoop);
}
Bind(&loopEnd);
LoopEnd(&loopHead);
Bind(&afterLoop);
{
result = True();
Jump(&exit);
}
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef StubBuilder::IsEnumCacheWithProtoChainInfoValid(GateRef obj)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::BOOL(), False());
DEFVARIABLE(current, VariableType::JS_ANY(), Undefined());
Label receiverHasNoElements(env);
Label prototypeIsEcmaObj(env);
Label isProtoChangeMarker(env);
Label protoNotChanged(env);
GateRef numOfElements = GetNumberOfElements(obj);
Branch(Int32GreaterThan(numOfElements, Int32(0)), &exit, &receiverHasNoElements);
Bind(&receiverHasNoElements);
GateRef prototype = GetPrototypeFromHClass(LoadHClass(obj));
Branch(IsEcmaObject(prototype), &prototypeIsEcmaObj, &exit);
Bind(&prototypeIsEcmaObj);
GateRef protoChangeMarker = GetProtoChangeMarkerFromHClass(LoadHClass(prototype));
Branch(TaggedIsProtoChangeMarker(protoChangeMarker), &isProtoChangeMarker, &exit);
Bind(&isProtoChangeMarker);
Branch(GetHasChanged(protoChangeMarker), &exit, &protoNotChanged);
Bind(&protoNotChanged);
{
Label loopHead(env);
Label loopEnd(env);
Label afterLoop(env);
Label currentHasNoElements(env);
current = prototype;
Branch(TaggedIsHeapObject(*current), &loopHead, &afterLoop);
LoopBegin(&loopHead);
{
GateRef numOfCurrentElements = GetNumberOfElements(*current);
Branch(Int32GreaterThan(numOfCurrentElements, Int32(0)), &exit, &currentHasNoElements);
Bind(&currentHasNoElements);
current = GetPrototypeFromHClass(LoadHClass(*current));
Branch(TaggedIsHeapObject(*current), &loopEnd, &afterLoop);
}
Bind(&loopEnd);
LoopEnd(&loopHead);
Bind(&afterLoop);
{
result = True();
Jump(&exit);
}
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef StubBuilder::TryGetEnumCache(GateRef glue, GateRef obj)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit(env);
DEFVARIABLE(result, VariableType::JS_ANY(), Undefined());
Label notSlowKeys(env);
Label notDictionaryMode(env);
Label checkSimpleEnumCache(env);
Label notSimpleEnumCache(env);
Label checkEnumCacheWithProtoChainInfo(env);
Label enumCacheValid(env);
Branch(IsSlowKeysObject(obj), &exit, &notSlowKeys);
Bind(&notSlowKeys);
GateRef hclass = LoadHClass(obj);
Branch(IsDictionaryModeByHClass(hclass), &exit, &notDictionaryMode);
Bind(&notDictionaryMode);
GateRef enumCache = GetEnumCacheFromHClass(hclass);
GateRef kind = GetEnumCacheKind(glue, enumCache);
Branch(Int32Equal(kind, Int32(static_cast<int32_t>(EnumCacheKind::SIMPLE))),
&checkSimpleEnumCache, &notSimpleEnumCache);
Bind(&checkSimpleEnumCache);
{
Branch(IsSimpleEnumCacheValid(obj), &enumCacheValid, &exit);
}
Bind(&notSimpleEnumCache);
Branch(Int32Equal(kind, Int32(static_cast<int32_t>(EnumCacheKind::PROTOCHAIN))),
&checkEnumCacheWithProtoChainInfo, &exit);
Bind(&checkEnumCacheWithProtoChainInfo);
{
Branch(IsEnumCacheWithProtoChainInfoValid(obj), &enumCacheValid, &exit);
}
Bind(&enumCacheValid);
{
result = enumCache;
Jump(&exit);
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef StubBuilder::DoubleToInt(GateRef glue, GateRef x, size_t typeBits)
{
auto env = GetEnvironment();

View File

@ -222,6 +222,7 @@ public:
GateRef BothAreString(GateRef x, GateRef y);
GateRef TaggedIsStringOrSymbol(GateRef obj);
GateRef TaggedIsSymbol(GateRef obj);
GateRef TaggedIsProtoChangeMarker(GateRef obj);
GateRef GetNextPositionForHash(GateRef last, GateRef count, GateRef size);
GateRef DoubleIsNAN(GateRef x);
GateRef DoubleIsINF(GateRef x);
@ -318,6 +319,10 @@ public:
GateRef TaggedIsPropertyBox(GateRef obj);
GateRef TaggedObjectIsBigInt(GateRef obj);
GateRef IsJsProxy(GateRef obj);
GateRef IsJSGlobalObject(GateRef obj);
GateRef IsModuleNamespace(GateRef obj);
GateRef ObjIsSpecialContainer(GateRef obj);
GateRef IsJSPrimitiveRef(GateRef obj);
GateRef IsJSFunctionBase(GateRef obj);
GateRef IsConstructor(GateRef object);
GateRef IsBase(GateRef func);
@ -392,6 +397,8 @@ public:
// SetDictionaryOrder func in property_attribute.h
GateRef SetDictionaryOrderFieldInPropAttr(GateRef attr, GateRef value);
GateRef GetPrototypeFromHClass(GateRef hClass);
GateRef GetEnumCacheFromHClass(GateRef hClass);
GateRef GetProtoChangeMarkerFromHClass(GateRef hClass);
GateRef GetLayoutFromHClass(GateRef hClass);
GateRef GetBitFieldFromHClass(GateRef hClass);
GateRef GetLengthFromString(GateRef value);
@ -420,6 +427,7 @@ public:
void IncNumberOfProps(GateRef glue, GateRef hClass);
GateRef GetNumberOfPropsFromHClass(GateRef hClass);
GateRef HasDeleteProperty(GateRef hClass);
GateRef IsTSHClass(GateRef hClass);
void SetNumberOfPropsToHClass(GateRef glue, GateRef hClass, GateRef value);
void SetElementsKindToTrackInfo(GateRef glue, GateRef trackInfo, GateRef elementsKind);
@ -534,7 +542,7 @@ public:
GateRef TruncInt64ToInt1(GateRef x);
GateRef TruncInt32ToInt1(GateRef x);
GateRef GetGlobalConstantAddr(GateRef index);
GateRef GetGlobalConstantString(ConstantIndex index);
GateRef GetGlobalConstantOffset(ConstantIndex index);
GateRef IsCallableFromBitField(GateRef bitfield);
GateRef IsCallable(GateRef obj);
GateRef GetOffsetFieldInPropAttr(GateRef attr);
@ -611,6 +619,34 @@ public:
GateRef GetContainerProperty(GateRef glue, GateRef receiver, GateRef index, GateRef jsType);
GateRef JSAPIContainerGet(GateRef glue, GateRef receiver, GateRef index);
// for-in
GateRef NextInternal(GateRef glue, GateRef iter);
GateRef GetLengthFromForInIterator(GateRef iter);
GateRef GetIndexFromForInIterator(GateRef iter);
GateRef GetKeysFromForInIterator(GateRef iter);
GateRef GetObjectFromForInIterator(GateRef iter);
GateRef GetCachedHclassFromForInIterator(GateRef iter);
void SetLengthOfForInIterator(GateRef glue, GateRef iter, GateRef length);
void SetIndexOfForInIterator(GateRef glue, GateRef iter, GateRef index);
void SetKeysOfForInIterator(GateRef glue, GateRef iter, GateRef keys);
void SetObjectOfForInIterator(GateRef glue, GateRef iter, GateRef object);
void SetCachedHclassOFForInIterator(GateRef glue, GateRef iter, GateRef hclass);
void IncreaseInteratorIndex(GateRef glue, GateRef iter, GateRef index);
GateRef GetEnumCacheKind(GateRef glue, GateRef enumCache);
GateRef GetEmptyArray(GateRef glue);
GateRef IsEnumCacheValid(GateRef receiver, GateRef cachedHclass, GateRef kind);
GateRef NeedCheckProperty(GateRef receiver);
GateRef EnumerateObjectProperties(GateRef glue, GateRef obj);
GateRef GetFunctionPrototype(GateRef glue, size_t index);
GateRef ToPrototypeOrObj(GateRef glue, GateRef obj);
GateRef IsSpecialKeysObject(GateRef obj);
GateRef IsSlowKeysObject(GateRef obj);
GateRef TryGetEnumCache(GateRef glue, GateRef obj);
GateRef GetNumberOfElements(GateRef obj);
GateRef IsSimpleEnumCacheValid(GateRef obj);
GateRef IsEnumCacheWithProtoChainInfoValid(GateRef obj);
// Exception handle
GateRef HasPendingException(GateRef glue);
void ReturnExceptionIfAbruptCompletion(GateRef glue);

View File

@ -1641,7 +1641,7 @@ void TypeMCRLowering::LowerTypeOf(GateRef gate, GateRef glue)
UNREACHABLE();
}
GateRef result = builder_.Load(VariableType::JS_POINTER(), gConstAddr, builder_.GetGlobalConstantString(index));
GateRef result = builder_.Load(VariableType::JS_POINTER(), gConstAddr, builder_.GetGlobalConstantOffset(index));
acc_.ReplaceGate(gate, builder_.GetState(), builder_.GetDepend(), result);
}
} // namespace panda::ecmascript::kungfu

View File

@ -1871,14 +1871,15 @@ void JSForInIterator::Dump(std::ostream &os) const
{
os << " - Object : ";
GetObject().DumpTaggedValue(os);
os << " - CachedHclass : ";
GetCachedHclass().DumpTaggedValue(os);
os << "\n";
os << " - WasVisited : " << GetWasVisited();
os << " - Keys : ";
GetKeys().DumpTaggedValue(os);
os << "\n";
os << " - VisitedObjs : ";
GetVisitedObjs().DumpTaggedValue(os);
os << " - Index : " << GetIndex();
os << "\n";
os << " - RemainingKeys : ";
GetRemainingKeys().DumpTaggedValue(os);
os << " - Length : " << GetLength();
os << "\n";
JSObject::Dump(os);
}
@ -4557,9 +4558,10 @@ void JSMap::DumpForSnapshot(std::vector<Reference> &vec) const
void JSForInIterator::DumpForSnapshot(std::vector<Reference> &vec) const
{
vec.emplace_back(CString("Object"), GetObject());
vec.emplace_back(CString("WasVisited"), JSTaggedValue(GetWasVisited()));
vec.emplace_back(CString("VisitedObjs"), GetVisitedObjs());
vec.emplace_back(CString("RemainingKeys"), GetRemainingKeys());
vec.emplace_back(CString("CachedHclass"), GetCachedHclass());
vec.emplace_back(CString("Keys"), GetKeys());
vec.emplace_back(CString("Index"), JSTaggedValue(GetIndex()));
vec.emplace_back(CString("Length"), JSTaggedValue(GetLength()));
JSObject::DumpForSnapshot(vec);
}

View File

@ -552,6 +552,9 @@ bool EcmaString::StringsAreEqual(const EcmaVM *vm, const JSHandle<EcmaString> &s
if (str1 == str2) {
return true;
}
if (str1->IsInternString() && str2->IsInternString()) {
return false;
}
uint32_t str1Len = str1->GetLength();
if (str1Len != str2->GetLength()) {
return false;

View File

@ -155,6 +155,25 @@ void GlobalDictionary::GetAllKeysByFilter(const JSThread *thread,
}
}
std::pair<uint32_t, uint32_t> GlobalDictionary::GetNumOfEnumKeys() const
{
uint32_t enumKeys = 0;
uint32_t shadowKeys = 0;
int size = Size();
for (int hashIndex = 0; hashIndex < size; hashIndex++) {
JSTaggedValue key = GetKey(hashIndex);
if (key.IsString()) {
PropertyAttributes attr = GetAttributes(hashIndex);
if (attr.IsEnumerable()) {
enumKeys++;
} else {
shadowKeys++;
}
}
}
return std::make_pair(enumKeys, shadowKeys);
}
void GlobalDictionary::GetEnumAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray,
uint32_t *keys) const
{

View File

@ -81,6 +81,8 @@ public:
inline void GetEnumAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray, uint32_t *keys) const;
inline std::pair<uint32_t, uint32_t> GetNumOfEnumKeys() const;
static bool inline CompKey(const std::pair<JSTaggedValue, uint32_t> &a,
const std::pair<JSTaggedValue, uint32_t> &b);

View File

@ -16,138 +16,123 @@
#include "ecmascript/js_for_in_iterator.h"
#include "ecmascript/base/builtins_base.h"
#include "ecmascript/global_dictionary-inl.h"
#include "ecmascript/global_env.h"
#include "ecmascript/ic/proto_change_details.h"
#include "ecmascript/js_object-inl.h"
#include "ecmascript/js_primitive_ref.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tagged_array-inl.h"
#include "ecmascript/tagged_dictionary.h"
#include "ecmascript/tagged_queue.h"
#include "ecmascript/tagged_queue.h"
namespace panda::ecmascript {
using BuiltinsBase = base::BuiltinsBase;
bool JSForInIterator::CheckObjProto(const JSThread *thread, const JSHandle<JSForInIterator> &it)
bool JSForInIterator::IsEnumCacheValid(JSTaggedValue receiver, JSTaggedValue cachedHclass, EnumCacheKind kind)
{
JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
JSHandle<JSTaggedValue> object(thread, it->GetObject());
if (!object->IsJSObject()) {
DISALLOW_GARBAGE_COLLECTION;
JSHClass *hclass = receiver.GetTaggedObject()->GetClass();
if (JSTaggedValue(hclass) != cachedHclass) {
return false;
}
auto *hclass = object->GetTaggedObject()->GetClass();
JSType jsType = hclass->GetObjectType();
if (jsType != JSType::JS_OBJECT) {
return false;
if (kind == EnumCacheKind::SIMPLE) {
return true;
}
ASSERT(kind == EnumCacheKind::PROTOCHAIN);
JSTaggedValue proto = hclass->GetPrototype();
if (!proto.IsJSObject()) {
if (!proto.IsECMAObject()) {
return false;
}
return hclass->GetPrototype().GetTaggedObject()->GetClass() ==
env->GetObjectFunctionPrototypeClass().GetTaggedValue().GetTaggedObject()->GetClass();
JSTaggedValue protoChangeMarker = proto.GetTaggedObject()->GetClass()->GetProtoChangeMarker();
if (protoChangeMarker.IsProtoChangeMarker()) {
if (!ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject())->GetHasChanged()) {
return true;
}
}
return false;
}
void JSForInIterator::GetAllEnumKeys(JSThread *thread, const JSHandle<JSForInIterator> &it,
const JSHandle<JSTaggedValue> &object)
bool JSForInIterator::NeedCheckProperty(JSTaggedValue receiver)
{
ASSERT(object->IsHeapObject());
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
JSMutableHandle<TaggedQueue> remaining(thread, thread->GlobalConstants()->GetEmptyTaggedQueue());
if (object->IsJSProxy()) {
JSHandle<TaggedArray> proxyArr = JSProxy::OwnPropertyKeys(thread, JSHandle<JSProxy>(object));
RETURN_IF_ABRUPT_COMPLETION(thread);
uint32_t length = proxyArr->GetLength();
for (uint32_t i = 0; i < length; i++) {
value.Update(proxyArr->Get(i));
PropertyDescriptor desc(thread);
JSProxy::GetOwnProperty(thread, JSHandle<JSProxy>(object), value, desc);
RETURN_IF_ABRUPT_COMPLETION(thread);
if (desc.IsEnumerable()) {
TaggedQueue *newQueue = TaggedQueue::Push(thread, remaining, value);
remaining.Update(JSTaggedValue(newQueue));
}
}
} else {
JSHandle<TaggedArray> arr = JSTaggedValue::GetOwnEnumPropertyKeys(thread, object);
uint32_t len = arr->GetLength();
for (uint32_t i = 0; i < len; i++) {
value.Update(arr->Get(i));
if (value->IsString()) {
TaggedQueue *newQueue = TaggedQueue::Push(thread, remaining, value);
remaining.Update(JSTaggedValue(newQueue));
}
DISALLOW_GARBAGE_COLLECTION;
JSTaggedValue current = receiver;
while (current.IsHeapObject()) {
if (!current.IsJSObject() || current.GetTaggedObject()->GetClass()->HasDeleteProperty()) {
return true;
}
current = JSObject::GetPrototype(current);
}
if (it->GetHasVisitObjs()) {
JSMutableHandle<TaggedQueue> remained(thread, thread->GlobalConstants()->GetEmptyTaggedQueue());
JSMutableHandle<TaggedQueue> visited(thread, it->GetVisitedObjs());
uint32_t size = visited->Size();
while (!remaining->Empty()) {
JSHandle<JSTaggedValue> key(thread, remaining->Pop(thread));
bool has = false;
for (uint32_t i = 0; i < size; i++) {
value.Update(visited->Get(i));
PropertyDescriptor desc(thread);
has = JSTaggedValue::GetOwnProperty(thread, value, key, desc);
RETURN_IF_ABRUPT_COMPLETION(thread);
if (has) {
break;
}
}
if (!has) {
TaggedQueue *newQueue = TaggedQueue::Push(thread, remained, key);
remained.Update(JSTaggedValue(newQueue));
}
}
it->SetRemainingKeys(thread, remained);
} else {
it->SetRemainingKeys(thread, remaining);
}
it->SetWasVisited(true);
object->GetTaggedObject()->GetClass()->SetHasDeleteProperty(false);
return false;
}
std::pair<JSTaggedValue, bool> JSForInIterator::NextInternal(JSThread *thread, const JSHandle<JSForInIterator> &it)
bool JSForInIterator::HasProperty(JSThread *thread, JSHandle<JSTaggedValue> receiver, JSHandle<JSTaggedValue> key)
{
while (true) {
JSHandle<JSTaggedValue> object(thread, it->GetObject());
if (object->IsNull() || object->IsUndefined()) {
return std::make_pair(JSTaggedValue::Undefined(), true);
JSMutableHandle<JSTaggedValue> current(thread, receiver.GetTaggedValue());
while (current->IsHeapObject()) {
PropertyDescriptor desc(thread);
bool has = JSTaggedValue::GetOwnProperty(thread, current, key, desc);
if (has && desc.IsEnumerable()) {
return true;
}
if (!it->GetWasVisited()) {
GetAllEnumKeys(thread, it, object);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, std::make_pair(JSTaggedValue::Exception(), false));
}
JSHandle<TaggedQueue> remaining(thread, it->GetRemainingKeys());
while (!remaining->Empty()) {
ASSERT(object->IsHeapObject());
JSTaggedValue r = remaining->Pop(thread);
bool hasDelete = object->GetTaggedObject()->GetClass()->HasDeleteProperty();
if (object->IsJSObject() && !hasDelete) {
return std::make_pair(r, false);
}
JSHandle<JSTaggedValue> key(thread, r);
PropertyDescriptor desc(thread);
bool has = JSTaggedValue::GetOwnProperty(thread, object, key, desc);
if (has) {
if (desc.IsEnumerable()) {
return std::make_pair(key.GetTaggedValue(), false);
}
}
}
JSMutableHandle<TaggedQueue> visited(thread, it->GetVisitedObjs());
TaggedQueue *newQueue = TaggedQueue::Push(thread, visited, object);
visited.Update(JSTaggedValue(newQueue));
it->SetVisitedObjs(thread, visited);
JSTaggedValue proto = JSTaggedValue::GetPrototype(thread, object);
RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, std::make_pair(JSTaggedValue::Exception(), false));
it->SetObject(thread, proto);
it->SetWasVisited(false);
it->SetHasVisitObjs(true);
current.Update(JSTaggedValue::GetPrototype(thread, current));
}
return false;
}
JSTaggedValue JSForInIterator::NextInternal(JSThread *thread, const JSHandle<JSForInIterator> &it)
{
uint32_t length = it->GetLength();
uint32_t index = it->GetIndex();
if (index >= length) {
return JSTaggedValue::Undefined();
}
JSTaggedValue taggedKeys = it->GetKeys();
JSTaggedValue receiver = it->GetObject();
EnumCacheKind kind = JSObject::GetEnumCacheKind(thread, taggedKeys);
TaggedArray *keys = TaggedArray::Cast(taggedKeys.GetTaggedObject());
if (IsEnumCacheValid(receiver, it->GetCachedHclass(), kind)) {
JSTaggedValue key = keys->Get(index);
index++;
it->SetIndex(index);
return key;
}
if (!NeedCheckProperty(receiver)) {
JSTaggedValue key = keys->Get(index);
index++;
it->SetIndex(index);
return key;
}
// slow path
return NextInternalSlowpath(thread, it);
}
JSTaggedValue JSForInIterator::NextInternalSlowpath(JSThread *thread, const JSHandle<JSForInIterator> &it)
{
uint32_t length = it->GetLength();
uint32_t index = it->GetIndex();
if (index >= length) {
return JSTaggedValue::Undefined();
}
JSHandle<TaggedArray> keysHandle(thread, it->GetKeys());
JSHandle<JSTaggedValue> receiverHandle(thread, it->GetObject());
JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
bool has = false;
while (index < length) {
keyHandle.Update(keysHandle->Get(index));
if (keyHandle->IsUndefined()) {
has = false;
break;
}
has = HasProperty(thread, receiverHandle, keyHandle);
if (has) {
break;
}
index++;
}
if (!has) {
it->SetIndex(index);
return JSTaggedValue::Undefined();
}
JSTaggedValue key = keysHandle->Get(index);
index++;
it->SetIndex(index);
return key;
}
// 13.7.5.16.2.1 %ForInIteratorPrototype%.next ( )
@ -158,8 +143,8 @@ JSTaggedValue JSForInIterator::Next(EcmaRuntimeCallInfo *msg)
[[maybe_unused]] EcmaHandleScope handleScope(thread);
// 1. Let O be the this value.
JSHandle<JSForInIterator> it(BuiltinsBase::GetThis(msg));
ASSERT(it->IsForinIterator());
std::pair<JSTaggedValue, bool> res = NextInternal(thread, it);
return res.first;
ASSERT(JSHandle<JSTaggedValue>(it)->IsForinIterator());
JSTaggedValue res = NextInternal(thread, it);
return res;
}
} // namespace panda::ecmascript

View File

@ -27,29 +27,27 @@ class JSForInIterator : public JSObject {
public:
CAST_CHECK(JSForInIterator, IsForinIterator);
static std::pair<JSTaggedValue, bool> NextInternal(JSThread *thread, const JSHandle<JSForInIterator> &it);
static JSTaggedValue NextInternal(JSThread *thread, const JSHandle<JSForInIterator> &it);
static JSTaggedValue NextInternalSlowpath(JSThread *thread, const JSHandle<JSForInIterator> &it);
static JSTaggedValue Next(EcmaRuntimeCallInfo *msg);
static bool CheckObjProto(const JSThread *thread, const JSHandle<JSForInIterator> &it);
static void GetAllEnumKeys(JSThread *thread, const JSHandle<JSForInIterator> &it,
const JSHandle<JSTaggedValue> &object);
static constexpr size_t OBJECT_OFFSET = JSObject::SIZE;
ACCESSORS(Object, OBJECT_OFFSET, VISITED_KEYS_OFFSET)
ACCESSORS(VisitedObjs, VISITED_KEYS_OFFSET, REMAINING_KEYS_OFFSET)
ACCESSORS(RemainingKeys, REMAINING_KEYS_OFFSET, BIT_FIELD_OFFSET)
ACCESSORS_BIT_FIELD(BitField, BIT_FIELD_OFFSET, LAST_OFFSET)
ACCESSORS(Object, OBJECT_OFFSET, CACHED_HCLASS_OFFSET)
ACCESSORS(CachedHclass, CACHED_HCLASS_OFFSET, KEYS_OFFSET)
ACCESSORS(Keys, KEYS_OFFSET, INDEX_OFFSET)
ACCESSORS_PRIMITIVE_FIELD(Index, uint32_t, INDEX_OFFSET, LENGTH_OFFSET)
ACCESSORS_PRIMITIVE_FIELD(Length, uint32_t, LENGTH_OFFSET, LAST_OFFSET)
DEFINE_ALIGN_SIZE(LAST_OFFSET);
// define BitField
static constexpr size_t WAS_VISITED_BITS = 3;
FIRST_BIT_FIELD(BitField, WasVisited, bool, WAS_VISITED_BITS)
NEXT_BIT_FIELD(BitField, HasVisitObjs, bool, 1, WasVisited)
DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, OBJECT_OFFSET, BIT_FIELD_OFFSET)
DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, OBJECT_OFFSET, INDEX_OFFSET)
DECL_DUMP()
private:
static bool IsEnumCacheValid(JSTaggedValue receiver, JSTaggedValue cachedHclass, EnumCacheKind kind);
static bool NeedCheckProperty(JSTaggedValue receiver);
static bool HasProperty(JSThread *thread, JSHandle<JSTaggedValue> receiver, JSHandle<JSTaggedValue> key);
};
} // namespace panda::ecmascript

View File

@ -298,6 +298,35 @@ enum class JSType : uint8_t {
JSTYPE_DECL,
};
// EnumCache:
// +-----------------+----------------------+
// | value | status |
// +-----------------+----------------------+
// | null | uninitialized |
// ------------------------------------------
// | undefined | a fast path to check |
// | | simple enum cache |
// ------------------------------------------
// | empty array | enum keys is empty |
// ------------------------------------------
// | non-empty array | non-empty enum keys |
// +----------------------------------------+
// structure of non-empty array of EnumCache:
// 0: an int value indicating enum cache kind
// 1-n: enum keys
namespace EnumCache {
static constexpr uint32_t ENUM_CACHE_HEADER_SIZE = 1;
static constexpr uint32_t ENUM_CACHE_KIND_OFFSET = 0;
enum class EnumCacheKind : uint8_t {
NONE = 0,
SIMPLE, // simple enum cache(used in for-in)
// make sure EnumCache is empty array only for SIMPLE
PROTOCHAIN, // enum cache with prototype chain info(used in for-in)
ONLY_OWN_KEYS // enum cache with only own enum keys(used in Json.stringify and Object.keys)
};
} // namespace EnumCache
class JSHClass : public TaggedObject {
public:
static constexpr int TYPE_BITFIELD_NUM = 8;

View File

@ -23,6 +23,7 @@
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/js_typed_array.h"
#include "ecmascript/tagged_array-inl.h"
#include "ecmascript/tagged_queue.h"
namespace panda::ecmascript {
inline void ECMAObject::SetCallable(bool flag)
@ -452,16 +453,77 @@ inline uint32_t JSObject::SetValuesOrEntries(JSThread *thread, const JSHandle<Ta
return index;
}
inline void JSObject::SetEnumCacheKind(JSThread *thread, TaggedArray *array, EnumCacheKind kind)
{
array->Set(thread, EnumCache::ENUM_CACHE_KIND_OFFSET, JSTaggedValue(static_cast<uint8_t>(kind)));
}
inline EnumCacheKind JSObject::GetEnumCacheKind(JSThread *thread, TaggedArray *array)
{
return static_cast<EnumCacheKind>(array->Get(thread, EnumCache::ENUM_CACHE_KIND_OFFSET).GetInt());
}
inline EnumCacheKind JSObject::GetEnumCacheKind(JSThread *thread, JSTaggedValue enumCache)
{
if (enumCache.IsUndefinedOrNull()) {
return EnumCacheKind::NONE;
}
JSTaggedValue emptyArray = thread->GlobalConstants()->GetEmptyArray();
if (enumCache == emptyArray) {
return EnumCacheKind::SIMPLE;
}
TaggedArray *array = TaggedArray::Cast(enumCache.GetTaggedObject());
return JSObject::GetEnumCacheKind(thread, array);
}
inline JSTaggedValue JSObject::GetPrototype(JSTaggedValue obj)
{
JSHClass *hclass = obj.GetTaggedObject()->GetClass();
return hclass->GetPrototype();
}
inline bool JSObject::IsDepulicateKeys(JSThread *thread, JSHandle<TaggedArray> keys, int32_t lastLength,
JSHandle<TaggedQueue> shadowQueue, JSHandle<JSTaggedValue> key)
{
if (lastLength < 0) {
return false;
}
JSMutableHandle<JSTaggedValue> value(thread, JSTaggedValue::Undefined());
for (int32_t i = EnumCache::ENUM_CACHE_HEADER_SIZE; i < lastLength; i++) {
value.Update(keys->Get(i));
bool has = JSTaggedValue::Equal(thread, value, key);
if (has) {
return true;
}
}
uint32_t shadowSize = shadowQueue->Size();
for (uint32_t i = 0; i < shadowSize; i++) {
value.Update(shadowQueue->Get(i));
bool has = JSTaggedValue::Equal(thread, value, key);
if (has) {
return true;
}
}
return false;
}
inline void JSObject::ClearHasDeleteProperty(JSHandle<JSTaggedValue> object)
{
object->GetTaggedObject()->GetClass()->SetHasDeleteProperty(false);
}
inline std::pair<JSHandle<TaggedArray>, JSHandle<TaggedArray>> JSObject::GetOwnEnumerableNamesInFastMode(
JSThread *thread, const JSHandle<JSObject> &obj, uint32_t *copyLengthOfKeys, uint32_t *copyLengthOfElements)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
uint32_t numOfKeys = obj->GetNumberOfKeys();
std::pair<uint32_t, uint32_t> numOfKeys = obj->GetNumberOfEnumKeys();
uint32_t numOfEnumKeys = numOfKeys.first;
uint32_t numOfElements = obj->GetNumberOfElements();
JSHandle<TaggedArray> elementArray = numOfElements > 0 ? JSObject::GetEnumElementKeys(
thread, obj, 0, numOfElements, copyLengthOfElements) : factory->EmptyArray();
JSHandle<TaggedArray> keyArray = numOfKeys > 0 ? JSObject::GetAllEnumKeys(
thread, obj, 0, numOfKeys, copyLengthOfKeys) : factory->EmptyArray();
JSHandle<TaggedArray> keyArray = numOfEnumKeys > 0 ? JSObject::GetAllEnumKeys(
thread, obj, numOfEnumKeys, copyLengthOfKeys) : factory->EmptyArray();
return std::make_pair(keyArray, elementArray);
}

View File

@ -21,6 +21,7 @@
#include "ecmascript/filter_helper.h"
#include "ecmascript/global_dictionary-inl.h"
#include "ecmascript/global_env.h"
#include "ecmascript/ic/proto_change_details.h"
#include "ecmascript/js_for_in_iterator.h"
#include "ecmascript/js_hclass.h"
#include "ecmascript/js_iterator.h"
@ -29,6 +30,7 @@
#include "ecmascript/object_factory-inl.h"
#include "ecmascript/object_fast_operator-inl.h"
#include "ecmascript/pgo_profiler/pgo_profiler.h"
#include "ecmascript/property_accessor.h"
#include "ecmascript/property_attributes.h"
#include "ecmascript/tagged_array-inl.h"
@ -300,12 +302,6 @@ void JSObject::DeletePropertyInternal(JSThread *thread, const JSHandle<JSObject>
}
if (!array->IsDictionaryMode()) {
JSHClass *hclass = obj->GetJSHClass();
// To maintain TS inherit info, not change hclass, just set hole.
if (hclass->HasTSSubtyping() && !hclass->IsPrototype()) {
obj->SetPropertyInlinedProps(thread, index, JSTaggedValue::Hole());
return;
}
JSHandle<NameDictionary> dictHandle(TransitionToDictionary(thread, obj));
int entry = dictHandle->FindEntry(key.GetTaggedValue());
ASSERT(entry != -1);
@ -384,14 +380,14 @@ void JSObject::GetAllKeysForSerialization(const JSHandle<JSObject> &obj, std::ve
}
}
JSHandle<TaggedArray> JSObject::GetAllEnumKeys(const JSThread *thread, const JSHandle<JSObject> &obj, int offset,
JSHandle<TaggedArray> JSObject::GetAllEnumKeys(JSThread *thread, const JSHandle<JSObject> &obj,
uint32_t numOfKeys, uint32_t *keys)
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
if (obj->IsJSGlobalObject()) {
JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(numOfKeys);
GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject());
dict->GetEnumAllKeys(thread, offset, *keyArray, keys);
dict->GetEnumAllKeys(thread, 0, *keyArray, keys);
return keyArray;
}
@ -399,34 +395,34 @@ JSHandle<TaggedArray> JSObject::GetAllEnumKeys(const JSThread *thread, const JSH
if (!array->IsDictionaryMode()) {
JSHClass *jsHclass = obj->GetJSHClass();
JSTaggedValue enumCache = jsHclass->GetEnumCache();
if (!enumCache.IsNull()) {
if (JSObject::GetEnumCacheKind(thread, enumCache) == EnumCacheKind::ONLY_OWN_KEYS) {
JSHandle<TaggedArray> cacheArray = JSHandle<TaggedArray>(thread, enumCache);
*keys = cacheArray->GetLength();
JSHandle<TaggedArray> keyArray = factory->CopyArray(cacheArray, *keys, *keys);
JSHandle<TaggedArray> keyArray = factory->CopyFromEnumCache(cacheArray);
*keys = keyArray->GetLength();
return keyArray;
}
JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(numOfKeys);
int end = static_cast<int>(jsHclass->NumberOfProps());
if (end > 0) {
if (numOfKeys > 0) {
int end = static_cast<int>(jsHclass->NumberOfProps());
JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(numOfKeys + EnumCache::ENUM_CACHE_HEADER_SIZE);
LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject())
->GetAllEnumKeys(thread, end, offset, *keyArray, keys, obj);
if (*keys == keyArray->GetLength()) {
jsHclass->SetEnumCache(thread, keyArray.GetTaggedValue());
JSHandle<TaggedArray> newkeyArray = factory->CopyArray(keyArray, *keys, *keys);
return newkeyArray;
}
->GetAllEnumKeys(thread, end, EnumCache::ENUM_CACHE_HEADER_SIZE, keyArray, keys, obj);
JSObject::SetEnumCacheKind(thread, *keyArray, EnumCacheKind::ONLY_OWN_KEYS);
jsHclass->SetEnumCache(thread, keyArray.GetTaggedValue());
JSHandle<TaggedArray> newkeyArray = factory->CopyFromEnumCache(keyArray);
return newkeyArray;
}
return keyArray;
return factory->EmptyArray();
}
JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(numOfKeys);
NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject());
dict->GetAllEnumKeys(thread, offset, *keyArray, keys);
dict->GetAllEnumKeys(thread, 0, keyArray, keys);
return keyArray;
}
void JSObject::GetAllEnumKeys(const JSThread *thread, const JSHandle<JSObject> &obj, int offset,
const JSHandle<TaggedArray> &keyArray)
uint32_t JSObject::GetAllEnumKeys(JSThread *thread, const JSHandle<JSObject> &obj, int offset,
const JSHandle<TaggedArray> &keyArray)
{
TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
uint32_t keys = 0;
@ -435,18 +431,19 @@ void JSObject::GetAllEnumKeys(const JSThread *thread, const JSHandle<JSObject> &
int end = static_cast<int>(jsHclass->NumberOfProps());
if (end > 0) {
LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject())
->GetAllEnumKeys(thread, end, offset, *keyArray, &keys, obj);
->GetAllEnumKeys(thread, end, offset, keyArray, &keys, obj);
}
return;
return keys;
}
if (obj->IsJSGlobalObject()) {
GlobalDictionary *dict = GlobalDictionary::Cast(obj->GetProperties().GetTaggedObject());
dict->GetEnumAllKeys(thread, offset, *keyArray, &keys);
return;
return keys;
}
NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject());
dict->GetAllEnumKeys(thread, offset, *keyArray, &keys);
dict->GetAllEnumKeys(thread, offset, keyArray, &keys);
return keys;
}
void JSObject::GetAllElementKeys(JSThread *thread, const JSHandle<JSObject> &obj, int offset,
@ -537,34 +534,49 @@ JSHandle<TaggedArray> JSObject::GetEnumElementKeys(JSThread *thread, const JSHan
{
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedArray> elementArray = factory->NewTaggedArray(numOfElements);
uint32_t elementIndex = 0;
CollectEnumElementsAlongProtoChain(thread, obj, offset, elementArray, keys);
return elementArray;
}
void JSObject::CollectEnumElementsAlongProtoChain(JSThread *thread, const JSHandle<JSObject> &obj, int offset,
JSHandle<TaggedArray> elementArray, uint32_t *keys,
int32_t lastLength)
{
uint32_t elementIndex = static_cast<uint32_t>(offset);
JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
if (obj->IsJSPrimitiveRef() && JSPrimitiveRef::Cast(*obj)->IsString()) {
elementIndex = JSPrimitiveRef::Cast(*obj)->GetStringLength();
*keys += elementIndex;
elementIndex += static_cast<uint32_t>(offset);
for (uint32_t i = static_cast<uint32_t>(offset); i < elementIndex; ++i) {
uint32_t strLen = JSPrimitiveRef::Cast(*obj)->GetStringLength();
for (uint32_t i = 0; i < strLen; ++i) {
keyHandle.Update(base::NumberHelper::IntToEcmaString(thread, i));
elementArray->Set(thread, i, keyHandle);
elementArray->Set(thread, elementIndex, keyHandle);
elementIndex++;
}
*keys += strLen;
}
JSHandle<TaggedArray> arr(thread, obj->GetElements());
if (!arr->IsDictionaryMode()) {
JSHandle<TaggedQueue> emptyQueue = thread->GetEcmaVM()->GetFactory()->GetEmptyTaggedQueue();
uint32_t elementsLen = arr->GetLength();
uint32_t preElementIndex = elementIndex;
for (uint32_t i = 0; i < elementsLen; ++i) {
if (!arr->Get(i).IsHole()) {
keyHandle.Update(base::NumberHelper::IntToEcmaString(thread, i));
elementArray->Set(thread, elementIndex++, keyHandle);
if (arr->Get(i).IsHole()) {
continue;
}
keyHandle.Update(base::NumberHelper::IntToEcmaString(thread, i));
bool isDuplicated = IsDepulicateKeys(thread, elementArray, lastLength, emptyQueue, keyHandle);
if (isDuplicated) {
continue;
}
elementArray->Set(thread, elementIndex, keyHandle);
elementIndex++;
}
*keys += (elementIndex - preElementIndex);
} else {
NumberDictionary::GetAllEnumKeys(thread, JSHandle<NumberDictionary>(arr), elementIndex, elementArray, keys);
NumberDictionary::GetAllEnumKeys(
thread, JSHandle<NumberDictionary>(arr), elementIndex, elementArray, keys, lastLength);
}
return elementArray;
}
void JSObject::GetEnumElementKeys(JSThread *thread, const JSHandle<JSObject> &obj, int offset,
@ -594,6 +606,27 @@ void JSObject::GetEnumElementKeys(JSThread *thread, const JSHandle<JSObject> &ob
}
}
std::pair<uint32_t, uint32_t> JSObject::GetNumberOfEnumKeys() const
{
DISALLOW_GARBAGE_COLLECTION;
TaggedArray *array = TaggedArray::Cast(GetProperties().GetTaggedObject());
if (!array->IsDictionaryMode()) {
int end = static_cast<int>(GetJSHClass()->NumberOfProps());
if (end > 0) {
LayoutInfo *layout = LayoutInfo::Cast(GetJSHClass()->GetLayout().GetTaggedObject());
return layout->GetNumOfEnumKeys(end, this);
}
return std::make_pair(0, 0);
}
if (IsJSGlobalObject()) {
GlobalDictionary *dict = GlobalDictionary::Cast(array);
return dict->GetNumOfEnumKeys();
}
NameDictionary *dict = NameDictionary::Cast(GetProperties().GetTaggedObject());
return dict->GetNumOfEnumKeys();
}
uint32_t JSObject::GetNumberOfKeys()
{
DISALLOW_GARBAGE_COLLECTION;
@ -1337,6 +1370,38 @@ JSHandle<TaggedArray> JSObject::GetAllPropertyKeys(JSThread *thread, const JSHan
return retArray;
}
void JSObject::CollectEnumKeysAlongProtoChain(JSThread *thread, const JSHandle<JSObject> &obj,
JSHandle<TaggedArray> keyArray, uint32_t *keys,
JSHandle<TaggedQueue> shadowQueue, int32_t lastLength)
{
ASSERT(!obj->IsJSGlobalObject());
TaggedArray *array = TaggedArray::Cast(obj->GetProperties().GetTaggedObject());
if (!array->IsDictionaryMode()) {
JSHClass *jsHclass = obj->GetJSHClass();
int end = static_cast<int>(jsHclass->NumberOfProps());
if (end > 0) {
LayoutInfo::Cast(jsHclass->GetLayout().GetTaggedObject())
->GetAllEnumKeys(thread, end, *keys, keyArray, keys, shadowQueue, obj, lastLength);
}
return;
}
NameDictionary *dict = NameDictionary::Cast(obj->GetProperties().GetTaggedObject());
dict->GetAllEnumKeys(thread, *keys, keyArray, keys, shadowQueue, lastLength);
}
void JSObject::AppendOwnEnumPropertyKeys(JSThread *thread, const JSHandle<JSObject> &obj,
JSHandle<TaggedArray> keyArray, uint32_t *keys,
JSHandle<TaggedQueue> shadowQueue)
{
int32_t lastLength = *keys;
uint32_t numOfElements = obj->GetNumberOfElements();
if (numOfElements > 0) {
CollectEnumElementsAlongProtoChain(thread, obj, *keys, keyArray, keys, lastLength);
}
CollectEnumKeysAlongProtoChain(thread, obj, keyArray, keys, shadowQueue, lastLength);
}
JSHandle<TaggedArray> JSObject::GetOwnEnumPropertyKeys(JSThread *thread, const JSHandle<JSObject> &obj)
{
uint32_t numOfElements = obj->GetNumberOfElements();
@ -2059,13 +2124,95 @@ void PropertyDescriptor::CompletePropertyDescriptor(const JSThread *thread, Prop
}
}
// static
// When receiver has no elements and there is no enum cache and elements on receiver's prototype chain,
// the enum cache is a simple enum cache.
// When receiver and receiver's prototype chain have no elements, and the prototype is not modified,
// the enum cache is a enum cache with protochain
bool JSObject::IsSimpleEnumCacheValid(JSTaggedValue receiver)
{
DISALLOW_GARBAGE_COLLECTION;
uint32_t numOfElements = JSObject::Cast(receiver.GetTaggedObject())->GetNumberOfElements();
if (numOfElements > 0) {
return false;
}
JSTaggedValue current = JSObject::GetPrototype(receiver);
while (current.IsHeapObject()) {
JSObject *currentObj = JSObject::Cast(current.GetTaggedObject());
uint32_t numOfCurrentElements = currentObj->GetNumberOfElements();
if (numOfCurrentElements > 0) {
return false;
}
JSHClass *hclass = currentObj->GetJSHClass();
JSTaggedValue protoEnumCache = hclass->GetEnumCache();
if (!protoEnumCache.IsUndefined()) {
return false;
}
current = JSObject::GetPrototype(current);
}
return true;
}
bool JSObject::IsEnumCacheWithProtoChainInfoValid(JSTaggedValue receiver)
{
DISALLOW_GARBAGE_COLLECTION;
// check elements of receiver
uint32_t numOfElements = JSObject::Cast(receiver.GetTaggedObject())->GetNumberOfElements();
if (numOfElements > 0) {
return false;
}
// check protochain keys
JSTaggedValue proto = JSObject::GetPrototype(receiver);
if (!proto.IsECMAObject()) {
return false;
}
JSTaggedValue protoChangeMarker = proto.GetTaggedObject()->GetClass()->GetProtoChangeMarker();
if (!protoChangeMarker.IsProtoChangeMarker()) {
return false;
}
if (ProtoChangeMarker::Cast(protoChangeMarker.GetTaggedObject())->GetHasChanged()) {
return false;
}
// check protochain elements
JSTaggedValue current = proto;
while (current.IsHeapObject()) {
JSObject *currentObj = JSObject::Cast(current.GetTaggedObject());
uint32_t numOfCurrentElements = currentObj->GetNumberOfElements();
if (numOfCurrentElements > 0) {
return false;
}
current = JSObject::GetPrototype(current);
}
return true;
}
JSTaggedValue JSObject::TryGetEnumCache(JSThread *thread, JSTaggedValue obj)
{
if (obj.IsSlowKeysObject() || obj.GetTaggedObject()->GetClass()->IsDictionaryMode()) {
return JSTaggedValue::Undefined();
}
JSTaggedValue enumCache = obj.GetTaggedObject()->GetClass()->GetEnumCache();
EnumCacheKind kind = JSObject::GetEnumCacheKind(thread, enumCache);
bool isEnumCacheValid = false;
switch (kind) {
case EnumCacheKind::SIMPLE:
isEnumCacheValid = IsSimpleEnumCacheValid(obj);
break;
case EnumCacheKind::PROTOCHAIN:
isEnumCacheValid = IsEnumCacheWithProtoChainInfoValid(obj);
default:
break;
}
if (!isEnumCacheValid) {
return JSTaggedValue::Undefined();
}
return enumCache;
}
// 13.7.5.15 EnumerateObjectProperties ( O )
JSHandle<JSForInIterator> JSObject::EnumerateObjectProperties(JSThread *thread, const JSHandle<JSTaggedValue> &obj)
{
// 1. Return an Iterator object (25.1.1.2) whose next method iterates over all the String-valued keys of
// enumerable properties of O. The Iterator object must inherit from %IteratorPrototype% (25.1.2). The
// mechanics and order of enumerating the properties is not specified but must conform to the rules specified
// below.
JSHandle<JSTaggedValue> object;
if (obj->IsString()) {
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
@ -2074,7 +2221,37 @@ JSHandle<JSForInIterator> JSObject::EnumerateObjectProperties(JSThread *thread,
object = JSTaggedValue::ToPrototypeOrObj(thread, obj);
}
return thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(object);
JSMutableHandle<JSTaggedValue> keys(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> cachedHclass(thread, JSTaggedValue::Undefined());
if (object->IsNull() || object->IsUndefined()) {
JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined();
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
keys.Update(factory->EmptyArray());
return factory->NewJSForinIterator(undefined, keys, cachedHclass);
}
keys.Update(TryGetEnumCache(thread, object.GetTaggedValue()));
if (!keys->IsUndefined()) {
cachedHclass.Update(JSTaggedValue(JSHandle<JSObject>::Cast(object)->GetJSHClass()));
return thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(object, keys, cachedHclass);
}
return LoadEnumerateProperties(thread, object);
}
JSHandle<JSForInIterator> JSObject::LoadEnumerateProperties(JSThread *thread, const JSHandle<JSTaggedValue> &object)
{
PropertyAccessor accessor(thread, object);
JSHandle<JSTaggedValue> fastKeys = accessor.GetKeysFast();
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSForInIterator, thread);
JSMutableHandle<JSTaggedValue> keys(thread, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> cachedHclass(thread, JSTaggedValue::Undefined());
if (fastKeys->IsUndefined()) {
keys.Update(accessor.GetKeysSlow());
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSForInIterator, thread);
} else {
keys.Update(fastKeys);
cachedHclass.Update(accessor.GetCachedHclass());
}
return thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(object, keys, cachedHclass);
}
void JSObject::DefinePropertyByLiteral(JSThread *thread, const JSHandle<JSObject> &obj,

View File

@ -40,6 +40,9 @@ class JSArray;
class JSForInIterator;
class LexicalEnv;
class GlobalEnv;
class TaggedQueue;
using EnumCacheKind = EnumCache::EnumCacheKind;
// Integrity level for objects
enum IntegrityLevel { SEALED, FROZEN };
@ -424,6 +427,8 @@ public:
// [[GetPrototypeOf]]
static JSTaggedValue GetPrototype(const JSHandle<JSObject> &obj);
static JSTaggedValue GetPrototype(JSTaggedValue obj);
// [[SetPrototypeOf]]
static bool SetPrototype(JSThread *thread, const JSHandle<JSObject> &obj, const JSHandle<JSTaggedValue> &proto);
@ -503,6 +508,14 @@ public:
static JSHandle<TaggedArray> GetAllPropertyKeys(JSThread *thread, const JSHandle<JSObject> &obj, uint32_t filter);
static void CollectEnumKeysAlongProtoChain(JSThread *thread, const JSHandle<JSObject> &obj,
JSHandle<TaggedArray> keyArray, uint32_t *keys,
JSHandle<TaggedQueue> shadowQueue, int32_t lastLength = -1);
static void AppendOwnEnumPropertyKeys(JSThread *thread, const JSHandle<JSObject> &obj,
JSHandle<TaggedArray> keyArray, uint32_t *keys,
JSHandle<TaggedQueue> shadowQueue);
static JSHandle<TaggedArray> GetOwnEnumPropertyKeys(JSThread *thread, const JSHandle<JSObject> &obj);
// 9.1.13 ObjectCreate
@ -512,8 +525,11 @@ public:
static bool InstanceOf(JSThread *thread, const JSHandle<JSTaggedValue> &object,
const JSHandle<JSTaggedValue> &target);
static JSTaggedValue TryGetEnumCache(JSThread *thread, JSTaggedValue obj);
// 13.7.5.15 EnumerateObjectProperties ( O ); same as [[Enumerate]]
static JSHandle<JSForInIterator> EnumerateObjectProperties(JSThread *thread, const JSHandle<JSTaggedValue> &obj);
static JSHandle<JSForInIterator> LoadEnumerateProperties(JSThread *thread, const JSHandle<JSTaggedValue> &object);
static bool IsRegExp(JSThread *thread, const JSHandle<JSTaggedValue> &argument);
@ -588,17 +604,21 @@ public:
static void GetALLElementKeysIntoVector(const JSThread *thread, const JSHandle<JSObject> &obj,
std::vector<JSTaggedValue> &keyVector);
std::pair<uint32_t, uint32_t> GetNumberOfEnumKeys() const;
uint32_t GetNumberOfKeys();
uint32_t GetNumberOfElements();
static JSHandle<TaggedArray> GetEnumElementKeys(JSThread *thread, const JSHandle<JSObject> &obj, int offset,
uint32_t numOfElements, uint32_t *keys);
static void CollectEnumElementsAlongProtoChain(JSThread *thread, const JSHandle<JSObject> &obj, int offset,
JSHandle<TaggedArray> elementArray, uint32_t *keys,
int32_t lastLength = -1);
static void GetEnumElementKeys(JSThread *thread, const JSHandle<JSObject> &obj, int offset,
const JSHandle<TaggedArray> &keyArray);
static JSHandle<TaggedArray> GetAllEnumKeys(const JSThread *thread, const JSHandle<JSObject> &obj, int offset,
static JSHandle<TaggedArray> GetAllEnumKeys(JSThread *thread, const JSHandle<JSObject> &obj,
uint32_t numOfKeys, uint32_t *keys);
static void GetAllEnumKeys(const JSThread *thread, const JSHandle<JSObject> &obj, int offset,
const JSHandle<TaggedArray> &keyArray);
static uint32_t GetAllEnumKeys(JSThread *thread, const JSHandle<JSObject> &obj, int offset,
const JSHandle<TaggedArray> &keyArray);
static void AddAccessor(JSThread *thread, const JSHandle<JSTaggedValue> &obj, const JSHandle<JSTaggedValue> &key,
const JSHandle<AccessorData> &value, PropertyAttributes attr);
@ -637,6 +657,15 @@ public:
static JSHandle<TaggedArray> GrowElementsCapacity(const JSThread *thread, const JSHandle<JSObject> &obj,
uint32_t capacity, bool highGrowth = false, bool isNew = false);
static bool IsDepulicateKeys(JSThread *thread, JSHandle<TaggedArray> keys, int32_t lastLength,
JSHandle<TaggedQueue> shadowQueue, JSHandle<JSTaggedValue> key);
static void SetEnumCacheKind(JSThread *thread, TaggedArray *array, EnumCacheKind kind);
static EnumCacheKind GetEnumCacheKind(JSThread *thread, TaggedArray *array);
static EnumCacheKind GetEnumCacheKind(JSThread *thread, JSTaggedValue enumCache);
static void ClearHasDeleteProperty(JSHandle<JSTaggedValue> object);
static JSHandle<JSTaggedValue> IterableToList(JSThread *thread, const JSHandle<JSTaggedValue> &items,
JSTaggedValue method = JSTaggedValue::Undefined());
@ -674,6 +703,8 @@ private:
static uint32_t SetValuesOrEntries(JSThread *thread, const JSHandle<TaggedArray> &prop, uint32_t index,
const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value,
PropertyKind kind);
static bool IsSimpleEnumCacheValid(JSTaggedValue receiver);
static bool IsEnumCacheWithProtoChainInfoValid(JSTaggedValue receiver);
};
} // namespace ecmascript
} // namespace panda

View File

@ -1294,6 +1294,16 @@ inline bool JSTaggedValue::IsJSGlobalObject() const
return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSGlobalObject();
}
inline bool JSTaggedValue::IsSpecialKeysObject() const
{
return IsTypedArray() || IsModuleNamespace() || IsSpecialContainer();
}
inline bool JSTaggedValue::IsSlowKeysObject() const
{
return IsJSGlobalObject() || IsJSProxy() || IsSpecialKeysObject();
}
inline bool JSTaggedValue::IsMachineCodeObject() const
{
return IsHeapObject() && GetTaggedObject()->GetClass()->IsMachineCodeObject();

View File

@ -676,6 +676,8 @@ public:
bool IsProtoChangeDetails() const;
bool IsMarkerCell() const;
bool IsTrackInfoObject() const;
bool IsSpecialKeysObject() const;
bool IsSlowKeysObject() const;
bool IsMachineCodeObject() const;
bool IsClassInfoExtractor() const;
bool IsTSType() const;

View File

@ -69,7 +69,7 @@ void LayoutInfo::GetAllKeys(const JSThread *thread, int end, int offset, TaggedA
for (int i = 0; i < end; i++) {
JSTaggedValue key = GetKey(i);
if (key.IsString()) {
if (IsUninitializedProperty(object, i)) {
if (IsUninitializedProperty(*object, i)) {
continue;
}
keyArray->Set(thread, enumKeys + offset, key);
@ -99,7 +99,7 @@ void LayoutInfo::GetAllKeysByFilter(const JSThread *thread, uint32_t numberOfPro
for (uint32_t i = 0; i < numberOfProps; i++) {
JSTaggedValue key = GetKey(static_cast<int>(i));
if (key.IsString() && !(filter & NATIVE_KEY_SKIP_STRINGS)) {
if (IsUninitializedProperty(object, i)) {
if (IsUninitializedProperty(*object, i)) {
continue;
}
PropertyAttributes attr = GetAttr(static_cast<int>(i));
@ -140,29 +140,81 @@ void LayoutInfo::GetAllKeysForSerialization(int end, std::vector<JSTaggedValue>
}
}
void LayoutInfo::GetAllEnumKeys(const JSThread *thread, int end, int offset, TaggedArray *keyArray,
std::pair<uint32_t, uint32_t> LayoutInfo::GetNumOfEnumKeys(int end, const JSObject *object) const
{
ASSERT(end <= NumberOfElements());
uint32_t enumKeys = 0;
uint32_t shadowKeys = 0;
for (int i = 0; i < end; i++) {
JSTaggedValue key = GetKey(i);
if (!key.IsString()) {
continue;
}
if (IsUninitializedProperty(object, i)) {
continue;
}
if (GetAttr(i).IsEnumerable()) {
enumKeys++;
} else {
shadowKeys++;
}
}
return std::make_pair(enumKeys, shadowKeys);
}
void LayoutInfo::GetAllEnumKeys(JSThread *thread, int end, int offset, JSHandle<TaggedArray> keyArray,
uint32_t *keys, JSHandle<TaggedQueue> shadowQueue, const JSHandle<JSObject> object,
int32_t lastLength)
{
ASSERT(end <= NumberOfElements());
ASSERT_PRINT(offset <= static_cast<int>(keyArray->GetLength()),
"keyArray capacity is not enough for dictionary");
JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
int enumKeys = 0;
for (int i = 0; i < end; i++) {
keyHandle.Update(GetKey(i));
if (!keyHandle->IsString()) {
continue;
}
if (IsUninitializedProperty(*object, i)) {
continue;
}
if (GetAttr(i).IsEnumerable()) {
bool isDuplicated = JSObject::IsDepulicateKeys(thread, keyArray, lastLength, shadowQueue, keyHandle);
if (isDuplicated) {
continue;
}
keyArray->Set(thread, enumKeys + offset, keyHandle);
enumKeys++;
} else {
TaggedQueue::PushFixedQueue(thread, shadowQueue, keyHandle);
}
}
*keys += enumKeys;
}
void LayoutInfo::GetAllEnumKeys(JSThread *thread, int end, int offset, JSHandle<TaggedArray> keyArray,
uint32_t *keys, const JSHandle<JSObject> object)
{
ASSERT(end <= NumberOfElements());
ASSERT_PRINT(offset + end <= static_cast<int>(keyArray->GetLength()),
ASSERT_PRINT(offset <= static_cast<int>(keyArray->GetLength()),
"keyArray capacity is not enough for dictionary");
DISALLOW_GARBAGE_COLLECTION;
JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
int enumKeys = 0;
for (int i = 0; i < end; i++) {
JSTaggedValue key = GetKey(i);
if (key.IsString() && GetAttr(i).IsEnumerable()) {
if (IsUninitializedProperty(object, i)) {
keyHandle.Update(GetKey(i));
if (keyHandle->IsString() && GetAttr(i).IsEnumerable()) {
if (IsUninitializedProperty(*object, i)) {
continue;
}
keyArray->Set(thread, enumKeys + offset, key);
keyArray->Set(thread, enumKeys + offset, keyHandle);
enumKeys++;
}
}
*keys += enumKeys;
}
bool LayoutInfo::IsUninitializedProperty(const JSHandle<JSObject> object, uint32_t index)
bool LayoutInfo::IsUninitializedProperty(const JSObject *object, uint32_t index) const
{
PropertyAttributes attr = GetAttr(index);
if (!attr.IsInlinedProps()) {

View File

@ -84,14 +84,17 @@ public:
void GetAllKeysForSerialization(int end, std::vector<JSTaggedValue> &keyVector);
void GetAllKeysByFilter(const JSThread *thread, uint32_t numberOfProps, uint32_t &keyArrayEffectivelength,
TaggedArray *keyArray, const JSHandle<JSObject> object, uint32_t filter);
void GetAllEnumKeys(const JSThread *thread, int end, int offset, TaggedArray *keyArray, uint32_t *keys,
std::pair<uint32_t, uint32_t> GetNumOfEnumKeys(int end, const JSObject *object) const;
void GetAllEnumKeys(JSThread *thread, int end, int offset, JSHandle<TaggedArray> keyArray, uint32_t *keys,
JSHandle<TaggedQueue> shadowQueue, const JSHandle<JSObject> object,
int32_t lastLength);
void GetAllEnumKeys(JSThread *thread, int end, int offset, JSHandle<TaggedArray> keyArray, uint32_t *keys,
const JSHandle<JSObject> object);
void DumpFieldIndexForProfile(int index, PGOHClassLayoutDesc &desc, PGOObjKind kind);
DECL_DUMP()
private:
bool IsUninitializedProperty(const JSHandle<JSObject> object, uint32_t index);
bool IsUninitializedProperty(const JSObject *object, uint32_t index) const;
};
} // namespace panda::ecmascript

View File

@ -655,16 +655,20 @@ JSHandle<JSArray> ObjectFactory::NewJSArray()
return JSHandle<JSArray>(NewJSObjectByConstructor(function));
}
JSHandle<JSForInIterator> ObjectFactory::NewJSForinIterator(const JSHandle<JSTaggedValue> &obj)
JSHandle<JSForInIterator> ObjectFactory::NewJSForinIterator(const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> keys,
const JSHandle<JSTaggedValue> cachedHclass)
{
JSHandle<GlobalEnv> env = vm_->GetGlobalEnv();
JSHandle<JSHClass> hclass(env->GetForinIteratorClass());
JSHandle<JSForInIterator> it = JSHandle<JSForInIterator>::Cast(NewJSObject(hclass));
it->SetObject(thread_, obj);
it->SetVisitedObjs(thread_, thread_->GlobalConstants()->GetEmptyTaggedQueue());
it->SetRemainingKeys(thread_, thread_->GlobalConstants()->GetEmptyTaggedQueue());
it->ClearBitField();
it->SetCachedHclass(thread_, cachedHclass);
it->SetKeys(thread_, keys);
it->SetIndex(EnumCache::ENUM_CACHE_HEADER_SIZE);
uint32_t enumLength = JSHandle<TaggedArray>::Cast(keys)->GetLength();
it->SetLength(enumLength);
return it;
}
@ -2439,6 +2443,25 @@ JSHandle<TaggedArray> ObjectFactory::CopyArray(const JSHandle<TaggedArray> &old,
return newArray;
}
JSHandle<TaggedArray> ObjectFactory::CopyFromEnumCache(const JSHandle<TaggedArray> &old)
{
NewObjectHook();
uint32_t oldLength = old->GetLength();
uint32_t newLength = oldLength - EnumCache::ENUM_CACHE_HEADER_SIZE;
size_t size = TaggedArray::ComputeSize(JSTaggedValue::TaggedTypeSize(), newLength);
TaggedObject *header = heap_->AllocateYoungOrHugeObject(
JSHClass::Cast(thread_->GlobalConstants()->GetArrayClass().GetTaggedObject()), size);
JSHandle<TaggedArray> newArray(thread_, header);
newArray->SetLength(newLength);
newArray->SetExtraLength(old->GetExtraLength());
for (uint32_t i = 0; i < newLength; i++) {
JSTaggedValue value = old->Get(i + EnumCache::ENUM_CACHE_HEADER_SIZE);
newArray->Set(thread_, i, value);
}
return newArray;
}
JSHandle<LayoutInfo> ObjectFactory::CreateLayoutInfo(int properties, MemSpaceType type, GrowMode mode)
{
int growLength =

View File

@ -350,7 +350,9 @@ public:
// Copy on write array is allocated in nonmovable space by default.
JSHandle<COWTaggedArray> NewCOWTaggedArray(uint32_t length, JSTaggedValue initVal = JSTaggedValue::Hole());
JSHandle<TaggedArray> NewDictionaryArray(uint32_t length);
JSHandle<JSForInIterator> NewJSForinIterator(const JSHandle<JSTaggedValue> &obj);
JSHandle<JSForInIterator> NewJSForinIterator(const JSHandle<JSTaggedValue> &obj,
const JSHandle<JSTaggedValue> keys,
const JSHandle<JSTaggedValue> cachedHclass);
JSHandle<ByteArray> NewByteArray(uint32_t length, uint32_t size);
@ -370,6 +372,7 @@ public:
JSHandle<TaggedArray> CopyArray(const JSHandle<TaggedArray> &old, uint32_t oldLength, uint32_t newLength,
JSTaggedValue initVal = JSTaggedValue::Hole(),
MemSpaceType type = MemSpaceType::SEMI_SPACE);
JSHandle<TaggedArray> CopyFromEnumCache(const JSHandle<TaggedArray> &old);
JSHandle<TaggedArray> CloneProperties(const JSHandle<TaggedArray> &old);
JSHandle<TaggedArray> CloneProperties(const JSHandle<TaggedArray> &old, const JSHandle<JSTaggedValue> &env,
const JSHandle<JSObject> &obj);

View File

@ -0,0 +1,313 @@
/*
* Copyright (c) 2023 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/property_accessor.h"
#include "ecmascript/js_object-inl.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tagged_array-inl.h"
namespace panda::ecmascript {
PropertyAccessor::PropertyAccessor(JSThread *thread, JSHandle<JSTaggedValue> object)
: thread_(thread),
receiver_(thread, object.GetTaggedValue()),
fastKeysArray_(thread, JSTaggedValue::Undefined()),
cachedHclass_(thread, JSTaggedValue::Undefined()),
keyLength_(0),
shadowKeyLength_(0),
onlyHasSimpleProperties_(true),
canUseEnumCache_(true),
hasSlowProperties_(false),
slowKeysArray_(thread, JSTaggedValue::Undefined()),
acutalKeyLength_(0)
{
PreLoad();
}
void PropertyAccessor::PreLoad()
{
if (receiver_->IsSlowKeysObject()) {
hasSlowProperties_ = true;
return;
}
JSHandle<JSObject> receiverObj(receiver_);
JSHClass *jshclass = receiverObj->GetJSHClass();
if (jshclass->IsDictionaryMode()) {
onlyHasSimpleProperties_ = false;
canUseEnumCache_ = false;
}
uint32_t numOfElements = receiverObj->GetNumberOfElements();
if (numOfElements > 0) {
AccumulateKeyLength(numOfElements);
onlyHasSimpleProperties_ = false;
canUseEnumCache_ = false;
}
std::pair<uint32_t, uint32_t> numOfKeys = receiverObj->GetNumberOfEnumKeys();
uint32_t numOfEnumKeys = numOfKeys.first;
if (numOfEnumKeys > 0) {
AccumulateKeyLength(numOfEnumKeys);
}
uint32_t numOfShadowKeys = numOfKeys.second;
if (numOfShadowKeys > 0) {
AccumulateShadowKeyLength(numOfShadowKeys);
}
CollectPrototypeInfo();
if (hasSlowProperties_ || !onlyHasSimpleProperties_) {
return;
}
ASSERT(canUseEnumCache_);
// fast path
InitSimplePropertiesEnumCache();
}
void PropertyAccessor::CollectPrototypeInfo()
{
DISALLOW_GARBAGE_COLLECTION;
JSTaggedValue current = JSTaggedValue::GetPrototype(thread_, receiver_);
while (current.IsHeapObject()) {
if (current.IsSlowKeysObject()) {
hasSlowProperties_ = true;
break;
}
JSObject *currentObj = JSObject::Cast(current.GetTaggedObject());
uint32_t numOfCurrentElements = currentObj->GetNumberOfElements();
if (numOfCurrentElements > 0) {
AccumulateKeyLength(numOfCurrentElements);
onlyHasSimpleProperties_ = false;
canUseEnumCache_ = false;
}
std::pair<uint32_t, uint32_t> numOfKeys = currentObj->GetNumberOfEnumKeys();
uint32_t numOfEnumKeys = numOfKeys.first;
if (numOfEnumKeys > 0) {
AccumulateKeyLength(numOfEnumKeys);
onlyHasSimpleProperties_ = false;
}
uint32_t numOfShadowKeys = numOfKeys.second;
if (numOfShadowKeys > 0) {
AccumulateShadowKeyLength(numOfShadowKeys);
}
JSHClass *jshclass = currentObj->GetJSHClass();
if (jshclass->IsDictionaryMode()) {
onlyHasSimpleProperties_ = false;
canUseEnumCache_ = false;
}
if (onlyHasSimpleProperties_) {
// a fast path to check simple enum cache
jshclass->SetEnumCache(thread_, JSTaggedValue::Undefined());
}
current = JSObject::GetPrototype(current);
}
}
void PropertyAccessor::InitSimplePropertiesEnumCache()
{
ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
JSHandle<JSObject> receiverObj(receiver_);
ASSERT(receiverObj->GetNumberOfElements() == 0);
JSMutableHandle<TaggedArray> keyArray(thread_, JSTaggedValue::Undefined());
if (keyLength_ == 0) {
keyArray.Update(factory->EmptyArray());
SetActualKeyLength(0);
} else {
uint32_t arraySize = keyLength_ + EnumCache::ENUM_CACHE_HEADER_SIZE;
JSHandle<TaggedArray> newArray = thread_->GetEcmaVM()->GetFactory()->NewTaggedArray(arraySize);
uint32_t length = JSObject::GetAllEnumKeys(thread_, receiverObj, EnumCache::ENUM_CACHE_HEADER_SIZE, newArray);
SetActualKeyLength(length);
JSObject::SetEnumCacheKind(thread_, *newArray, EnumCacheKind::SIMPLE);
keyArray.Update(newArray);
}
JSObject::ClearHasDeleteProperty(receiver_);
JSHClass *jsHclass = receiverObj->GetJSHClass();
jsHclass->SetEnumCache(thread_, keyArray.GetTaggedValue());
fastKeysArray_.Update(keyArray.GetTaggedValue());
cachedHclass_.Update(JSTaggedValue(jsHclass));
}
inline void PropertyAccessor::AccumulateKeyLength(uint32_t length)
{
keyLength_ += length;
}
inline void PropertyAccessor::AccumulateShadowKeyLength(uint32_t length)
{
shadowKeyLength_ += length;
}
JSHandle<JSTaggedValue> PropertyAccessor::GetCachedHclass()
{
return cachedHclass_;
}
uint32_t PropertyAccessor::GetActualKeyLength() const
{
return acutalKeyLength_;
}
inline void PropertyAccessor::SetActualKeyLength(uint32_t length)
{
acutalKeyLength_ = length;
}
void PropertyAccessor::AddKeysEndIfNeeded(JSHandle<TaggedArray> keys)
{
// when has duplicated keys
if (acutalKeyLength_ < keyLength_) {
keys->Set(thread_, acutalKeyLength_ + EnumCache::ENUM_CACHE_HEADER_SIZE, JSTaggedValue::Undefined());
}
}
void PropertyAccessor::TryInitEnumCacheWithProtoChainInfo()
{
#if ECMASCRIPT_ENABLE_IC
if (!canUseEnumCache_) {
JSObject::SetEnumCacheKind(thread_, TaggedArray::Cast(fastKeysArray_->GetTaggedObject()), EnumCacheKind::NONE);
return;
}
ASSERT(!onlyHasSimpleProperties_);
JSHandle<JSObject> receiverObj(receiver_);
JSHandle<JSHClass> jsHclass(thread_, receiverObj->GetJSHClass());
jsHclass->SetEnumCache(thread_, fastKeysArray_.GetTaggedValue());
JSObject::SetEnumCacheKind(
thread_, TaggedArray::Cast(fastKeysArray_->GetTaggedObject()), EnumCacheKind::PROTOCHAIN);
cachedHclass_.Update(jsHclass);
JSHClass::EnableProtoChangeMarker(thread_, jsHclass);
#endif
}
JSHandle<JSTaggedValue> PropertyAccessor::GetKeysFast()
{
if (!fastKeysArray_->IsUndefined()) {
AddKeysEndIfNeeded(JSHandle<TaggedArray>(thread_, fastKeysArray_.GetTaggedValue()));
return fastKeysArray_;
}
if (hasSlowProperties_) {
return JSHandle<JSTaggedValue>(thread_, JSTaggedValue::Undefined());
}
ASSERT(keyLength_ != 0);
ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
uint32_t arraySize = keyLength_ + EnumCache::ENUM_CACHE_HEADER_SIZE;
JSHandle<TaggedArray> keyArray = factory->NewTaggedArray(arraySize);
JSHandle<TaggedQueue> shadowQueue = factory->NewTaggedQueue(shadowKeyLength_);
uint32_t keysNum = EnumCache::ENUM_CACHE_HEADER_SIZE;
JSMutableHandle<JSTaggedValue> current(thread_, receiver_);
while (current->IsHeapObject()) {
JSObject::AppendOwnEnumPropertyKeys(thread_, JSHandle<JSObject>(current), keyArray, &keysNum, shadowQueue);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
JSObject::ClearHasDeleteProperty(current);
current.Update(JSObject::GetPrototype(current.GetTaggedValue()));
}
SetActualKeyLength(keysNum - EnumCache::ENUM_CACHE_HEADER_SIZE);
AddKeysEndIfNeeded(keyArray);
fastKeysArray_.Update(keyArray.GetTaggedValue());
TryInitEnumCacheWithProtoChainInfo();
return fastKeysArray_;
}
JSHandle<JSTaggedValue> PropertyAccessor::GetKeysSlow()
{
std::vector<JSHandle<TaggedArray>> remainings;
std::vector<JSHandle<JSTaggedValue>> visited;
JSMutableHandle<JSTaggedValue> current(thread_, receiver_);
while (current->IsHeapObject()) {
PushRemainingKeys(JSHandle<JSObject>(current), remainings);
RETURN_HANDLE_IF_ABRUPT_COMPLETION(JSTaggedValue, thread_);
JSObject::ClearHasDeleteProperty(current);
visited.emplace_back(thread_, current.GetTaggedValue());
current.Update(JSTaggedValue::GetPrototype(thread_, current));
}
MergeRemainings(remainings, visited);
return JSHandle<JSTaggedValue>(thread_, slowKeysArray_.GetTaggedValue());
}
void PropertyAccessor::PushRemainingKeys(JSHandle<JSObject> object, std::vector<JSHandle<TaggedArray>> &remainings)
{
JSMutableHandle<JSTaggedValue> value(thread_, JSTaggedValue::Undefined());
uint32_t remainingIndex = 0;
if (object->IsJSProxy()) {
JSHandle<TaggedArray> proxyArr = JSProxy::OwnPropertyKeys(thread_, JSHandle<JSProxy>(object));
RETURN_IF_ABRUPT_COMPLETION(thread_);
uint32_t length = proxyArr->GetLength();
for (uint32_t i = 0; i < length; i++) {
value.Update(proxyArr->Get(i));
PropertyDescriptor desc(thread_);
JSProxy::GetOwnProperty(thread_, JSHandle<JSProxy>(object), value, desc);
RETURN_IF_ABRUPT_COMPLETION(thread_);
if (!desc.IsEnumerable()) {
proxyArr->Set(thread_, i, JSTaggedValue::Hole());
} else {
remainingIndex++;
}
}
remainings.push_back(proxyArr);
AccumulateKeyLength(remainingIndex);
} else {
JSHandle<TaggedArray> array = JSTaggedValue::GetOwnEnumPropertyKeys(thread_, JSHandle<JSTaggedValue>(object));
uint32_t length = array->GetLength();
for (uint32_t i = 0; i < length; i++) {
value.Update(array->Get(i));
if (!value->IsString()) {
array->Set(thread_, i, JSTaggedValue::Hole());
} else {
remainingIndex++;
}
}
remainings.push_back(array);
AccumulateKeyLength(remainingIndex);
}
}
void PropertyAccessor::MergeRemainings(const std::vector<JSHandle<TaggedArray>> &remainings,
const std::vector<JSHandle<JSTaggedValue>> &visited)
{
uint32_t arraySize = keyLength_ + EnumCache::ENUM_CACHE_HEADER_SIZE;
JSHandle<TaggedArray> keyArray = thread_->GetEcmaVM()->GetFactory()->NewTaggedArray(arraySize);
JSMutableHandle<TaggedArray> remaining(thread_, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> keyHandle(thread_, JSTaggedValue::Undefined());
JSMutableHandle<JSTaggedValue> objHandle(thread_, JSTaggedValue::Undefined());
uint32_t index = EnumCache::ENUM_CACHE_HEADER_SIZE;
uint32_t numberOfRemaining = remainings.size();
for (uint32_t i = 0; i < numberOfRemaining; i++) {
remaining.Update(remainings[i]);
uint32_t remainingSize = remaining->GetLength();
for (uint32_t j = 0; j < remainingSize; j++) {
keyHandle.Update(remaining->Get(thread_, j));
if (keyHandle->IsHole()) {
continue;
}
bool has = false;
for (uint32_t k = 0; k < i; k++) {
objHandle.Update(visited[k]);
PropertyDescriptor desc(thread_);
has = JSTaggedValue::GetOwnProperty(thread_, objHandle, keyHandle, desc);
RETURN_IF_ABRUPT_COMPLETION(thread_);
if (has) {
break;
}
}
if (!has) {
keyArray->Set(thread_, index, keyHandle);
index++;
}
}
}
SetActualKeyLength(index - EnumCache::ENUM_CACHE_HEADER_SIZE);
AddKeysEndIfNeeded(keyArray);
slowKeysArray_.Update(keyArray.GetTaggedValue());
JSObject::SetEnumCacheKind(thread_, *keyArray, EnumCacheKind::NONE);
}
} // namespace panda::ecmascript

View File

@ -0,0 +1,63 @@
/*
* Copyright (c) 2023 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_PROPERTY_ACCESSOR_H
#define ECMASCRIPT_PROPERTY_ACCESSOR_H
#include "ecmascript/js_handle.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/js_thread.h"
namespace panda::ecmascript {
class JSObject;
class TaggedArray;
class PropertyAccessor {
public:
PropertyAccessor(JSThread *thread, JSHandle<JSTaggedValue> object);
JSHandle<JSTaggedValue> GetKeysFast();
JSHandle<JSTaggedValue> GetKeysSlow();
JSHandle<JSTaggedValue> GetCachedHclass();
uint32_t GetActualKeyLength() const;
private:
void PreLoad();
void CollectPrototypeInfo();
void InitSimplePropertiesEnumCache();
void AccumulateKeyLength(uint32_t length);
void AccumulateShadowKeyLength(uint32_t length);
void PushRemainingKeys(JSHandle<JSObject> object, std::vector<JSHandle<TaggedArray>> &remainings);
void MergeRemainings(const std::vector<JSHandle<TaggedArray>> &remainings,
const std::vector<JSHandle<JSTaggedValue>> &visited);
void SetActualKeyLength(uint32_t length);
void AddKeysEndIfNeeded(JSHandle<TaggedArray> keys);
void TryInitEnumCacheWithProtoChainInfo();
JSThread *thread_{nullptr};
JSMutableHandle<JSTaggedValue> receiver_;
JSMutableHandle<JSTaggedValue> fastKeysArray_;
JSMutableHandle<JSTaggedValue> cachedHclass_;
uint32_t keyLength_ {0};
uint32_t shadowKeyLength_ {0};
// receiver has no elements, and is not dictionary mode and has empty prototype
bool onlyHasSimpleProperties_ {true};
bool canUseEnumCache_ {true};
bool hasSlowProperties_ {false};
JSMutableHandle<TaggedArray> slowKeysArray_;
uint32_t acutalKeyLength_ {0};
};
} // namespace panda::ecmascript
#endif // ECMASCRIPT_PROPERTY_ACCESSOR_H

View File

@ -99,6 +99,8 @@ namespace panda::ecmascript {
V(Ldlexvar) \
V(Ldlexenv) \
V(GetPropIterator) \
V(GetPropIteratorSlowpath) \
V(PrimitiveStringCreate) \
V(CreateIterResultObj) \
V(SuspendGenerator) \
V(ResumeGenerator) \
@ -179,6 +181,7 @@ namespace panda::ecmascript {
V(CreateRegExpWithLiteral) \
V(CreateArrayWithBuffer) \
V(GetNextPropName) \
V(GetNextPropNameSlowpath) \
V(CopyDataProperties) \
V(GetUnmapedArgs) \
V(TryStGlobalByName) \

View File

@ -203,10 +203,9 @@ JSTaggedValue RuntimeStubs::RuntimeGetTemplateObject(JSThread *thread, const JSH
JSTaggedValue RuntimeStubs::RuntimeGetNextPropName(JSThread *thread, const JSHandle<JSTaggedValue> &iter)
{
ASSERT(iter->IsForinIterator());
std::pair<JSTaggedValue, bool> res =
JSForInIterator::NextInternal(thread, JSHandle<JSForInIterator>::Cast(iter));
JSTaggedValue res = JSForInIterator::NextInternal(thread, JSHandle<JSForInIterator>::Cast(iter));
RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
return res.first;
return res;
}
JSTaggedValue RuntimeStubs::RuntimeIterNext(JSThread *thread, const JSHandle<JSTaggedValue> &iter)

View File

@ -42,6 +42,7 @@
#include "ecmascript/js_date.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_object.h"
#include "ecmascript/js_primitive_ref.h"
#include "ecmascript/js_proxy.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/js_typed_array.h"
@ -633,6 +634,15 @@ DEF_RUNTIME_STUBS(GetNextPropName)
return RuntimeGetNextPropName(thread, iter).GetRawData();
}
DEF_RUNTIME_STUBS(GetNextPropNameSlowpath)
{
RUNTIME_STUBS_HEADER(GetNextPropNameSlowpath);
JSHandle<JSTaggedValue> iter = GetHArg<JSTaggedValue>(argv, argc, 0); // 0: means the zeroth parameter
ASSERT(iter->IsForinIterator());
JSTaggedValue res = JSForInIterator::NextInternalSlowpath(thread, JSHandle<JSForInIterator>::Cast(iter));
return res.GetRawData();
}
DEF_RUNTIME_STUBS(IterNext)
{
RUNTIME_STUBS_HEADER(IterNext);
@ -1335,6 +1345,21 @@ DEF_RUNTIME_STUBS(GetPropIterator)
return RuntimeGetPropIterator(thread, value).GetRawData();
}
DEF_RUNTIME_STUBS(GetPropIteratorSlowpath)
{
RUNTIME_STUBS_HEADER(GetPropIteratorSlowpath);
JSHandle<JSTaggedValue> value = GetHArg<JSTaggedValue>(argv, argc, 0); // 0: means the zeroth parameter
return JSObject::LoadEnumerateProperties(thread, value).GetTaggedValue().GetRawData();
}
DEF_RUNTIME_STUBS(PrimitiveStringCreate)
{
RUNTIME_STUBS_HEADER(PrimitiveStringCreate);
JSHandle<JSTaggedValue> str = GetHArg<JSTaggedValue>(argv, argc, 0); // 0: means the zeroth parameter
JSHandle<JSTaggedValue> newTarget = thread->GlobalConstants()->GetHandledUndefined();
return JSPrimitiveRef::StringCreate(thread, str, newTarget).GetTaggedValue().GetRawData();
}
DEF_RUNTIME_STUBS(AsyncFunctionEnter)
{
RUNTIME_STUBS_HEADER(AsyncFunctionEnter);

View File

@ -176,6 +176,7 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co
V(ThrowConstAssignment) \
V(GetTemplateObject) \
V(GetNextPropName) \
V(GetNextPropNameSlowpath) \
V(ThrowIfNotObject) \
V(IterNext) \
V(CloseIterator) \
@ -221,6 +222,8 @@ using FastCallAotEntryType = JSTaggedValue (*)(uintptr_t glue, uint32_t argc, co
V(LdModuleVar) \
V(Throw) \
V(GetPropIterator) \
V(GetPropIteratorSlowpath) \
V(PrimitiveStringCreate) \
V(AsyncFunctionEnter) \
V(GetIterator) \
V(GetAsyncIterator) \

View File

@ -16,6 +16,7 @@
#include "ecmascript/tagged_dictionary.h"
#include "ecmascript/ecma_string-inl.h"
#include "ecmascript/filter_helper.h"
#include "ecmascript/js_object-inl.h"
#include "ecmascript/tagged_hash_table.h"
namespace panda::ecmascript {
@ -112,22 +113,71 @@ void NameDictionary::GetAllKeysByFilter(const JSThread *thread, uint32_t &keyArr
}
}
void NameDictionary::GetAllEnumKeys(const JSThread *thread, int offset, TaggedArray *keyArray, uint32_t *keys) const
std::pair<uint32_t, uint32_t> NameDictionary::GetNumOfEnumKeys() const
{
uint32_t arrayIndex = 0;
CVector<std::pair<JSTaggedValue, PropertyAttributes>> sortArr;
uint32_t enumKeys = 0;
uint32_t shadowKeys = 0;
int size = Size();
for (int hashIndex = 0; hashIndex < size; hashIndex++) {
JSTaggedValue key = GetKey(hashIndex);
if (key.IsString()) {
PropertyAttributes attr = GetAttributes(hashIndex);
if (attr.IsEnumerable()) {
std::pair<JSTaggedValue, PropertyAttributes> pair(key, attr);
enumKeys++;
} else {
shadowKeys++;
}
}
}
return std::make_pair(enumKeys, shadowKeys);
}
void NameDictionary::GetAllEnumKeys(JSThread *thread, int offset, JSHandle<TaggedArray> keyArray, uint32_t *keys,
JSHandle<TaggedQueue> shadowQueue, int32_t lastLength) const
{
uint32_t arrayIndex = 0;
CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
int size = Size();
for (int hashIndex = 0; hashIndex < size; hashIndex++) {
JSHandle<JSTaggedValue> keyHandle(thread, GetKey(hashIndex));
if (keyHandle->IsString()) {
PropertyAttributes attr = GetAttributes(hashIndex);
if (attr.IsEnumerable()) {
std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(keyHandle, attr);
bool isDuplicated = JSObject::IsDepulicateKeys(thread, keyArray, lastLength, shadowQueue, keyHandle);
if (isDuplicated) {
continue;
}
sortArr.push_back(pair);
} else {
TaggedQueue::PushFixedQueue(thread, shadowQueue, keyHandle);
}
}
}
std::sort(sortArr.begin(), sortArr.end(), CompHandleKey);
for (auto entry : sortArr) {
keyArray->Set(thread, arrayIndex + static_cast<uint32_t>(offset), entry.first);
arrayIndex++;
}
*keys += arrayIndex;
}
void NameDictionary::GetAllEnumKeys(JSThread *thread, int offset, JSHandle<TaggedArray> keyArray, uint32_t *keys) const
{
uint32_t arrayIndex = 0;
CVector<std::pair<JSHandle<JSTaggedValue>, PropertyAttributes>> sortArr;
int size = Size();
for (int hashIndex = 0; hashIndex < size; hashIndex++) {
JSHandle<JSTaggedValue> keyHandle(thread, GetKey(hashIndex));
if (keyHandle->IsString()) {
PropertyAttributes attr = GetAttributes(hashIndex);
if (attr.IsEnumerable()) {
std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> pair(keyHandle, attr);
sortArr.push_back(pair);
}
}
}
std::sort(sortArr.begin(), sortArr.end(), CompKey);
std::sort(sortArr.begin(), sortArr.end(), CompHandleKey);
for (auto entry : sortArr) {
keyArray->Set(thread, arrayIndex + static_cast<uint32_t>(offset), entry.first);
arrayIndex++;
@ -268,16 +318,15 @@ void NumberDictionary::GetAllKeysByFilter(const JSThread *thread, const JSHandle
}
}
std::sort(sortArr.begin(), sortArr.end(), CompKey);
ASSERT_NO_ABRUPT_COMPLETION(thread);
for (auto entry : sortArr) {
JSHandle<JSTaggedValue> keyHandle(thread, entry);
ASSERT_NO_ABRUPT_COMPLETION(thread);
keyArray->Set(thread, keyArrayEffectivelength, keyHandle.GetTaggedValue());
keyArray->Set(thread, keyArrayEffectivelength, entry);
keyArrayEffectivelength++;
}
}
void NumberDictionary::GetAllEnumKeys(const JSThread *thread, const JSHandle<NumberDictionary> &obj, int offset,
const JSHandle<TaggedArray> &keyArray, uint32_t *keys)
void NumberDictionary::GetAllEnumKeys(JSThread *thread, const JSHandle<NumberDictionary> &obj, int offset,
const JSHandle<TaggedArray> &keyArray, uint32_t *keys, int32_t lastLength)
{
ASSERT_PRINT(offset + obj->EntriesCount() <= static_cast<int>(keyArray->GetLength()),
"keyArray capacity is not enough for dictionary");
@ -294,10 +343,18 @@ void NumberDictionary::GetAllEnumKeys(const JSThread *thread, const JSHandle<Num
}
}
std::sort(sortArr.begin(), sortArr.end(), CompKey);
ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
JSHandle<TaggedQueue> emptyQueue = factory->GetEmptyTaggedQueue();
JSMutableHandle<JSTaggedValue> keyHandle(thread, JSTaggedValue::Undefined());
for (auto entry : sortArr) {
JSHandle<JSTaggedValue> keyHandle(thread, entry);
keyHandle.Update(entry);
JSHandle<EcmaString> str = JSTaggedValue::ToString(const_cast<JSThread *>(thread), keyHandle);
ASSERT_NO_ABRUPT_COMPLETION(thread);
bool isDuplicated = JSObject::IsDepulicateKeys(thread, keyArray, lastLength, emptyQueue,
JSHandle<JSTaggedValue>(str));
if (isDuplicated) {
continue;
}
keyArray->Set(thread, arrayIndex + static_cast<uint32_t>(offset), str.GetTaggedValue());
arrayIndex++;
}

View File

@ -62,7 +62,15 @@ public:
void GetAllKeys(const JSThread *thread, int offset, TaggedArray *keyArray) const;
void GetAllKeysByFilter(const JSThread *thread, uint32_t &keyArrayEffectivelength,
TaggedArray *keyArray, uint32_t filter) const;
void GetAllEnumKeys(const JSThread *thread, int offset, TaggedArray *keyArray, uint32_t *keys) const;
std::pair<uint32_t, uint32_t> GetNumOfEnumKeys() const;
void GetAllEnumKeys(JSThread *thread, int offset, JSHandle<TaggedArray> keyArray, uint32_t *keys,
JSHandle<TaggedQueue> shadowQueue, int32_t lastLength) const;
void GetAllEnumKeys(JSThread *thread, int offset, JSHandle<TaggedArray> keyArray, uint32_t *keys) const;
static inline bool CompHandleKey(const std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> &a,
const std::pair<JSHandle<JSTaggedValue>, PropertyAttributes> &b)
{
return a.second.GetDictionaryOrder() < b.second.GetDictionaryOrder();
}
static inline bool CompKey(const std::pair<JSTaggedValue, PropertyAttributes> &a,
const std::pair<JSTaggedValue, PropertyAttributes> &b)
{
@ -114,8 +122,8 @@ public:
const JSHandle<TaggedArray> &keyArray);
static void GetAllKeysByFilter(const JSThread *thread, const JSHandle<NumberDictionary> &obj,
uint32_t &keyArrayEffectivelength, const JSHandle<TaggedArray> &keyArray, uint32_t filter);
static void GetAllEnumKeys(const JSThread *thread, const JSHandle<NumberDictionary> &obj, int offset,
const JSHandle<TaggedArray> &keyArray, uint32_t *keys);
static void GetAllEnumKeys(JSThread *thread, const JSHandle<NumberDictionary> &obj, int offset,
const JSHandle<TaggedArray> &keyArray, uint32_t *keys, int32_t lastLength = -1);
static inline bool CompKey(const JSTaggedValue &a, const JSTaggedValue &b)
{
ASSERT(a.IsNumber() && b.IsNumber());

View File

@ -629,7 +629,9 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump)
case JSType::JS_FORIN_ITERATOR: {
CHECK_DUMP_FIELDS(JSObject::SIZE, JSForInIterator::SIZE, 4U);
JSHandle<JSTaggedValue> array(thread, factory->NewJSArray().GetTaggedValue());
JSHandle<JSForInIterator> forInIter = factory->NewJSForinIterator(array);
JSHandle<JSTaggedValue> keys(thread, factory->EmptyArray().GetTaggedValue());
JSHandle<JSTaggedValue> hclass(thread, JSTaggedValue::Undefined());
JSHandle<JSForInIterator> forInIter = factory->NewJSForinIterator(array, keys, hclass);
DUMP_FOR_HANDLE(forInIter);
break;
}

View File

@ -73,21 +73,17 @@ HWTEST_F_L0(JSForinIteratorTest, Create)
JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(son), key2, key1Value);
JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(son), key3, key1Value);
JSHandle<JSForInIterator> it = thread->GetEcmaVM()->GetFactory()->NewJSForinIterator(JSHandle<JSTaggedValue>(son));
std::pair<JSTaggedValue, bool> n1 = JSForInIterator::NextInternal(thread, it);
EXPECT_EQ(n1.first, key1.GetTaggedValue());
EXPECT_FALSE(n1.second);
JSHandle<JSForInIterator> it = JSObject::EnumerateObjectProperties(thread, JSHandle<JSTaggedValue>(son));
JSTaggedValue n1 = JSForInIterator::NextInternal(thread, it);
EXPECT_EQ(n1, key1.GetTaggedValue());
std::pair<JSTaggedValue, bool> n2 = JSForInIterator::NextInternal(thread, it);
EXPECT_EQ(n2.first, key2.GetTaggedValue());
EXPECT_FALSE(n2.second);
JSTaggedValue n2 = JSForInIterator::NextInternal(thread, it);
EXPECT_EQ(n2, key2.GetTaggedValue());
std::pair<JSTaggedValue, bool> n3 = JSForInIterator::NextInternal(thread, it);
EXPECT_EQ(n3.first, key3.GetTaggedValue());
EXPECT_FALSE(n3.second);
JSTaggedValue n3 = JSForInIterator::NextInternal(thread, it);
EXPECT_EQ(n3, key3.GetTaggedValue());
std::pair<JSTaggedValue, bool> n4 = JSForInIterator::NextInternal(thread, it);
EXPECT_EQ(n4.first, JSTaggedValue::Undefined());
EXPECT_TRUE(n4.second);
JSTaggedValue n4 = JSForInIterator::NextInternal(thread, it);
EXPECT_EQ(n4, JSTaggedValue::Undefined());
}
} // namespace panda::test

View File

@ -189,7 +189,7 @@ HWTEST_F_L0(LayoutInfoTest, GetAllEnumKeys)
layoutInfoHandle->AddKey(thread, i, elementsKey.GetTaggedValue(), defaultAttr);
}
uint32_t keys = 0;
layoutInfoHandle->GetAllEnumKeys(thread, infoLength, 0, *keyArray, &keys, objectHandle); // 0: offset
layoutInfoHandle->GetAllEnumKeys(thread, infoLength, 0, keyArray, &keys, objectHandle); // 0: offset
EXPECT_EQ(keyArray->Get(0), key3.GetTaggedValue());
EXPECT_EQ(keys, 1U);
}

View File

@ -11,7 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
true
false
1
undefined
undefined

View File

@ -54,6 +54,12 @@ group("ark_js_moduletest") {
"flatten",
"forawaitof",
"forin",
"forin_delete_property",
"forin_dictionary_mode",
"forin_empty_prototype",
"forin_enum_cache",
"forin_non_empty_prototype",
"forin_special_object",
"fortest",
"funcprotochangeobjectandnew",
"functionapply",
@ -180,6 +186,12 @@ group("ark_asm_test") {
"errorcause",
"flatten",
"forin",
"forin_delete_property",
"forin_dictionary_mode",
"forin_empty_prototype",
"forin_enum_cache",
"forin_non_empty_prototype",
"forin_special_object",
"fortest",
"funcprotochangeobjectandnew",
"functionapply",
@ -286,6 +298,12 @@ group("ark_asm_single_step_test") {
"ecmastringtable",
"errorcause",
"forin",
"forin_delete_property",
"forin_dictionary_mode",
"forin_empty_prototype",
"forin_enum_cache",
"forin_non_empty_prototype",
"forin_special_object",
"fortest",
"funcprotochangeobjectandnew",
"functionapply",

View File

@ -0,0 +1,18 @@
# Copyright (c) 2023 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.
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_moduletest_action("forin_delete_property") {
deps = []
}

View File

@ -0,0 +1,23 @@
# Copyright (c) 2023 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.
a
b
===============
a
b
1
===============
b
1
a

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2023 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.
*/
/*
* @tc.name:forin_delete_property
* @tc.desc:test forin_delete_property
* @tc.type: FUNC
* @tc.require: issueI84DMO
*/
// fast path
let fast = {"a":1}
fast.b = "a"
for (let i in fast) {
print(i)
delete fast.a
}
print("===============")
// slow path
let parent = {
"c": undefined,
"a": 1,
"b": undefined,
1: 2
}
let own = {
"a": 1,
"b": 1,
}
own.__proto__ = parent
for (let i in own) {
delete own.a
print(i)
delete parent.c
}
print("===============")
for (let i in own) {
print(i)
}

View File

@ -0,0 +1,18 @@
# Copyright (c) 2023 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.
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_moduletest_action("forin_dictionary_mode") {
deps = []
}

View File

@ -0,0 +1,21 @@
# Copyright (c) 2023 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.
1
c
b
=============
1
a
c
b

View File

@ -0,0 +1,45 @@
/*
* Copyright (c) 2023 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.
*/
/*
* @tc.name:forin_dictionary_mode
* @tc.desc:test forin_dictionary_mode
* @tc.type: FUNC
* @tc.require: issueI84DMO
*/
let parent = {
"c": undefined,
"a": 1,
"b": undefined,
1: 2
}
delete parent.a
let own = {
"a": 1,
"b": 1,
1: 2,
}
delete own.b
own.__proto__ = parent
for (let i in parent) {
print(i)
}
print("=============")
for (let i in own) {
print(i)
}

View File

@ -0,0 +1,18 @@
# Copyright (c) 2023 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.
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_moduletest_action("forin_empty_prototype") {
deps = []
}

View File

@ -0,0 +1,23 @@
# Copyright (c) 2023 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.
a
b
a
b
a
b
c
2
4
s

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2023 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.
*/
/*
* @tc.name:forin_empty_prototype
* @tc.desc:test forin_empty_prototype
* @tc.type: FUNC
* @tc.require: issueI84DMO
*/
// no elements
let fast = {"a":1}
fast.b = "a"
for (let i in fast) {
print(i)
}
// use enum cache
for (let i in fast) {
print(i)
}
fast.c = 1
// invalidate enum cache
for (let i in fast) {
print(i)
}
// has elements
let slow = {"s":222}
slow[2] = "aa"
slow[4] = 1
for (let i in slow) {
print(i)
}

View File

@ -0,0 +1,18 @@
# Copyright (c) 2023 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.
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_moduletest_action("forin_enum_cache") {
deps = []
}

View File

@ -0,0 +1,34 @@
# Copyright (c) 2023 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.
===generate enum cache===
a
b
c
===use enum cache===
a
b
c
===re-generate enum cache===
a
b
c
e
===change attribute===
a
b
c
===delete property===
a
b
c

View File

@ -0,0 +1,69 @@
/*
* Copyright (c) 2023 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.
*/
/*
* @tc.name:forin_enum_cache
* @tc.desc:test forin_enum_cache
* @tc.type: FUNC
* @tc.require: issueI84DMO
*/
let grandparent = {}
let parent = {
"c": undefined,
"a": 1,
"b": undefined,
}
let own = {
"a": 1,
"b": 1,
}
own.__proto__ = parent
parent.__proto__ = grandparent
// generate enum cache
print("===generate enum cache===")
for (let i in own) {
print(i)
}
// use enum cache
print("===use enum cache===")
for (let i in own) {
print(i)
}
// invalid enum cache and re-generate enum cache
print("===re-generate enum cache===")
grandparent['e'] = 1
for (let i in own) {
print(i)
}
// change attribute
print("===change attribute===")
Object.defineProperty(grandparent, "e", {
configurable:true,
enumerable:false,
value:"ggg",
writable:true
})
for (let i in own) {
print(i)
}
// delete property
print("===delete property===")
grandparent['f'] = 1
for (let i in own) {
print(i)
delete grandparent['f']
}

View File

@ -0,0 +1,18 @@
# Copyright (c) 2023 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.
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_moduletest_action("forin_non_empty_prototype") {
deps = []
}

View File

@ -0,0 +1,19 @@
# Copyright (c) 2023 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.
1
a
b
c
2
d

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2023 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.
*/
/*
* @tc.name:forin_non_empty_prototype
* @tc.desc:test forin_non_empty_prototype
* @tc.type: FUNC
* @tc.require: issueI84DMO
*/
let grandparent = {
"a": 1,
"d": undefined,
1: 2,
2: 3
}
let parent = {
"c": undefined,
"a": 1,
"b": undefined,
1: 2
}
let own = {
"a": 1,
"b": 1,
1: 2,
}
own.__proto__ = parent
parent.__proto__ = grandparent
for (let i in own) {
print(i)
}

View File

@ -0,0 +1,18 @@
# Copyright (c) 2023 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.
import("//arkcompiler/ets_runtime/test/test_helper.gni")
host_moduletest_action("forin_special_object") {
deps = []
}

View File

@ -0,0 +1,39 @@
# Copyright (c) 2023 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.
2
8
a
0
1
3
4
5
6
7
9
===============
0
1
2
3
4
5
6
7
8
9
===============
_secret
test
eyeCount

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2023 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.
*/
/*
* @tc.name:forin_specail_object
* @tc.desc:test forin_specail_object
* @tc.type: FUNC
* @tc.require: issueI84DMO
*/
var arr = new Array(10)
for (let i = 0; i < 5; i++) {
arr[i] = i;
}
let parent = new Int8Array(arr);
let self = {
2: "b",
"a": {},
8: []
}
self.__proto__ = parent
for (let i in self) {
print(i)
}
print("===============")
for (let i in parent) {
print(i)
}
print("===============")
const targetObj = {
_secret: 'easily scared',
test: "ss",
eyeCount: 4
};
const proxy_has = new Proxy(targetObj, {
has: (target, key) => {
return key in target;
}
})
for (const key in proxy_has) {
print(key);
}

View File

@ -293,7 +293,7 @@
}
/*
* FastTypeOf(); GetGlobalConstantString(); TaggedIsTrue(); TaggedIsFalse(); TaggedIsNull(); IsString(); IsSymbol(); IsCallable();
* FastTypeOf(); GetGlobalConstantOffset(); TaggedIsTrue(); TaggedIsFalse(); TaggedIsNull(); IsString(); IsSymbol(); IsCallable();
* TaggedObjectIsBigInt(); Int32Mul(); Int64Mul();
*/
/**************HandleTypeofImm8****************/