mirror of
https://gitee.com/openharmony/arkcompiler_ets_runtime
synced 2024-11-26 19:50:55 +00:00
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:
parent
a1adef8e9e
commit
eef37db2c3
1
BUILD.gn
1
BUILD.gn
@ -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",
|
||||
|
@ -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++) {
|
||||
|
@ -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++) {
|
||||
|
@ -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
|
||||
|
@ -464,6 +464,8 @@ private:
|
||||
V(GetSingleCharCodeByIndex) \
|
||||
V(CreateStringBySingleCharCode) \
|
||||
V(FastStringEqual) \
|
||||
V(Getpropiterator) \
|
||||
V(Getnextpropname) \
|
||||
V(JSHClassFindProtoTransitions)
|
||||
|
||||
#define DECL_CALL_SIGNATURE(name) \
|
||||
|
@ -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)));
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -78,6 +78,8 @@ namespace panda::ecmascript::kungfu {
|
||||
V(JsBoundCallInternal) \
|
||||
V(JsProxyCallInternal) \
|
||||
V(CreateStringBySingleCharCode) \
|
||||
V(Getpropiterator) \
|
||||
V(Getnextpropname) \
|
||||
V(GetSingleCharCodeByIndex) \
|
||||
V(FastStringEqual)
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)));
|
||||
|
@ -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, ¬EmptyArray);
|
||||
Bind(&isEmptyArray);
|
||||
{
|
||||
result = Int32(static_cast<int32_t>(EnumCacheKind::SIMPLE));
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(¬EmptyArray);
|
||||
{
|
||||
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, ¬SimpleEnumCache);
|
||||
Bind(&isSimpleEnumCache);
|
||||
{
|
||||
result = True();
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(¬SimpleEnumCache);
|
||||
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, ¬Finish);
|
||||
Bind(¬Finish);
|
||||
GateRef keys = GetKeysFromForInIterator(iter);
|
||||
GateRef receiver = GetObjectFromForInIterator(iter);
|
||||
GateRef cachedHclass = GetCachedHclassFromForInIterator(iter);
|
||||
GateRef kind = GetEnumCacheKind(glue, keys);
|
||||
Branch(IsEnumCacheValid(receiver, cachedHclass, kind), &fastGetKey, ¬EnumCacheValid);
|
||||
Bind(¬EnumCacheValid);
|
||||
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, ¬Number);
|
||||
Bind(&isNumber);
|
||||
{
|
||||
result = GetFunctionPrototype(glue, GlobalEnv::NUMBER_FUNCTION_INDEX);
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(¬Number);
|
||||
Branch(TaggedIsBoolean(obj), &isBoolean, ¬Boolean);
|
||||
Bind(&isBoolean);
|
||||
{
|
||||
result = GetFunctionPrototype(glue, GlobalEnv::BOOLEAN_FUNCTION_INDEX);
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(¬Boolean);
|
||||
Branch(TaggedIsString(obj), &isString, ¬String);
|
||||
Bind(&isString);
|
||||
{
|
||||
result = GetFunctionPrototype(glue, GlobalEnv::STRING_FUNCTION_INDEX);
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(¬String);
|
||||
Branch(TaggedIsSymbol(obj), &isSymbol, ¬Symbol);
|
||||
Bind(&isSymbol);
|
||||
{
|
||||
result = GetFunctionPrototype(glue, GlobalEnv::SYMBOL_FUNCTION_INDEX);
|
||||
Jump(&exit);
|
||||
}
|
||||
Bind(¬Symbol);
|
||||
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, ¬PrimitiveString);
|
||||
Bind(&isJSPrimitiveRef);
|
||||
GateRef value = Load(VariableType::JS_ANY(), obj, IntPtr(JSPrimitiveRef::VALUE_OFFSET));
|
||||
Branch(TaggedIsString(value), &isPrimitiveString, ¬PrimitiveString);
|
||||
Bind(&isPrimitiveString);
|
||||
{
|
||||
numOfElements = GetLengthFromString(value);
|
||||
Jump(¬PrimitiveString);
|
||||
}
|
||||
Bind(¬PrimitiveString);
|
||||
GateRef elements = GetElementsArray(obj);
|
||||
Branch(IsDictionaryMode(elements), &isDictMode, ¬DictMode);
|
||||
Bind(¬DictMode);
|
||||
{
|
||||
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, ¬Hole);
|
||||
Bind(¬Hole);
|
||||
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, ¤tHasNoElements);
|
||||
Bind(¤tHasNoElements);
|
||||
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, ¤tHasNoElements);
|
||||
Bind(¤tHasNoElements);
|
||||
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, ¬SlowKeys);
|
||||
Bind(¬SlowKeys);
|
||||
GateRef hclass = LoadHClass(obj);
|
||||
Branch(IsDictionaryModeByHClass(hclass), &exit, ¬DictionaryMode);
|
||||
Bind(¬DictionaryMode);
|
||||
GateRef enumCache = GetEnumCacheFromHClass(hclass);
|
||||
GateRef kind = GetEnumCacheKind(glue, enumCache);
|
||||
Branch(Int32Equal(kind, Int32(static_cast<int32_t>(EnumCacheKind::SIMPLE))),
|
||||
&checkSimpleEnumCache, ¬SimpleEnumCache);
|
||||
Bind(&checkSimpleEnumCache);
|
||||
{
|
||||
Branch(IsSimpleEnumCacheValid(obj), &enumCacheValid, &exit);
|
||||
}
|
||||
Bind(¬SimpleEnumCache);
|
||||
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();
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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()) {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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);
|
||||
|
313
ecmascript/property_accessor.cpp
Normal file
313
ecmascript/property_accessor.cpp
Normal 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
|
63
ecmascript/property_accessor.h
Normal file
63
ecmascript/property_accessor.h
Normal 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
|
@ -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) \
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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) \
|
||||
|
@ -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++;
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
true
|
||||
false
|
||||
1
|
||||
undefined
|
||||
undefined
|
||||
|
@ -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",
|
||||
|
18
test/moduletest/forin_delete_property/BUILD.gn
Normal file
18
test/moduletest/forin_delete_property/BUILD.gn
Normal 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 = []
|
||||
}
|
23
test/moduletest/forin_delete_property/expect_output.txt
Normal file
23
test/moduletest/forin_delete_property/expect_output.txt
Normal 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
|
@ -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)
|
||||
}
|
18
test/moduletest/forin_dictionary_mode/BUILD.gn
Normal file
18
test/moduletest/forin_dictionary_mode/BUILD.gn
Normal 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 = []
|
||||
}
|
21
test/moduletest/forin_dictionary_mode/expect_output.txt
Normal file
21
test/moduletest/forin_dictionary_mode/expect_output.txt
Normal 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
|
@ -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)
|
||||
}
|
18
test/moduletest/forin_empty_prototype/BUILD.gn
Normal file
18
test/moduletest/forin_empty_prototype/BUILD.gn
Normal 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 = []
|
||||
}
|
23
test/moduletest/forin_empty_prototype/expect_output.txt
Normal file
23
test/moduletest/forin_empty_prototype/expect_output.txt
Normal 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
|
@ -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)
|
||||
}
|
18
test/moduletest/forin_enum_cache/BUILD.gn
Normal file
18
test/moduletest/forin_enum_cache/BUILD.gn
Normal 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 = []
|
||||
}
|
34
test/moduletest/forin_enum_cache/expect_output.txt
Normal file
34
test/moduletest/forin_enum_cache/expect_output.txt
Normal 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
|
69
test/moduletest/forin_enum_cache/forin_enum_cache.js
Normal file
69
test/moduletest/forin_enum_cache/forin_enum_cache.js
Normal 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']
|
||||
}
|
18
test/moduletest/forin_non_empty_prototype/BUILD.gn
Normal file
18
test/moduletest/forin_non_empty_prototype/BUILD.gn
Normal 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 = []
|
||||
}
|
19
test/moduletest/forin_non_empty_prototype/expect_output.txt
Normal file
19
test/moduletest/forin_non_empty_prototype/expect_output.txt
Normal 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
|
@ -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)
|
||||
}
|
18
test/moduletest/forin_special_object/BUILD.gn
Normal file
18
test/moduletest/forin_special_object/BUILD.gn
Normal 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 = []
|
||||
}
|
39
test/moduletest/forin_special_object/expect_output.txt
Normal file
39
test/moduletest/forin_special_object/expect_output.txt
Normal 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
|
56
test/moduletest/forin_special_object/forin_special_object.js
Normal file
56
test/moduletest/forin_special_object/forin_special_object.js
Normal 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);
|
||||
}
|
@ -293,7 +293,7 @@
|
||||
}
|
||||
|
||||
/*
|
||||
* FastTypeOf(); GetGlobalConstantString(); TaggedIsTrue(); TaggedIsFalse(); TaggedIsNull(); IsString(); IsSymbol(); IsCallable();
|
||||
* FastTypeOf(); GetGlobalConstantOffset(); TaggedIsTrue(); TaggedIsFalse(); TaggedIsNull(); IsString(); IsSymbol(); IsCallable();
|
||||
* TaggedObjectIsBigInt(); Int32Mul(); Int64Mul();
|
||||
*/
|
||||
/**************HandleTypeofImm8****************/
|
||||
|
Loading…
Reference in New Issue
Block a user