From aa2409e00ffbee5a089352ed3b32d44c03498e98 Mon Sep 17 00:00:00 2001 From: xliu Date: Tue, 4 Jan 2022 19:30:40 +0800 Subject: [PATCH] 1.add arraylist and ArkPrivate. 2.add special container in stub. 3.fix Switch Signed-off-by: xliu Change-Id: I4082326fa075bce0c5c7bf8e6873282086fe5d6e --- BUILD.gn | 3 + ecmascript/builtins.cpp | 37 ++- ecmascript/builtins.h | 4 + ecmascript/builtins/builtins_ark_tools.h | 2 +- ecmascript/compiler/circuit_builder.cpp | 2 +- ecmascript/compiler/circuit_builder.h | 4 +- ecmascript/compiler/compile_llvm_lib.sh | 2 +- ecmascript/compiler/fast_stub_define.h | 1 + ecmascript/compiler/gate.cpp | 2 +- ecmascript/compiler/llvm_ir_builder.cpp | 5 +- ecmascript/compiler/stub-inl.h | 33 +- ecmascript/compiler/stub.cpp | 293 +++++++++++------- ecmascript/compiler/stub.h | 12 +- ecmascript/compiler/stub_descriptor.cpp | 16 + .../containers/containers_arraylist.cpp | 69 +++++ ecmascript/containers/containers_arraylist.h | 31 ++ ecmascript/containers/containers_private.cpp | 191 ++++++++++++ ecmascript/containers/containers_private.h | 53 ++++ ecmascript/dump.cpp | 21 ++ ecmascript/hprof/heap_snapshot.cpp | 2 + .../interpreter/fast_runtime_stub-inl.h | 43 +++ ecmascript/interpreter/fast_runtime_stub.h | 5 + ecmascript/js_arraylist.cpp | 121 ++++++++ ecmascript/js_arraylist.h | 64 ++++ ecmascript/js_hclass.h | 17 + ecmascript/js_tagged_value-inl.h | 10 + ecmascript/js_tagged_value.cpp | 77 +++++ ecmascript/js_tagged_value.h | 7 + ecmascript/mem/object_xray-inl.h | 5 + ecmascript/message_string.h | 12 +- ecmascript/object_factory.cpp | 4 + ecmascript/runtime_trampolines.cpp | 9 + ecmascript/runtime_trampolines.h | 1 + .../snapshot/mem/snapshot_serialize.cpp | 5 + ecmascript/tests/dump_test.cpp | 7 + test/moduletest/BUILD.gn | 1 + test/moduletest/container/BUILD.gn | 23 ++ test/moduletest/container/container.js | 17 + .../container/container_arraylist.js | 30 ++ test/moduletest/container/expect_output.txt | 16 + 40 files changed, 1105 insertions(+), 152 deletions(-) create mode 100644 ecmascript/containers/containers_arraylist.cpp create mode 100644 ecmascript/containers/containers_arraylist.h create mode 100644 ecmascript/containers/containers_private.cpp create mode 100644 ecmascript/containers/containers_private.h create mode 100644 ecmascript/js_arraylist.cpp create mode 100644 ecmascript/js_arraylist.h create mode 100644 test/moduletest/container/BUILD.gn create mode 100644 test/moduletest/container/container.js create mode 100644 test/moduletest/container/container_arraylist.js create mode 100644 test/moduletest/container/expect_output.txt diff --git a/BUILD.gn b/BUILD.gn index e0b5115e..a749a091 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -281,6 +281,8 @@ ecma_source = [ "ecmascript/builtins/builtins_weak_map.cpp", "ecmascript/builtins/builtins_weak_set.cpp", "ecmascript/class_linker/panda_file_translator.cpp", + "ecmascript/containers/containers_arraylist.cpp", + "ecmascript/containers/containers_private.cpp", "ecmascript/cpu_profiler/cpu_profiler.cpp", "ecmascript/cpu_profiler/profile_processor.cpp", "ecmascript/cpu_profiler/profile_generator.cpp", @@ -318,6 +320,7 @@ ecma_source = [ "ecmascript/js_array.cpp", "ecmascript/js_array_iterator.cpp", "ecmascript/js_arraybuffer.cpp", + "ecmascript/js_arraylist.cpp", "ecmascript/js_async_function.cpp", "ecmascript/js_dataview.cpp", "ecmascript/js_date.cpp", diff --git a/ecmascript/builtins.cpp b/ecmascript/builtins.cpp index b073d9e3..b23c4d65 100644 --- a/ecmascript/builtins.cpp +++ b/ecmascript/builtins.cpp @@ -54,9 +54,11 @@ #include "ecmascript/builtins/builtins_typedarray.h" #include "ecmascript/builtins/builtins_weak_map.h" #include "ecmascript/builtins/builtins_weak_set.h" +#include "ecmascript/containers/containers_private.h" #include "ecmascript/ecma_runtime_call_info.h" #include "ecmascript/js_array.h" #include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_arraylist.h" #include "ecmascript/js_array_iterator.h" #include "ecmascript/js_async_function.h" #include "ecmascript/js_collator.h" @@ -132,6 +134,7 @@ using RelativeTimeFormat = builtins::BuiltinsRelativeTimeFormat; using NumberFormat = builtins::BuiltinsNumberFormat; using Collator = builtins::BuiltinsCollator; using PluralRules = builtins::BuiltinsPluralRules; +using ContainersPrivate = containers::ContainersPrivate; void Builtins::Initialize(const JSHandle &env, JSThread *thread) { @@ -306,9 +309,14 @@ void Builtins::InitializeGlobalObject(const JSHandle &env, const JSHa JSRuntimeOptions options = vm_->GetJSOptions(); if (options.IsEnableArkTools()) { - SetConstant(globalObject, "ArkTools", InitializeArkTools(env).GetTaggedValue()); + JSHandle arkTools(InitializeArkTools(env)); + SetConstantObject(globalObject, "ArkTools", arkTools); } + // Set ArkPrivate + JSHandle arkPrivate(InitializeArkPrivate(env)); + SetConstantObject(globalObject, "ArkPrivate", arkPrivate); + // Global object function SetFunction(env, globalObject, "eval", Global::NotSupportEval, FunctionLength::ONE); SetFunction(env, globalObject, "isFinite", Global::IsFinite, FunctionLength::ONE); @@ -2211,6 +2219,15 @@ void Builtins::SetFunction(const JSHandle &env, const JSHandle &env, const JSHandle &obj, const char *key, + EcmaEntrypoint func, int length) const +{ + JSHandle keyString(factory_->NewFromString(key)); + JSHandle function = NewFunction(env, keyString, func, length); + PropertyDescriptor descriptor(thread_, JSHandle(function), false, false, false); + JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); +} + template void Builtins::SetFunctionAtSymbol(const JSHandle &env, const JSHandle &obj, const JSHandle &symbol, const char *name, EcmaEntrypoint func, @@ -2274,6 +2291,14 @@ void Builtins::SetConstant(const JSHandle &obj, const char *key, JSTag PropertyDescriptor descriptor(thread_, JSHandle(thread_, value), false, false, false); JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); } + +void Builtins::SetConstantObject(const JSHandle &obj, const char *key, JSHandle &value) const +{ + JSHandle keyString(factory_->NewFromString(key)); + PropertyDescriptor descriptor(thread_, value, false, false, false); + JSObject::DefineOwnProperty(thread_, obj, keyString, descriptor); +} + void Builtins::SetGlobalThis(const JSHandle &obj, const char *key, const JSHandle &globalValue) { JSHandle keyString(factory_->NewFromString(key)); @@ -2734,10 +2759,16 @@ void Builtins::InitializePluralRules(const JSHandle &env) JSHandle Builtins::InitializeArkTools(const JSHandle &env) const { - [[maybe_unused]] EcmaHandleScope scope(thread_); - JSHandle tools = factory_->NewEmptyJSObject(); SetFunction(env, tools, "print", builtins::BuiltinsArkTools::ObjectDump, FunctionLength::ZERO); return tools; } + +JSHandle Builtins::InitializeArkPrivate(const JSHandle &env) const +{ + JSHandle arkPrivate = factory_->NewEmptyJSObject(); + SetFrozenFunction(env, arkPrivate, "Load", ContainersPrivate::Load, FunctionLength::ZERO); + SetConstant(arkPrivate, "ArrayList", JSTaggedValue(static_cast(containers::ContainerTag::ArrayList))); + return arkPrivate; +} } // namespace panda::ecmascript diff --git a/ecmascript/builtins.h b/ecmascript/builtins.h index 63fa4b4f..ac9411ef 100644 --- a/ecmascript/builtins.h +++ b/ecmascript/builtins.h @@ -209,6 +209,10 @@ private: void SetGetter(const JSHandle &obj, const JSHandle &key, const JSHandle &getter) const; JSHandle InitializeArkTools(const JSHandle &env) const; + JSHandle InitializeArkPrivate(const JSHandle &env) const; + void SetConstantObject(const JSHandle &obj, const char *key, JSHandle &value) const; + void SetFrozenFunction(const JSHandle &env, const JSHandle &obj, const char *key, + EcmaEntrypoint func, int length) const; }; } // namespace panda::ecmascript #endif // ECMASCRIPT_BUILTINS_H diff --git a/ecmascript/builtins/builtins_ark_tools.h b/ecmascript/builtins/builtins_ark_tools.h index f5256bb6..aaadacca 100644 --- a/ecmascript/builtins/builtins_ark_tools.h +++ b/ecmascript/builtins/builtins_ark_tools.h @@ -23,7 +23,7 @@ namespace panda::ecmascript::builtins { class BuiltinsArkTools : public base::BuiltinsBase { public: // Make sure the ECMASCRIPT_OBJECT_DUMP in config.h has been opened before use it - // Use through arkTools.print(msg, [obj1, obj2, ... objn]) in js + // Use through ArkTools.print(msg, [obj1, obj2, ... objn]) in js static JSTaggedValue ObjectDump(EcmaRuntimeCallInfo *msg); }; } // namespace panda::ecmascript::builtins diff --git a/ecmascript/compiler/circuit_builder.cpp b/ecmascript/compiler/circuit_builder.cpp index d4f8472a..4bf38c72 100644 --- a/ecmascript/compiler/circuit_builder.cpp +++ b/ecmascript/compiler/circuit_builder.cpp @@ -155,7 +155,7 @@ GateRef CircuitBuilder::NewIfFalse(GateRef ifBranch) return circuit_->NewGate(OpCode(OpCode::IF_FALSE), 0, { ifBranch }, TypeCode::NOTYPE); } -GateRef CircuitBuilder::NewSwitchCase(GateRef switchBranch, int32_t value) +GateRef CircuitBuilder::NewSwitchCase(GateRef switchBranch, int64_t value) { return circuit_->NewGate(OpCode(OpCode::SWITCH_CASE), value, { switchBranch }, TypeCode::NOTYPE); } diff --git a/ecmascript/compiler/circuit_builder.h b/ecmascript/compiler/circuit_builder.h index 6d2a508f..060e96b8 100644 --- a/ecmascript/compiler/circuit_builder.h +++ b/ecmascript/compiler/circuit_builder.h @@ -53,7 +53,7 @@ public: GateRef LoopEnd(GateRef state); GateRef NewIfTrue(GateRef ifBranch); GateRef NewIfFalse(GateRef ifBranch); - GateRef NewSwitchCase(GateRef switchBranch, int32_t value); + GateRef NewSwitchCase(GateRef switchBranch, int64_t value); GateRef NewDefaultCase(GateRef switchBranch); GateRef NewLoadGate(MachineType type, GateRef val, GateRef depend); GateRef NewStoreGate(MachineType type, GateRef ptr, GateRef val, GateRef depend); @@ -89,4 +89,4 @@ private: }; } // namespace panda::ecmascript::kungfu -#endif // ECMASCRIPT_COMPILER_CIRCUIT_BUILDER_H \ No newline at end of file +#endif // ECMASCRIPT_COMPILER_CIRCUIT_BUILDER_H diff --git a/ecmascript/compiler/compile_llvm_lib.sh b/ecmascript/compiler/compile_llvm_lib.sh index 71e06e55..90815a0e 100755 --- a/ecmascript/compiler/compile_llvm_lib.sh +++ b/ecmascript/compiler/compile_llvm_lib.sh @@ -47,4 +47,4 @@ else fi fi -echo "++++++++++++++++++++++++++++++++++" \ No newline at end of file +echo "++++++++++++++++++++++++++++++++++" diff --git a/ecmascript/compiler/fast_stub_define.h b/ecmascript/compiler/fast_stub_define.h index 0abef158..72f1c4f8 100644 --- a/ecmascript/compiler/fast_stub_define.h +++ b/ecmascript/compiler/fast_stub_define.h @@ -26,6 +26,7 @@ namespace panda::ecmascript::kungfu { V(CallGetter2, 4) \ V(CallInternalGetter, 3) \ V(ThrowTypeError, 2) \ + V(JSArrayListSetByIndex, 4) \ V(JSProxySetProperty, 6) \ V(GetHash32, 2) \ V(FindElementWithCache, 4) \ diff --git a/ecmascript/compiler/gate.cpp b/ecmascript/compiler/gate.cpp index ecd2aff5..7c2c47b4 100644 --- a/ecmascript/compiler/gate.cpp +++ b/ecmascript/compiler/gate.cpp @@ -1349,4 +1349,4 @@ bool OpCode::IsNop() const { return (op_ == OpCode::NOP); } -} // namespace panda::ecmascript::kungfu \ No newline at end of file +} // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/llvm_ir_builder.cpp b/ecmascript/compiler/llvm_ir_builder.cpp index adedefdf..82fb7aa0 100644 --- a/ecmascript/compiler/llvm_ir_builder.cpp +++ b/ecmascript/compiler/llvm_ir_builder.cpp @@ -1013,13 +1013,14 @@ void LLVMIRBuilder::VisitSwitch(GateRef gate, GateRef input, const std::vector(caseNum - 1); i++) { + for (int i = 0; i < static_cast(caseNum); i++) { if (circuit_->GetOpCode(outList[i]) == OpCode::DEFAULT_CASE) { continue; } curOutBB = EnsureBasicBlock(instIdMapBbId_[circuit_->GetId(outList[i])]); llvmCurOutBB = curOutBB->GetImpl()->llvm_bb_; - LLVMAddCase(result, LLVMConstInt(LLVMInt64Type(), circuit_->GetBitField(outList[i]), 0), llvmCurOutBB); + LLVMAddCase(result, LLVMConstInt(ConvertLLVMTypeFromGate(input), circuit_->GetBitField(outList[i]), 0), + llvmCurOutBB); } EndCurrentBlock(); gateToLLVMMaps_[gate] = result; diff --git a/ecmascript/compiler/stub-inl.h b/ecmascript/compiler/stub-inl.h index dd405808..5c2a2665 100644 --- a/ecmascript/compiler/stub-inl.h +++ b/ecmascript/compiler/stub-inl.h @@ -1360,32 +1360,6 @@ GateRef Stub::GetValueFromTaggedArray(MachineType returnType, GateRef array, Gat return Load(returnType, array, dataOffset); } -GateRef Stub::GetElementRepresentation(GateRef hClass) -{ - GateRef bitfieldOffset = GetArchRelateConstant(JSHClass::BIT_FIELD_OFFSET); - GateRef bitfield = Load(MachineType::UINT32, hClass, bitfieldOffset); - return Word32And( - Word32LSR(bitfield, GetInt32Constant(JSHClass::ElementRepresentationBits::START_BIT)), - GetInt32Constant(((1LLU << JSHClass::ElementRepresentationBits::SIZE) - 1))); -} - -void Stub::SetElementRepresentation(GateRef glue, GateRef hClass, GateRef value) -{ - GateRef bitfieldOffset = GetArchRelateConstant(JSHClass::BIT_FIELD_OFFSET); - GateRef oldValue = Load(MachineType::UINT32, hClass, bitfieldOffset); - GateRef oldWithMask = Word32And(oldValue, - GetInt32Constant(~static_cast(JSHClass::ElementRepresentationBits::Mask()))); - GateRef newValue = Word32LSR(value, GetInt32Constant(JSHClass::ElementRepresentationBits::START_BIT)); - Store(MachineType::UINT32, glue, hClass, bitfieldOffset, Word32Or(oldWithMask, newValue)); -} - -void Stub::UpdateAndStoreRepresention(GateRef glue, GateRef hClass, GateRef value) -{ - GateRef rep = GetElementRepresentation(hClass); - GateRef newRep = UpdateRepresention(rep, value); - SetElementRepresentation(glue, hClass, newRep); -} - void Stub::UpdateValueAndAttributes(GateRef glue, GateRef elements, GateRef index, GateRef value, GateRef attr) { GateRef arrayIndex = @@ -1407,6 +1381,13 @@ GateRef Stub::IsSpecialIndexedObj(GateRef jsType) return Int32GreaterThan(jsType, GetInt32Constant(static_cast(JSType::JS_ARRAY))); } +GateRef Stub::IsSpecialContainer(GateRef jsType) +{ + return TruncInt32ToInt1(Word32And( + ZExtInt1ToInt32(Int32GreaterThanOrEqual(jsType, GetInt32Constant(static_cast(JSType::JS_ARRAY_LIST)))), + ZExtInt1ToInt32(Int32LessThanOrEqual(jsType, GetInt32Constant(static_cast(JSType::JS_QUEUE)))))); +} + GateRef Stub::IsAccessorInternal(GateRef value) { return Word32Equal(GetObjectType(LoadHClass(value)), diff --git a/ecmascript/compiler/stub.cpp b/ecmascript/compiler/stub.cpp index 14f8a365..4535c339 100644 --- a/ecmascript/compiler/stub.cpp +++ b/ecmascript/compiler/stub.cpp @@ -16,6 +16,7 @@ #include "ecmascript/compiler/stub.h" #include "ecmascript/compiler/llvm_ir_builder.h" #include "ecmascript/compiler/stub-inl.h" +#include "ecmascript/js_arraylist.h" #include "ecmascript/js_object.h" #include "ecmascript/tagged_hash_table-inl.h" #include "libpandabase/macros.h" @@ -306,7 +307,7 @@ void Stub::Branch(GateRef condition, Label *trueLabel, Label *falseLabel) env_.SetCurrentLabel(nullptr); } -void Stub::Switch(GateRef index, Label *defaultLabel, int32_t *keysValue, Label *keysLabel, int numberOfKeys) +void Stub::Switch(GateRef index, Label *defaultLabel, int64_t *keysValue, Label *keysLabel, int numberOfKeys) { auto currentLabel = env_.GetCurrentLabel(); auto currentControl = currentLabel->GetControl(); @@ -1138,102 +1139,6 @@ GateRef Stub::TaggedToRepresentation(GateRef value) return ret; } -GateRef Stub::UpdateRepresention(GateRef oldRep, GateRef value) -{ - auto env = GetEnvironment(); - Label entry(env); - env->PushCurrentLabel(&entry); - Label exit(env); - DEFVARIABLE(resultRep, MachineType::INT64, oldRep); - Label isMixedRep(env); - Label notMiexedRep(env); - Branch(Word64Equal(oldRep, GetWord64Constant(static_cast(Representation::MIXED))), - &isMixedRep, ¬MiexedRep); - Bind(&isMixedRep); - Jump(&exit); - Bind(¬MiexedRep); - { - GateRef newRep = TaggedToRepresentation(value); - Label isNoneRep(env); - Label notNoneRep(env); - Branch(Word64Equal(oldRep, GetWord64Constant(static_cast(Representation::NONE))), - &isNoneRep, ¬NoneRep); - Bind(&isNoneRep); - { - resultRep = newRep; - Jump(&exit); - } - Bind(¬NoneRep); - { - Label isEqaulNewRep(env); - Label notEqaualNewRep(env); - Branch(Word64NotEqual(oldRep, newRep), &isEqaulNewRep, ¬EqaualNewRep); - Bind(&isEqaulNewRep); - { - resultRep = newRep; - Jump(&exit); - } - Bind(¬EqaualNewRep); - { - Label defaultLabel(env); - Label intLabel(env); - Label doubleLabel(env); - Label numberLabel(env); - Label objectLabel(env); - // 4 : 4 means that there are 4 args in total. - std::array repCaseLabels = { - intLabel, - doubleLabel, - numberLabel, - objectLabel, - }; - // 4 : 4 means that there are 4 args in total. - std::array keyValues = { - static_cast(Representation::INT), - static_cast(Representation::DOUBLE), - static_cast(Representation::NUMBER), - static_cast(Representation::OBJECT), - }; - // 4 : 4 means that there are 4 cases in total. - Switch(oldRep, &defaultLabel, keyValues.data(), repCaseLabels.data(), 4); - Bind(&intLabel); - Jump(&numberLabel); - Bind(&doubleLabel); - Jump(&numberLabel); - Bind(&numberLabel); - { - Label isObjectNewRep(env); - Label notObjectNewRep(env); - Branch(Word64NotEqual(newRep, GetWord64Constant( - static_cast(Representation::OBJECT))), - ¬ObjectNewRep, &isObjectNewRep); - Bind(¬ObjectNewRep); - { - resultRep = GetWord64Constant(static_cast(Representation::NUMBER)); - Jump(&exit); - } - Bind(&isObjectNewRep); - { - resultRep = GetWord64Constant(static_cast(Representation::MIXED)); - Jump(&exit); - } - } - Bind(&objectLabel); - { - resultRep = GetWord64Constant(static_cast(Representation::MIXED)); - Jump(&exit); - } - Bind(&defaultLabel); - Jump(&exit); - } - } - } - Bind(&exit); - auto ret = *resultRep; - env->PopCurrentLabel(); - return ret; -} - GateRef Stub::Store(MachineType type, GateRef glue, GateRef base, GateRef offset, GateRef value) { auto depend = env_.GetCurrentLabel()->GetDepend(); @@ -2023,8 +1928,20 @@ GateRef Stub::GetPropertyByIndex(GateRef glue, GateRef receiver, GateRef index) Branch(IsSpecialIndexedObj(jsType), &isSpecialIndexed, ¬SpecialIndexed); Bind(&isSpecialIndexed); { - result = GetHoleConstant(); - Jump(&exit); + Label isSpecialContainer(env); + Label notSpecialContainer(env); + // Add SpecialContainer + Branch(IsSpecialContainer(jsType), &isSpecialContainer, ¬SpecialContainer); + Bind(&isSpecialContainer); + { + result = GetContainerProperty(glue, *holder, index, jsType); + Jump(&exit); + } + Bind(¬SpecialContainer); + { + result = GetHoleConstant(); + Jump(&exit); + } } Bind(¬SpecialIndexed); { @@ -2402,8 +2319,22 @@ GateRef Stub::SetPropertyByIndex(GateRef glue, GateRef receiver, GateRef index, Label notSpecialIndex(env); Branch(IsSpecialIndexedObj(jsType), &isSpecialIndex, ¬SpecialIndex); Bind(&isSpecialIndex); - returnValue = GetHoleConstant(MachineType::UINT64); - Jump(&exit); + { + Label isSpecialContainer(env); + Label notSpecialContainer(env); + // Add SpecialContainer + Branch(IsSpecialContainer(jsType), &isSpecialContainer, ¬SpecialContainer); + Bind(&isSpecialContainer); + { + returnValue = SetContainerProperty(glue, *holder, index, value, jsType); + Jump(&exit); + } + Bind(¬SpecialContainer); + { + returnValue = GetHoleConstant(MachineType::UINT64); + Jump(&exit); + } + } Bind(¬SpecialIndex); { GateRef elements = GetElementsArray(*holder); @@ -2510,8 +2441,25 @@ GateRef Stub::SetPropertyByName(GateRef glue, GateRef receiver, GateRef key, Gat // if branch condition : IsSpecialIndexedObj(jsType) Branch(IsSpecialIndexedObj(jsType), &isSIndexObj, ¬SIndexObj); Bind(&isSIndexObj); - result = GetHoleConstant(MachineType::UINT64); - Jump(&exit); + { + Label isSpecialContainer(env); + Label notSpecialContainer(env); + // Add SpecialContainer + Branch(IsSpecialContainer(jsType), &isSpecialContainer, ¬SpecialContainer); + Bind(&isSpecialContainer); + { + GateRef taggedId = GetInt32Constant(GET_MESSAGE_STRING_ID(CanNotSetPropertyOnContainer)); + CallRuntime(GET_STUBDESCRIPTOR(ThrowTypeError), glue, GetWord64Constant(FAST_STUB_ID(ThrowTypeError)), + {glue, taggedId}); + result = GetExceptionConstant(MachineType::UINT64); + Jump(&exit); + } + Bind(¬SpecialContainer); + { + result = GetHoleConstant(MachineType::UINT64); + Jump(&exit); + } + } Bind(¬SIndexObj); { Label isDicMode(env); @@ -2730,8 +2678,25 @@ GateRef Stub::SetPropertyByNameWithOwn(GateRef glue, GateRef receiver, GateRef k // if branch condition : IsSpecialIndexedObj(jsType) Branch(IsSpecialIndexedObj(jsType), &isSIndexObj, ¬SIndexObj); Bind(&isSIndexObj); - result = GetHoleConstant(MachineType::UINT64); - Jump(&exit); + { + Label isSpecialContainer(env); + Label notSpecialContainer(env); + // Add SpecialContainer + Branch(IsSpecialContainer(jsType), &isSpecialContainer, ¬SpecialContainer); + Bind(&isSpecialContainer); + { + GateRef taggedId = GetInt32Constant(GET_MESSAGE_STRING_ID(CanNotSetPropertyOnContainer)); + CallRuntime(GET_STUBDESCRIPTOR(ThrowTypeError), glue, GetWord64Constant(FAST_STUB_ID(ThrowTypeError)), + {glue, taggedId}); + result = GetExceptionConstant(MachineType::UINT64); + Jump(&exit); + } + Bind(¬SpecialContainer); + { + result = GetHoleConstant(MachineType::UINT64); + Jump(&exit); + } + } Bind(¬SIndexObj); { Label isDicMode(env); @@ -2949,4 +2914,122 @@ void Stub::NotifyHClassChanged(GateRef glue, GateRef oldHClass, GateRef newHClas env->PopCurrentLabel(); return; } + +GateRef Stub::GetContainerProperty(GateRef glue, GateRef receiver, GateRef index, GateRef jsType) +{ + auto env = GetEnvironment(); + Label entry(env); + env->PushCurrentLabel(&entry); + Label exit(env); + DEFVARIABLE(result, MachineType::TAGGED, GetUndefinedConstant()); + + Label arrayListLabel(env); + Label queueLabel(env); + Label defaultLabel(env); + std::array repCaseLabels = { // 2 : 2 means that there are 2 args in total. + arrayListLabel, + queueLabel, + }; + std::array keyValues = { // 2 : 2 means that there are 2 args in total. + static_cast(JSType::JS_ARRAY_LIST), + static_cast(JSType::JS_QUEUE), + }; + // 2 : 2 means that there are 2 cases. + Switch(ZExtInt32ToInt64(jsType), &defaultLabel, keyValues.data(), repCaseLabels.data(), 2); + Bind(&arrayListLabel); + { + result = JSArrayListGet(glue, receiver, index); + Jump(&exit); + } + Bind(&queueLabel); + { + Jump(&exit); + } + Bind(&defaultLabel); + { + Jump(&exit); + } + + Bind(&exit); + auto ret = *result; + env->PopCurrentLabel(); + return ret; +} + +GateRef Stub::JSArrayListGet(GateRef glue, GateRef receiver, GateRef index) +{ + auto env = GetEnvironment(); + Label entry(env); + env->PushCurrentLabel(&entry); + Label exit(env); + DEFVARIABLE(result, MachineType::TAGGED, GetUndefinedConstant()); + + GateRef lengthOffset = GetArchRelateConstant(panda::ecmascript::JSArrayList::LENGTH_OFFSET); + GateRef length = TaggedCastToInt32(Load(MachineType::UINT64, receiver, lengthOffset)); + Label isVailedIndex(env); + Label notValidIndex(env); + Branch(TruncInt32ToInt1(Word32And(ZExtInt1ToInt32(Int32GreaterThanOrEqual(index, GetInt32Constant(0))), + ZExtInt1ToInt32(Int32LessThan(index, length)))), &isVailedIndex, ¬ValidIndex); + Bind(&isVailedIndex); + { + GateRef elements = GetElementsArray(receiver); + result = GetValueFromTaggedArray(MachineType::TAGGED, elements, index); + Jump(&exit); + } + Bind(¬ValidIndex); + { + GateRef taggedId = GetInt32Constant(GET_MESSAGE_STRING_ID(GetPropertyOutOfBounds)); + CallRuntime(GET_STUBDESCRIPTOR(ThrowTypeError), glue, GetWord64Constant(FAST_STUB_ID(ThrowTypeError)), + {glue, taggedId}); + result = GetExceptionConstant(); + Jump(&exit); + } + + Bind(&exit); + auto ret = *result; + env->PopCurrentLabel(); + return ret; +} + +GateRef Stub::SetContainerProperty(GateRef glue, GateRef receiver, GateRef index, GateRef value, GateRef jsType) +{ + auto env = GetEnvironment(); + Label entry(env); + env->PushCurrentLabel(&entry); + Label exit(env); + DEFVARIABLE(result, MachineType::UINT64, GetUndefinedConstant(MachineType::UINT64)); + Label arrayListLabel(env); + Label queueLabel(env); + Label defaultLabel(env); + std::array repCaseLabels = { // 2 : 2 means that there are 2 args in total. + arrayListLabel, + queueLabel, + }; + std::array keyValues = { // 2 : 2 means that there are 2 args in total. + static_cast(JSType::JS_ARRAY_LIST), + static_cast(JSType::JS_QUEUE), + }; + // 2 : 2 means that there are 2 cases. + Switch(ZExtInt32ToInt64(jsType), &defaultLabel, keyValues.data(), repCaseLabels.data(), 2); + Bind(&arrayListLabel); + { + StubDescriptor *jsarraylistSetByIndex = GET_STUBDESCRIPTOR(JSArrayListSetByIndex); + CallRuntime(jsarraylistSetByIndex, glue, GetWord64Constant(FAST_STUB_ID(JSArrayListSetByIndex)), + { glue, receiver, index, value }); + Jump(&exit); + } + Bind(&queueLabel); + { + Jump(&exit); + } + Bind(&defaultLabel); + { + Jump(&exit); + } + + Bind(&exit); + auto ret = *result; + env->PopCurrentLabel(); + return ret; +} } // namespace panda::ecmascript::kungfu diff --git a/ecmascript/compiler/stub.h b/ecmascript/compiler/stub.h index 9d310bed..8941e79f 100644 --- a/ecmascript/compiler/stub.h +++ b/ecmascript/compiler/stub.h @@ -450,7 +450,7 @@ public: inline void Bind(Label *label); void Jump(Label *label); void Branch(GateRef condition, Label *trueLabel, Label *falseLabel); - void Switch(GateRef index, Label *defaultLabel, int32_t *keysValue, Label *keysLabel, int numberOfKeys); + void Switch(GateRef index, Label *defaultLabel, int64_t *keysValue, Label *keysLabel, int numberOfKeys); void Seal(Label *label) { label->Seal(); @@ -638,13 +638,10 @@ public: void ThrowTypeAndReturn(GateRef glue, int messageId, GateRef val); inline GateRef GetValueFromTaggedArray(MachineType returnType, GateRef elements, GateRef index); inline void SetValueToTaggedArray(MachineType valType, GateRef glue, GateRef array, GateRef index, GateRef val); - inline GateRef GetElementRepresentation(GateRef hClass); - inline void SetElementRepresentation(GateRef glue, GateRef hClass, GateRef value); inline void UpdateValueAndAttributes(GateRef glue, GateRef elements, GateRef index, GateRef value, GateRef attr); inline GateRef IsSpecialIndexedObj(GateRef jsType); + inline GateRef IsSpecialContainer(GateRef jsType); inline GateRef IsAccessorInternal(GateRef value); - inline void UpdateAndStoreRepresention(GateRef glue, GateRef hClass, GateRef value); - GateRef UpdateRepresention(GateRef oldRep, GateRef value); template GateRef GetAttributesFromDictionary(GateRef elements, GateRef entry); template @@ -730,6 +727,11 @@ public: GateRef SetPropertyByNameWithOwn(GateRef glue, GateRef receiver, GateRef key, GateRef value); // Do not crawl the prototype chain + // Add SpecialContainer + GateRef GetContainerProperty(GateRef glue, GateRef receiver, GateRef index, GateRef jsType); + GateRef SetContainerProperty(GateRef glue, GateRef receiver, GateRef index, GateRef value, GateRef jsType); + GateRef JSArrayListGet(GateRef glue, GateRef receiver, GateRef index); + private: Environment env_; std::string methodName_; diff --git a/ecmascript/compiler/stub_descriptor.cpp b/ecmascript/compiler/stub_descriptor.cpp index a1e6828b..ba6b21b2 100644 --- a/ecmascript/compiler/stub_descriptor.cpp +++ b/ecmascript/compiler/stub_descriptor.cpp @@ -720,6 +720,22 @@ CALL_STUB_INIT_DESCRIPTOR(LoopTest1) descriptor->SetStubKind(StubDescriptor::CallStubKind::TEST_FUNC); } +CALL_STUB_INIT_DESCRIPTOR(JSArrayListSetByIndex) +{ + // 4 : 4 input parameters + StubDescriptor arraylistSetByIndex("JSArrayListSetByIndex", 0, 4, ArgumentsOrder::DEFAULT_ORDER, MachineType::NONE); + *descriptor = arraylistSetByIndex; + // 4 : 4 input parameters + std::array params = { + MachineType::NATIVE_POINTER, + MachineType::TAGGED_POINTER, + MachineType::INT32, + MachineType::TAGGED, + }; + descriptor->SetParameters(params.data()); + descriptor->SetStubKind(StubDescriptor::CallStubKind::RUNTIME_STUB); +} + void FastStubDescriptors::InitializeStubDescriptors() { // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) diff --git a/ecmascript/containers/containers_arraylist.cpp b/ecmascript/containers/containers_arraylist.cpp new file mode 100644 index 00000000..051a5e97 --- /dev/null +++ b/ecmascript/containers/containers_arraylist.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "containers_arraylist.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_arraylist.h" +#include "ecmascript/object_factory.h" +#include "ecmascript/tagged_array-inl.h" + +namespace panda::ecmascript::containers { +JSTaggedValue ContainersArrayList::ArrayListConstructor(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Constructor); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle newTarget = GetNewTarget(argv); + if (newTarget->IsUndefined()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "new target can't be undefined", JSTaggedValue::Exception()); + } + JSHandle constructor = GetConstructor(argv); + JSHandle obj = factory->NewJSObjectByConstructor(JSHandle(constructor), newTarget); + + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + + return obj.GetTaggedValue(); +} + +JSTaggedValue ContainersArrayList::Add(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Add); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle self = GetThis(argv); + + if (!self->IsJSArrayList()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "obj is not JSArrayList", JSTaggedValue::Exception()); + } + + JSHandle value(GetCallArg(argv, 0)); + JSArrayList::Add(thread, JSHandle::Cast(self), value); + + RETURN_VALUE_IF_ABRUPT_COMPLETION(thread, JSTaggedValue::False()); + return JSTaggedValue::True(); +} + +JSTaggedValue ContainersArrayList::Iterator(EcmaRuntimeCallInfo *argv) +{ + ASSERT(argv); + BUILTINS_API_TRACE(argv->GetThread(), ArrayList, Iterator); + JSThread *thread = argv->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + return JSTaggedValue::Undefined(); +} +} // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_arraylist.h b/ecmascript/containers/containers_arraylist.h new file mode 100644 index 00000000..9a23eb36 --- /dev/null +++ b/ecmascript/containers/containers_arraylist.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ECMASCRIPT_CONTAINERS_CONTAINERS_ARRAYLIST_H +#define ECMASCRIPT_CONTAINERS_CONTAINERS_ARRAYLIST_H + +#include "ecmascript/base/builtins_base.h" +#include "ecmascript/ecma_runtime_call_info.h" + +namespace panda::ecmascript::containers { +class ContainersArrayList : public base::BuiltinsBase { +public: + static JSTaggedValue ArrayListConstructor(EcmaRuntimeCallInfo *argv); + + static JSTaggedValue Add(EcmaRuntimeCallInfo *argv); + static JSTaggedValue Iterator(EcmaRuntimeCallInfo *argv); +}; +} // namespace panda::ecmascript::containers +#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_ARRAYLIST_H diff --git a/ecmascript/containers/containers_private.cpp b/ecmascript/containers/containers_private.cpp new file mode 100644 index 00000000..4e736b5c --- /dev/null +++ b/ecmascript/containers/containers_private.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/containers/containers_private.h" + +#include "containers_arraylist.h" +#include "ecmascript/global_env.h" +#include "ecmascript/interpreter/fast_runtime_stub-inl.h" +#include "ecmascript/js_arraylist.h" +#include "ecmascript/js_function.h" + +namespace panda::ecmascript::containers { +JSTaggedValue ContainersPrivate::Load(EcmaRuntimeCallInfo *msg) +{ + ASSERT(msg); + JSThread *thread = msg->GetThread(); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle argv = GetCallArg(msg, 0); + JSHandle thisValue(GetThis(msg)); + + uint32_t tag = 0; + if (!JSTaggedValue::ToElementIndex(argv.GetTaggedValue(), &tag) || tag >= ContainerTag::END) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Incorrect input parameters", JSTaggedValue::Exception()); + } + + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSTaggedValue res = JSTaggedValue::Undefined(); + switch (tag) { + case ContainerTag::ArrayList: { + JSHandle key(factory->NewFromCanBeCompressString("ArrayListConstructor")); + JSTaggedValue value = + FastRuntimeStub::GetPropertyByName(thread, thisValue.GetTaggedValue(), key.GetTaggedValue()); + if (value != JSTaggedValue::Undefined()) { + res = value; + } else { + JSHandle arrayList = InitializeArrayList(thread); + SetFrozenConstructor(thread, thisValue, "ArrayListConstructor", arrayList); + res = arrayList.GetTaggedValue(); + } + break; + } + case ContainerTag::Queue: + case ContainerTag::END: + break; + default: + UNREACHABLE(); + } + + return res; +} + +JSHandle ContainersPrivate::NewContainerConstructor(JSThread *thread, const JSHandle &prototype, + EcmaEntrypoint ctorFunc, const char *name, int length) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle ctor = + factory->NewJSFunction(env, reinterpret_cast(ctorFunc), FunctionKind::BUILTIN_CONSTRUCTOR); + + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSFunction::SetFunctionLength(thread, ctor, JSTaggedValue(length)); + JSHandle nameString(factory->NewFromCanBeCompressString(name)); + JSFunction::SetFunctionName(thread, JSHandle(ctor), nameString, + JSHandle(thread, JSTaggedValue::Undefined())); + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + PropertyDescriptor descriptor1(thread, JSHandle::Cast(ctor), true, false, true); + JSObject::DefineOwnProperty(thread, prototype, constructorKey, descriptor1); + + /* set "prototype" in constructor */ + ctor->SetFunctionPrototype(thread, prototype.GetTaggedValue()); + + return ctor; +} + +void ContainersPrivate::SetFrozenFunction(JSThread *thread, const JSHandle &obj, const char *key, + EcmaEntrypoint func, int length) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle keyString(factory->NewFromCanBeCompressString(key)); + JSHandle function = NewFunction(thread, keyString, func, length); + PropertyDescriptor descriptor(thread, JSHandle(function), false, false, false); + JSObject::DefineOwnProperty(thread, obj, keyString, descriptor); +} + +void ContainersPrivate::SetFrozenConstructor(JSThread *thread, const JSHandle &obj, const char *keyChar, + JSHandle &value) +{ + JSObject::PreventExtensions(thread, obj); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle key(factory->NewFromCanBeCompressString(keyChar)); + PropertyDescriptor descriptor(thread, value, false, false, false); + JSObject::DefineOwnProperty(thread, obj, key, descriptor); +} + +JSHandle ContainersPrivate::NewFunction(JSThread *thread, const JSHandle &key, + EcmaEntrypoint func, int length) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle function = + factory->NewJSFunction(thread->GetEcmaVM()->GetGlobalEnv(), reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread, function, JSTaggedValue(length)); + JSHandle baseFunction(function); + JSFunction::SetFunctionName(thread, baseFunction, key, thread->GlobalConstants()->GetHandledUndefined()); + return function; +} + +JSHandle ContainersPrivate::CreateGetter(JSThread *thread, EcmaEntrypoint func, const char *name, + int length) +{ + JSHandle env = thread->GetEcmaVM()->GetGlobalEnv(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle function = factory->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread, function, JSTaggedValue(length)); + JSHandle funcName(factory->NewFromString(name)); + JSHandle prefix = thread->GlobalConstants()->GetHandledGetString(); + JSFunction::SetFunctionName(thread, JSHandle(function), funcName, prefix); + return JSHandle(function); +} + +void ContainersPrivate::SetGetter(JSThread *thread, const JSHandle &obj, const JSHandle &key, + const JSHandle &getter) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle accessor = factory->NewAccessorData(); + accessor->SetGetter(thread, getter); + PropertyAttributes attr = PropertyAttributes::DefaultAccessor(false, false, false); + JSObject::AddAccessor(thread, JSHandle::Cast(obj), key, accessor, attr); +} + +void ContainersPrivate::SetFunctionAtSymbol(JSThread *thread, const JSHandle &env, + const JSHandle &obj, const JSHandle &symbol, + const char *name, EcmaEntrypoint func, int length) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle function = factory->NewJSFunction(env, reinterpret_cast(func)); + JSFunction::SetFunctionLength(thread, function, JSTaggedValue(length)); + JSHandle nameString(factory->NewFromString(name)); + JSHandle baseFunction(function); + JSFunction::SetFunctionName(thread, baseFunction, nameString, thread->GlobalConstants()->GetHandledUndefined()); + + PropertyDescriptor descriptor(thread, JSHandle::Cast(function), false, false, false); + JSObject::DefineOwnProperty(thread, obj, symbol, descriptor); +} + +void ContainersPrivate::SetStringTagSymbol(JSThread *thread, const JSHandle &env, + const JSHandle &obj, const char *key) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle tag(factory->NewFromString(key)); + JSHandle symbol = env->GetToStringTagSymbol(); + PropertyDescriptor desc(thread, tag, false, false, false); + JSObject::DefineOwnProperty(thread, obj, symbol, desc); +} + +JSHandle ContainersPrivate::InitializeArrayList(JSThread *thread) +{ + const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + // ArrayList.prototype + JSHandle arrayListFuncPrototype = factory->NewEmptyJSObject(); + JSHandle arrayListFuncPrototypeValue(arrayListFuncPrototype); + // ArrayList.prototype_or_dynclass + JSHandle arrayListInstanceDynclass = + factory->NewEcmaDynClass(JSArrayList::SIZE, JSType::JS_ARRAY_LIST, arrayListFuncPrototypeValue); + // ArrayList() = new Function() + JSHandle arrayListFunction(NewContainerConstructor( + thread, arrayListFuncPrototype, ContainersArrayList::ArrayListConstructor, "ArrayList", FuncLength::ZERO)); + JSHandle(arrayListFunction)->SetFunctionPrototype(thread, arrayListInstanceDynclass.GetTaggedValue()); + + // "constructor" property on the prototype + JSHandle constructorKey = globalConst->GetHandledConstructorString(); + JSObject::SetProperty(thread, JSHandle(arrayListFuncPrototype), constructorKey, arrayListFunction); + + // ArrayList.prototype.add() + SetFrozenFunction(thread, arrayListFuncPrototype, "add", ContainersArrayList::Add, FuncLength::ONE); + + return arrayListFunction; +} +} // namespace panda::ecmascript::containers diff --git a/ecmascript/containers/containers_private.h b/ecmascript/containers/containers_private.h new file mode 100644 index 00000000..2950ed48 --- /dev/null +++ b/ecmascript/containers/containers_private.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ECMASCRIPT_CONTAINERS_CONTAINERS_PRIVATE_H +#define ECMASCRIPT_CONTAINERS_CONTAINERS_PRIVATE_H + +#include "ecmascript/base/builtins_base.h" + +namespace panda::ecmascript::containers { +enum FuncLength : uint8_t { ZERO = 0, ONE, TWO, THREE, FOUR }; +enum ContainerTag : uint8_t { ArrayList = 0, Queue, END }; +// Using Lazy-loading container, including ArrayList, Queue, Stack, Vector, List, LinkedList, Deque, +// TreeMap, TreeSet, HashMap, HashSet, LightWightMap, LightWightSet, PlainArray. +// Use through ArkPrivate.Load([ContainerTag]) in js, ContainTag was declaerd in ArkPrivate like ArkPrivate::ArrayList. +class ContainersPrivate : public base::BuiltinsBase { +public: + static JSTaggedValue Load(EcmaRuntimeCallInfo *msg); + +private: + static JSHandle NewContainerConstructor(JSThread *thread, const JSHandle &prototype, + EcmaEntrypoint ctorFunc, const char *name, int length); + static JSHandle NewFunction(JSThread *thread, const JSHandle &key, EcmaEntrypoint func, + int length); + static void SetFrozenFunction(JSThread *thread, const JSHandle &obj, const char *key, EcmaEntrypoint func, + int length); + static void SetFrozenConstructor(JSThread *thread, const JSHandle &obj, const char *keyChar, + JSHandle &value); + static JSHandle CreateGetter(JSThread *thread, EcmaEntrypoint func, const char *name, + int length); + static void SetGetter(JSThread *thread, const JSHandle &obj, + const JSHandle &key, const JSHandle &getter); + static void SetFunctionAtSymbol(JSThread *thread, const JSHandle &env, + const JSHandle &obj, const JSHandle &symbol, + const char *name, EcmaEntrypoint func, int length); + static void SetStringTagSymbol(JSThread *thread, const JSHandle &env, + const JSHandle &obj, const char *key); + static JSHandle InitializeArrayList(JSThread *thread); +}; +} // namespace panda::ecmascript::containers + +#endif // ECMASCRIPT_CONTAINERS_CONTAINERS_PRIVATE_H diff --git a/ecmascript/dump.cpp b/ecmascript/dump.cpp index f2a1dfcd..6fd511ec 100644 --- a/ecmascript/dump.cpp +++ b/ecmascript/dump.cpp @@ -34,6 +34,7 @@ #include "ecmascript/js_array.h" #include "ecmascript/js_array_iterator.h" #include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_arraylist.h" #include "ecmascript/js_async_function.h" #include "ecmascript/js_collator.h" #include "ecmascript/js_dataview.h" @@ -254,6 +255,8 @@ CString JSHClass::DumpJSType(JSType type) return "EcmaModule"; case JSType::CLASS_INFO_EXTRACTOR: return "ClassInfoExtractor"; + case JSType::JS_ARRAY_LIST: + return "ArrayList"; default: { CString ret = "unknown type "; return ret + static_cast(type); @@ -597,6 +600,9 @@ static void DumpObject(JSThread *thread, TaggedObject *obj, std::ostream &os) case JSType::CLASS_INFO_EXTRACTOR: ClassInfoExtractor::Cast(obj)->Dump(thread, os); break; + case JSType::JS_ARRAY_LIST: + JSArrayList::Cast(obj)->Dump(thread, os); + break; default: UNREACHABLE(); break; @@ -1088,6 +1094,12 @@ void JSArray::Dump(JSThread *thread, std::ostream &os) const JSObject::Dump(thread, os); } +void JSArrayList::Dump(JSThread *thread, std::ostream &os) const +{ + os << " - length: " << std::dec << GetLength().GetArrayLength() << "\n"; + JSObject::Dump(thread, os); +} + void JSArrayIterator::Dump(JSThread *thread, std::ostream &os) const { JSArray *array = JSArray::Cast(GetIteratedArray().GetTaggedObject()); @@ -2139,6 +2151,9 @@ static void DumpObject(JSThread *thread, TaggedObject *obj, case JSType::ECMA_MODULE: EcmaModule::Cast(obj)->DumpForSnapshot(thread, vec); return; + case JSType::JS_ARRAY_LIST: + JSArrayList::Cast(obj)->DumpForSnapshot(thread, vec); + return; default: break; } @@ -2486,6 +2501,12 @@ void JSArray::DumpForSnapshot([[maybe_unused]] JSThread *thread, JSObject::DumpForSnapshot(thread, vec); } +void JSArrayList::DumpForSnapshot([[maybe_unused]] JSThread *thread, + std::vector> &vec) const +{ + JSObject::DumpForSnapshot(thread, vec); +} + void JSArrayIterator::DumpForSnapshot([[maybe_unused]] JSThread *thread, std::vector> &vec) const { diff --git a/ecmascript/hprof/heap_snapshot.cpp b/ecmascript/hprof/heap_snapshot.cpp index fe5b7432..bcf69f2c 100644 --- a/ecmascript/hprof/heap_snapshot.cpp +++ b/ecmascript/hprof/heap_snapshot.cpp @@ -339,6 +339,8 @@ CString *HeapSnapShot::GenerateNodeName(JSThread *thread, TaggedObject *entry) return GetString("CompletionRecord"); case JSType::ECMA_MODULE: return GetString("EcmaModule"); + case JSType::JS_ARRAY_LIST: + return GetString("ArrayList"); default: break; } diff --git a/ecmascript/interpreter/fast_runtime_stub-inl.h b/ecmascript/interpreter/fast_runtime_stub-inl.h index 3dd66b1d..d3d40c49 100644 --- a/ecmascript/interpreter/fast_runtime_stub-inl.h +++ b/ecmascript/interpreter/fast_runtime_stub-inl.h @@ -21,6 +21,7 @@ #include "ecmascript/global_dictionary-inl.h" #include "ecmascript/global_env.h" #include "ecmascript/internal_call_params.h" +#include "ecmascript/js_arraylist.h" #include "ecmascript/js_function.h" #include "ecmascript/js_hclass-inl.h" #include "ecmascript/js_proxy.h" @@ -161,6 +162,11 @@ bool FastRuntimeStub::IsSpecialReceiverObj(JSType jsType) return jsType > JSType::JS_PRIMITIVE_REF; } +bool FastRuntimeStub::IsSpecialContainer(JSType jsType) +{ + return jsType >= JSType::JS_ARRAY_LIST && jsType <= JSType::JS_QUEUE; +} + int32_t FastRuntimeStub::TryToElementsIndex(JSTaggedValue key) { if (LIKELY(key.IsInt())) { @@ -309,6 +315,9 @@ JSTaggedValue FastRuntimeStub::GetPropertyByIndex(JSThread *thread, JSTaggedValu auto *hclass = holder.GetTaggedObject()->GetClass(); JSType jsType = hclass->GetObjectType(); if (IsSpecialIndexedObj(jsType)) { + if (IsSpecialContainer(jsType)) { + return GetContainerProperty(thread, holder, index, jsType); + } return JSTaggedValue::Hole(); } TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(holder)->GetElements().GetTaggedObject()); @@ -437,6 +446,9 @@ JSTaggedValue FastRuntimeStub::SetPropertyByName(JSThread *thread, JSTaggedValue auto *hclass = holder.GetTaggedObject()->GetClass(); JSType jsType = hclass->GetObjectType(); if (IsSpecialIndexedObj(jsType)) { + if (IsSpecialContainer(jsType)) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot set property on Container", JSTaggedValue::Exception()); + } return JSTaggedValue::Hole(); } // UpdateRepresentation @@ -518,6 +530,9 @@ JSTaggedValue FastRuntimeStub::SetPropertyByIndex(JSThread *thread, JSTaggedValu auto *hclass = holder.GetTaggedObject()->GetClass(); JSType jsType = hclass->GetObjectType(); if (IsSpecialIndexedObj(jsType)) { + if (IsSpecialContainer(jsType)) { + return SetContainerProperty(thread, holder, index, value, jsType); + } return JSTaggedValue::Hole(); } TaggedArray *elements = TaggedArray::Cast(JSObject::Cast(holder)->GetElements().GetTaggedObject()); @@ -1317,5 +1332,33 @@ JSTaggedValue FastRuntimeStub::HasOwnProperty(JSThread *thread, JSObject *obj, J return FastRuntimeStub::FindOwnProperty(thread, obj, key); } + +JSTaggedValue FastRuntimeStub::GetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSType jsType) +{ + JSTaggedValue res = JSTaggedValue::Undefined(); + switch (jsType) { + case JSType::JS_ARRAY_LIST: + res = JSArrayList::Cast(receiver.GetTaggedObject())->Get(thread, index); + break; + default: + break; + } + return res; +} + +JSTaggedValue FastRuntimeStub::SetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value, JSType jsType) +{ + JSTaggedValue res = JSTaggedValue::Undefined(); + switch (jsType) { + case JSType::JS_ARRAY_LIST: + res = JSArrayList::Cast(receiver.GetTaggedObject())->Set(thread, index, value); + break; + default: + break; + } + return res; +} } // namespace panda::ecmascript #endif // ECMASCRIPT_INTERPRETER_FAST_RUNTIME_STUB_INL_H diff --git a/ecmascript/interpreter/fast_runtime_stub.h b/ecmascript/interpreter/fast_runtime_stub.h index b9dacb04..5bc8771f 100644 --- a/ecmascript/interpreter/fast_runtime_stub.h +++ b/ecmascript/interpreter/fast_runtime_stub.h @@ -101,6 +101,7 @@ private: friend class ICRuntimeStub; static inline bool IsSpecialIndexedObj(JSType jsType); static inline bool IsSpecialReceiverObj(JSType jsType); + static inline bool IsSpecialContainer(JSType jsType); static inline int32_t TryToElementsIndex(JSTaggedValue key); static inline JSTaggedValue CallGetter(JSThread *thread, JSTaggedValue receiver, JSTaggedValue holder, JSTaggedValue value); @@ -110,6 +111,10 @@ private: PropertyAttributes attr); static inline JSTaggedValue AddPropertyByIndex(JSThread *thread, JSTaggedValue receiver, uint32_t index, JSTaggedValue value); + static inline JSTaggedValue GetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSType jsType); + static inline JSTaggedValue SetContainerProperty(JSThread *thread, JSTaggedValue receiver, uint32_t index, + JSTaggedValue value, JSType jsType); }; } // namespace panda::ecmascript diff --git a/ecmascript/js_arraylist.cpp b/ecmascript/js_arraylist.cpp new file mode 100644 index 00000000..01094a91 --- /dev/null +++ b/ecmascript/js_arraylist.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "js_arraylist.h" + +#include "ecmascript/js_tagged_value.h" +#include "ecmascript/object_factory.h" + +namespace panda::ecmascript { +void JSArrayList::Add(JSThread *thread, const JSHandle &arrayList, const JSHandle &value) +{ + // GrowCapacity + array_size_t length = arrayList->GetLength().GetArrayLength(); + JSHandle elements = GrowCapacity(thread, arrayList, length + 1); + + ASSERT(!elements->IsDictionaryMode()); + elements->Set(thread, length, value); + arrayList->SetLength(thread, JSTaggedValue(++length)); +} + +JSHandle JSArrayList::GrowCapacity(const JSThread *thread, const JSHandle &obj, + array_size_t capacity) +{ + JSHandle oldElements(thread, obj->GetElements()); + array_size_t oldLength = oldElements->GetLength(); + if (capacity < oldLength) { + return oldElements; + } + array_size_t newCapacity = ComputeCapacity(capacity); + JSHandle newElements = + thread->GetEcmaVM()->GetFactory()->CopyArray(oldElements, oldLength, newCapacity); + + obj->SetElements(thread, newElements); + return newElements; +} + +JSTaggedValue JSArrayList::Get(JSThread *thread, const uint32_t index) +{ + if (index < 0 || index >= GetLength().GetArrayLength()) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Get property index out-of-bounds", JSTaggedValue::Exception()); + } + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + return elements->Get(index); +} + +JSTaggedValue JSArrayList::Set(JSThread *thread, const uint32_t index, JSTaggedValue value) +{ + if (index < 0 || index >= GetLength().GetArrayLength()) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Set property index out-of-bounds", JSTaggedValue::Exception()); + } + + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + elements->Set(thread, index, value); + return JSTaggedValue::Undefined(); +} + +bool JSArrayList::Delete(JSThread *thread, const JSHandle &obj, const JSHandle &key) +{ + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not delete a type other than number", false); + } + array_size_t length = obj->GetLength().GetArrayLength(); + if (index < 0 || index >= length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Delete property index out-of-bounds", false); + } + TaggedArray *elements = TaggedArray::Cast(obj->GetElements().GetTaggedObject()); + for (array_size_t i = 0; i < length - 1; i++) { + elements->Set(thread, i, elements->Get(i + 1)); + } + obj->SetLength(thread, JSTaggedValue(--length)); + return true; +} + +bool JSArrayList::Has(JSTaggedValue value) const +{ + TaggedArray *elements = TaggedArray::Cast(GetElements().GetTaggedObject()); + return !(elements->GetIdx(value) == TaggedArray::MAX_ARRAY_INDEX); +} + +JSHandle JSArrayList::OwnKeys(JSThread *thread, const JSHandle &obj) +{ + array_size_t length = obj->GetLength().GetArrayLength(); + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle keys = factory->NewTaggedArray(length); + + for (array_size_t i = 0; i < length; i++) { + keys->Set(thread, i, JSTaggedValue(i)); + } + + return keys; +} + +bool JSArrayList::GetOwnProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc) +{ + uint32_t index = 0; + if (UNLIKELY(JSTaggedValue::ToElementIndex(key.GetTaggedValue(), &index))) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not get property whose type is not number", false); + } + + array_size_t length = obj->GetLength().GetArrayLength(); + if (index < 0 || index >= length) { + THROW_RANGE_ERROR_AND_RETURN(thread, "Get property index out-of-bounds", false); + } + return JSObject::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); +} +} // namespace panda::ecmascript diff --git a/ecmascript/js_arraylist.h b/ecmascript/js_arraylist.h new file mode 100644 index 00000000..3a48e7c2 --- /dev/null +++ b/ecmascript/js_arraylist.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ECMASCRIPT_JSARRAYLIST_H +#define ECMASCRIPT_JSARRAYLIST_H + +#include "js_object.h" +#include "js_tagged_value-inl.h" + +namespace panda::ecmascript { +class JSArrayList : public JSObject { +public: + static constexpr int DEFAULT_CAPACITY_LENGTH = 10; + static JSArrayList *Cast(ObjectHeader *object) + { + return static_cast(object); + } + + static void Add(JSThread *thread, const JSHandle &arrayList, const JSHandle &value); + + JSTaggedValue Get(JSThread *thread, const uint32_t index); + + JSTaggedValue Set(JSThread *thread, const uint32_t index, JSTaggedValue value); + bool Has(JSTaggedValue value) const; + + static bool Delete(JSThread *thread, const JSHandle &obj, const JSHandle &key); + static JSHandle OwnKeys(JSThread *thread, const JSHandle &obj); + static bool GetOwnProperty(JSThread *thread, const JSHandle &obj, const JSHandle &key, + PropertyDescriptor &desc); + + inline int GetSize() const + { + return GetLength().GetArrayLength(); + } + + static constexpr size_t LENGTH_OFFSET = JSObject::SIZE; + ACCESSORS(Length, LENGTH_OFFSET, SIZE); + + DECL_VISIT_OBJECT_FOR_JS_OBJECT(JSObject, LENGTH_OFFSET, SIZE) + DECL_DUMP() +private: + inline static array_size_t ComputeCapacity(array_size_t oldCapacity) + { + array_size_t newCapacity = oldCapacity + (oldCapacity >> 1U); + return newCapacity > DEFAULT_CAPACITY_LENGTH ? newCapacity : DEFAULT_CAPACITY_LENGTH; + } + static JSHandle GrowCapacity(const JSThread *thread, const JSHandle &obj, + array_size_t capacity); +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_JSARRAYLIST_H diff --git a/ecmascript/js_hclass.h b/ecmascript/js_hclass.h index a104cd79..b48b3465 100644 --- a/ecmascript/js_hclass.h +++ b/ecmascript/js_hclass.h @@ -112,6 +112,8 @@ class ProtoChangeDetails; \ /* SPECIAL indexed objects begin, DON'T CHANGE HERE ///////////////////////////////////////////////-PADDING */ \ JS_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_ARRAY_LIST, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ + JS_QUEUE, /* /////////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_TYPED_ARRAY, /* JS_TYPED_ARRAY_BEGIN /////////////////////////////////////////////////////////////////// */ \ JS_INT8_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ JS_UINT8_ARRAY, /* ////////////////////////////////////////////////////////////////////////////////-PADDING */ \ @@ -603,6 +605,21 @@ public: return GetObjectType() == JSType::JS_PLURAL_RULES; } + inline bool IsJSArrayList() const + { + return GetObjectType() == JSType::JS_ARRAY_LIST; + } + + inline bool IsJSQueue() const + { + return GetObjectType() == JSType::JS_QUEUE; + } + + inline bool IsSpecialContainer() const + { + return GetObjectType() >= JSType::JS_ARRAY_LIST && GetObjectType() <= JSType::JS_QUEUE; + } + inline bool IsAccessorData() const { return GetObjectType() == JSType::ACCESSOR_DATA; diff --git a/ecmascript/js_tagged_value-inl.h b/ecmascript/js_tagged_value-inl.h index 3fbd14d4..3e02edef 100644 --- a/ecmascript/js_tagged_value-inl.h +++ b/ecmascript/js_tagged_value-inl.h @@ -502,6 +502,16 @@ inline bool JSTaggedValue::IsJSPluralRules() const return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSPluralRules(); } +inline bool JSTaggedValue::IsJSArrayList() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsJSArrayList(); +} + +inline bool JSTaggedValue::IsSpecialContainer() const +{ + return IsHeapObject() && GetTaggedObject()->GetClass()->IsSpecialContainer(); +} + inline bool JSTaggedValue::IsPromiseIteratorRecord() const { return IsHeapObject() && GetTaggedObject()->GetClass()->IsPromiseIteratorRecord(); diff --git a/ecmascript/js_tagged_value.cpp b/ecmascript/js_tagged_value.cpp index 3b7ca144..f698b036 100644 --- a/ecmascript/js_tagged_value.cpp +++ b/ecmascript/js_tagged_value.cpp @@ -19,6 +19,7 @@ #include "ecmascript/global_env.h" #include "ecmascript/internal_call_params.h" #include "ecmascript/js_array.h" +#include "ecmascript/js_arraylist.h" #include "ecmascript/js_handle.h" #include "ecmascript/js_primitive_ref.h" #include "ecmascript/js_proxy.h" @@ -552,6 +553,10 @@ bool JSTaggedValue::DeleteProperty(JSThread *thread, const JSHandle(obj), key); } + if (obj->IsSpecialContainer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not delete property in Container Object", false); + } + return JSObject::DeleteProperty(thread, JSHandle(obj), key); } @@ -609,6 +614,10 @@ bool JSTaggedValue::DefineOwnProperty(JSThread *thread, const JSHandleIsSpecialContainer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not defineProperty on Container Object", false); + } + return JSObject::DefineOwnProperty(thread, JSHandle(obj), key, desc); } @@ -621,6 +630,9 @@ bool JSTaggedValue::GetOwnProperty(JSThread *thread, const JSHandleIsTypedArray()) { return JSTypedArray::GetOwnProperty(thread, obj, JSTypedArray::ToPropKey(thread, key), desc); } + if (obj->IsSpecialContainer()) { + return GetContainerProperty(thread, obj, key, desc); + } return JSObject::GetOwnProperty(thread, JSHandle(obj), key, desc); } @@ -630,6 +642,9 @@ bool JSTaggedValue::SetPrototype(JSThread *thread, const JSHandle if (obj->IsJSProxy()) { return JSProxy::SetPrototype(thread, JSHandle(obj), proto); } + if (obj->IsSpecialContainer()) { + THROW_TYPE_ERROR_AND_RETURN(thread, "Can not set Prototype on Container Object", false); + } return JSObject::SetPrototype(thread, JSHandle(obj), proto); } @@ -650,6 +665,9 @@ JSHandle JSTaggedValue::GetOwnPropertyKeys(JSThread *thread, const if (obj->IsTypedArray()) { return JSTypedArray::OwnPropertyKeys(thread, obj); } + if (obj->IsSpecialContainer()) { + return GetOwnContainerPropertyKeys(thread, obj); + } return JSObject::GetOwnPropertyKeys(thread, JSHandle(obj)); } @@ -663,6 +681,9 @@ bool JSTaggedValue::HasProperty(JSThread *thread, const JSHandle if (obj->IsTypedArray()) { return JSTypedArray::HasProperty(thread, obj, JSTypedArray::ToPropKey(thread, key)); } + if (obj->IsSpecialContainer()) { + return HasContainerProperty(thread, obj, key); + } return JSObject::HasProperty(thread, JSHandle(obj), key); } @@ -676,6 +697,9 @@ bool JSTaggedValue::HasProperty(JSThread *thread, const JSHandle JSHandle key_handle(thread, JSTaggedValue(key)); return JSTypedArray::HasProperty(thread, obj, JSHandle(ToString(thread, key_handle))); } + if (obj->IsSpecialContainer()) { + return HasContainerProperty(thread, obj, JSHandle(thread, JSTaggedValue(key))); + } return JSObject::HasProperty(thread, JSHandle(obj), key); } @@ -748,4 +772,57 @@ JSTaggedValue JSTaggedValue::GetSuperBase(JSThread *thread, const JSHandleIsECMAObject()); return JSObject::Cast(obj.GetTaggedValue())->GetPrototype(thread); } + +bool JSTaggedValue::HasContainerProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key) +{ + auto *hclass = obj->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + switch (jsType) { + case JSType::JS_ARRAY_LIST: { + return JSHandle::Cast(obj)->Has(key.GetTaggedValue()); + } + case JSType::JS_QUEUE: + break; + default: { + UNREACHABLE(); + } + } + return false; +} + +JSHandle JSTaggedValue::GetOwnContainerPropertyKeys(JSThread *thread, const JSHandle &obj) +{ + auto *hclass = obj->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + switch (jsType) { + case JSType::JS_ARRAY_LIST: { + return JSArrayList::OwnKeys(thread, JSHandle::Cast(obj)); + } + case JSType::JS_QUEUE: + break; + default: { + UNREACHABLE(); + } + } + return thread->GetEcmaVM()->GetFactory()->EmptyArray(); +} + +bool JSTaggedValue::GetContainerProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc) +{ + auto *hclass = obj->GetTaggedObject()->GetClass(); + JSType jsType = hclass->GetObjectType(); + switch (jsType) { + case JSType::JS_ARRAY_LIST: { + return JSArrayList::GetOwnProperty(thread, JSHandle::Cast(obj), key, desc); + } + case JSType::JS_QUEUE: + break; + default: { + UNREACHABLE(); + } + } + return false; +} } // namespace panda::ecmascript diff --git a/ecmascript/js_tagged_value.h b/ecmascript/js_tagged_value.h index 27387745..f26f15f4 100644 --- a/ecmascript/js_tagged_value.h +++ b/ecmascript/js_tagged_value.h @@ -312,6 +312,8 @@ public: bool IsJSNumberFormat() const; bool IsJSCollator() const; bool IsJSPluralRules() const; + bool IsJSArrayList() const; + bool IsSpecialContainer() const; bool IsPrototypeHandler() const; bool IsTransitionHandler() const; @@ -343,6 +345,11 @@ private: void DumpSpecialValue([[maybe_unused]] JSThread *thread, std::ostream &os) const; void DumpHeapObjectType([[maybe_unused]] JSThread *thread, std::ostream &os) const; + static bool HasContainerProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key); + static JSHandle GetOwnContainerPropertyKeys(JSThread *thread, const JSHandle &obj); + static bool GetContainerProperty(JSThread *thread, const JSHandle &obj, + const JSHandle &key, PropertyDescriptor &desc); }; } // namespace panda::ecmascript #endif // ECMASCRIPT_JS_TAGGED_VALUE_H diff --git a/ecmascript/mem/object_xray-inl.h b/ecmascript/mem/object_xray-inl.h index 580a948a..1e6a52a7 100644 --- a/ecmascript/mem/object_xray-inl.h +++ b/ecmascript/mem/object_xray-inl.h @@ -28,6 +28,7 @@ #include "ecmascript/jobs/pending_job.h" #include "ecmascript/js_arguments.h" #include "ecmascript/js_array.h" +#include "ecmascript/js_arraylist.h" #include "ecmascript/js_array_iterator.h" #include "ecmascript/js_arraybuffer.h" #include "ecmascript/js_async_function.h" @@ -300,6 +301,10 @@ void ObjectXRay::VisitObjectBody(TaggedObject *object, JSHClass *klass, const Ec case JSType::CLASS_INFO_EXTRACTOR: ClassInfoExtractor::Cast(object)->VisitRangeSlot(visitor); break; + case JSType::JS_QUEUE: + case JSType::JS_ARRAY_LIST: + JSArrayList::Cast(object)->VisitRangeSlot(visitor); + break; default: UNREACHABLE(); } diff --git a/ecmascript/message_string.h b/ecmascript/message_string.h index 144f8bdb..3fd53814 100644 --- a/ecmascript/message_string.h +++ b/ecmascript/message_string.h @@ -20,10 +20,12 @@ namespace panda::ecmascript { // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) -#define MESSAGE_STRING_LIST(V) \ - V(SetReadOnlyProperty, "Cannot set readonly property") \ - V(FunctionCallNotConstructor, "class constructor cannot call") \ - V(SetPropertyWhenNotExtensible, "Cannot add property in prevent extensions ") +#define MESSAGE_STRING_LIST(V) \ + V(SetReadOnlyProperty, "Cannot set readonly property") \ + V(FunctionCallNotConstructor, "class constructor cannot call") \ + V(SetPropertyWhenNotExtensible, "Cannot add property in prevent extensions ") \ + V(GetPropertyOutOfBounds, "Get Property index out-of-bounds") \ + V(CanNotSetPropertyOnContainer, "Cannot set property on Container") class MessageString { public: @@ -40,4 +42,4 @@ public: // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define GET_MESSAGE_STRING_ID(name) static_cast((MessageString::MessageId::Message_##name)) } // namespace panda::ecmascript -#endif // ECMASCRIPT_MESSAGE_STRING_H \ No newline at end of file +#endif // ECMASCRIPT_MESSAGE_STRING_H diff --git a/ecmascript/object_factory.cpp b/ecmascript/object_factory.cpp index a6262714..e03b4fd3 100644 --- a/ecmascript/object_factory.cpp +++ b/ecmascript/object_factory.cpp @@ -40,6 +40,7 @@ #include "ecmascript/js_array.h" #include "ecmascript/js_array_iterator.h" #include "ecmascript/js_arraybuffer.h" +#include "ecmascript/js_arraylist.h" #include "ecmascript/js_async_function.h" #include "ecmascript/js_dataview.h" #include "ecmascript/js_date.h" @@ -812,6 +813,9 @@ JSHandle ObjectFactory::NewJSObjectByConstructor(const JSHandleSetByteLength(thread_, JSTaggedValue(0)); JSDataView::Cast(*obj)->SetByteOffset(thread_, JSTaggedValue(0)); break; + case JSType::JS_ARRAY_LIST: + JSArrayList::Cast(*obj)->SetLength(thread_, JSTaggedValue(0)); + break; case JSType::JS_FUNCTION: case JSType::JS_GENERATOR_FUNCTION: case JSType::JS_FORIN_ITERATOR: diff --git a/ecmascript/runtime_trampolines.cpp b/ecmascript/runtime_trampolines.cpp index f54fc376..f0ccbb27 100644 --- a/ecmascript/runtime_trampolines.cpp +++ b/ecmascript/runtime_trampolines.cpp @@ -21,6 +21,7 @@ #include "ecmascript/ic/profile_type_info.h" #include "ecmascript/ic/properties_cache.h" #include "ecmascript/interpreter/interpreter-inl.h" +#include "ecmascript/js_arraylist.h" #include "ecmascript/js_object.h" #include "ecmascript/js_proxy.h" #include "ecmascript/layout_info.h" @@ -372,4 +373,12 @@ void RuntimeTrampolines::NoticeThroughChainAndRefreshUser(uintptr_t argGlue, JST JSHClass::NoticeThroughChain(thread, oldHClassHandle); JSHClass::RefreshUsers(thread, oldHClassHandle, newHClassHandle); } + +void RuntimeTrampolines::JSArrayListSetByIndex(uintptr_t argGlue, JSTaggedValue obj, int32_t index, JSTaggedValue value) +{ + auto thread = JSThread::GlueToJSThread(argGlue); + [[maybe_unused]] EcmaHandleScope handleScope(thread); + JSHandle arrayList(thread, obj); + arrayList->Set(thread, index, value); +} } // namespace panda::ecmascript diff --git a/ecmascript/runtime_trampolines.h b/ecmascript/runtime_trampolines.h index 3a0973c9..98b73d6e 100644 --- a/ecmascript/runtime_trampolines.h +++ b/ecmascript/runtime_trampolines.h @@ -76,6 +76,7 @@ public: JSTaggedType argElement, uint32_t elementIndex, uint32_t capacity); static void DebugPrint(int fmtMessageId, ...); static void NoticeThroughChainAndRefreshUser(uintptr_t argGlue, uint64_t argoldHClass, uint64_t argnewHClass); + static void JSArrayListSetByIndex(uintptr_t argGlue, JSTaggedValue obj, int32_t index, JSTaggedValue value); }; } // namespace panda::ecmascript #endif diff --git a/ecmascript/snapshot/mem/snapshot_serialize.cpp b/ecmascript/snapshot/mem/snapshot_serialize.cpp index 88a19de6..c6e479aa 100644 --- a/ecmascript/snapshot/mem/snapshot_serialize.cpp +++ b/ecmascript/snapshot/mem/snapshot_serialize.cpp @@ -53,6 +53,7 @@ #include "ecmascript/builtins/builtins_weak_map.h" #include "ecmascript/builtins/builtins_weak_set.h" #include "ecmascript/class_linker/program_object.h" +#include "ecmascript/containers/containers_arraylist.h" #include "ecmascript/global_env.h" #include "ecmascript/js_array_iterator.h" #include "ecmascript/js_for_in_iterator.h" @@ -110,6 +111,7 @@ using NumberFormat = builtins::BuiltinsNumberFormat; using RelativeTimeFormat = builtins::BuiltinsRelativeTimeFormat; using Collator = builtins::BuiltinsCollator; using PluralRules = builtins::BuiltinsPluralRules; +using ArrayList = containers::ContainersArrayList; constexpr int TAGGED_SIZE = JSTaggedValue::TaggedTypeSize(); constexpr int OBJECT_HEADER_SIZE = TaggedObject::TaggedObjectSize(); @@ -544,6 +546,9 @@ static uintptr_t g_nativeTable[] = { reinterpret_cast(PluralRules::SupportedLocalesOf), reinterpret_cast(PluralRules::Select), reinterpret_cast(PluralRules::ResolvedOptions), + reinterpret_cast(ArrayList::ArrayListConstructor), + reinterpret_cast(ArrayList::Add), + reinterpret_cast(ArrayList::Iterator), // not builtins method reinterpret_cast(JSFunction::PrototypeSetter), diff --git a/ecmascript/tests/dump_test.cpp b/ecmascript/tests/dump_test.cpp index 55e5bb05..4eb27098 100644 --- a/ecmascript/tests/dump_test.cpp +++ b/ecmascript/tests/dump_test.cpp @@ -32,6 +32,7 @@ #include "ecmascript/jobs/pending_job.h" #include "ecmascript/js_arguments.h" #include "ecmascript/js_array.h" +#include "ecmascript/js_arraylist.h" #include "ecmascript/js_array_iterator.h" #include "ecmascript/js_arraybuffer.h" #include "ecmascript/js_async_function.h" @@ -621,6 +622,12 @@ HWTEST_F_L0(EcmaDumpTest, HeapProfileDump) DUMP_FOR_HANDLE(classInfoExtractor) break; } + case JSType::JS_QUEUE: + case JSType::JS_ARRAY_LIST: { + CHECK_DUMP_FILEDS(JSObject::SIZE, JSArrayList::SIZE, 1) + // unused + break; + } default: LOG_ECMA_MEM(ERROR) << "JSType " << static_cast(type) << " cannot be dumped."; UNREACHABLE(); diff --git a/test/moduletest/BUILD.gn b/test/moduletest/BUILD.gn index 71261c85..bef016d8 100755 --- a/test/moduletest/BUILD.gn +++ b/test/moduletest/BUILD.gn @@ -18,6 +18,7 @@ group("ark_js_moduletest") { "bitwiseop:bitwiseopAction", "callframe:callframeAction", "class:classAction", + "container:containerAction", "dyninstruction:dyninstructionAction", "fortest:fortestAction", "generator:generatorAction", diff --git a/test/moduletest/container/BUILD.gn b/test/moduletest/container/BUILD.gn new file mode 100644 index 00000000..4a227617 --- /dev/null +++ b/test/moduletest/container/BUILD.gn @@ -0,0 +1,23 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//ark/js_runtime/test/test_helper.gni") + +host_moduletest_action("container_arraylist") { + deps = [] +} + +host_moduletest_action("container") { + extra_modules = [ "container_arraylist" ] + deps = [ ":gen_container_arraylist_abc" ] +} diff --git a/test/moduletest/container/container.js b/test/moduletest/container/container.js new file mode 100644 index 00000000..0de6186f --- /dev/null +++ b/test/moduletest/container/container.js @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var helloworld = "container test start" +print(helloworld) \ No newline at end of file diff --git a/test/moduletest/container/container_arraylist.js b/test/moduletest/container/container_arraylist.js new file mode 100644 index 00000000..b89ed726 --- /dev/null +++ b/test/moduletest/container/container_arraylist.js @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var fastarray = undefined; +if (globalThis["ArkPrivate"] != undefined) { + fastarray = ArkPrivate.Load(ArkPrivate.ArrayList); +} + +let arr = new fastarray(); +arr.add(1); +arr.add(2); +print(arr[0]); + +try { + arr["aa"] = 3; +} catch (e) { + print(e); +} \ No newline at end of file diff --git a/test/moduletest/container/expect_output.txt b/test/moduletest/container/expect_output.txt new file mode 100644 index 00000000..895975c3 --- /dev/null +++ b/test/moduletest/container/expect_output.txt @@ -0,0 +1,16 @@ +# Copyright (c) 2021 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +container test start +1 +TypeError: Cannot set property on Container