diff --git a/ecmascript/builtins/builtins_array.cpp b/ecmascript/builtins/builtins_array.cpp index e31e5c7778..df3d53547a 100644 --- a/ecmascript/builtins/builtins_array.cpp +++ b/ecmascript/builtins/builtins_array.cpp @@ -1826,6 +1826,17 @@ JSTaggedValue BuiltinsArray::ReduceRight(EcmaRuntimeCallInfo *argv) // e. Decrease k by 1. JSMutableHandle key(thread, JSTaggedValue::Undefined()); JSTaggedValue callResult = JSTaggedValue::Undefined(); + + JSHandle thisArgHandle = globalConst->GetHandledUndefined(); + if (thisObjVal->IsStableJSArray(thread)) { + JSTaggedValue ret = JSStableArray::HandleReduceRightOfStable(thread, thisObjHandle, + callbackFnHandle, accumulator, thisArgHandle, k); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (ret.ToBoolean()) { + return accumulator.GetTaggedValue(); + } + } + while (k >= 0) { key.Update(JSTaggedValue(k)); bool exists = (thisHandle->IsTypedArray() || JSTaggedValue::HasProperty(thread, thisObjVal, key)); @@ -1833,7 +1844,6 @@ JSTaggedValue BuiltinsArray::ReduceRight(EcmaRuntimeCallInfo *argv) if (exists) { JSHandle kValue = JSArray::FastGetPropertyByValue(thread, thisObjVal, key); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - JSHandle thisArgHandle = globalConst->GetHandledUndefined(); const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O» JSHandle undefined = globalConst->GetHandledUndefined(); EcmaRuntimeCallInfo *info = diff --git a/ecmascript/builtins/builtins_array.h b/ecmascript/builtins/builtins_array.h index 45a74a6ed4..1766b78f08 100644 --- a/ecmascript/builtins/builtins_array.h +++ b/ecmascript/builtins/builtins_array.h @@ -81,7 +81,7 @@ /* Array.prototype.reduce ( callbackfn [ , initialValue ] ) */ \ V("reduce", Reduce, 1, ArrayReduce) \ /* Array.prototype.reduceRight ( callbackfn [ , initialValue ] ) */ \ - V("reduceRight", ReduceRight, 1, INVALID) \ + V("reduceRight", ReduceRight, 1, ArrayReduceRight) \ /* Array.prototype.reverse ( ) */ \ V("reverse", Reverse, 0, ArrayReverse) \ /* Array.prototype.shift ( ) */ \ diff --git a/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp b/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp index 16ab386829..1890f2351f 100644 --- a/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp +++ b/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp @@ -2535,6 +2535,211 @@ void BuiltinsArrayStubBuilder::Every(GateRef glue, GateRef thisValue, GateRef nu } } +void BuiltinsArrayStubBuilder::ReduceRight(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label thisExists(env); + Label isHeapObject(env); + Label isJsArray(env); + Label defaultConstr(env); + Label isStability(env); + Label notCOWArray(env); + Label equalCls(env); + BRANCH(TaggedIsUndefinedOrNull(thisValue), slowPath, &thisExists); + Bind(&thisExists); + BRANCH(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath); + Bind(&isHeapObject); + BRANCH(IsJsArray(thisValue), &isJsArray, slowPath); + Bind(&isJsArray); + BRANCH(HasConstructor(thisValue), slowPath, &defaultConstr); + Bind(&defaultConstr); + BRANCH(IsStableJSArray(glue, thisValue), &isStability, slowPath); + Bind(&isStability); + BRANCH(IsJsCOWArray(thisValue), slowPath, ¬COWArray); + Bind(¬COWArray); + GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit())); + GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset); + auto arrayFunc = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, GlobalEnv::ARRAY_FUNCTION_INDEX); + GateRef intialHClass = Load(VariableType::JS_ANY(), arrayFunc, IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET)); + GateRef arrayCls = LoadHClass(thisValue); + BRANCH(Equal(intialHClass, arrayCls), &equalCls, slowPath); + Bind(&equalCls); + + DEFVARIABLE(thisLen, VariableType::INT32(), Int32(0)); + DEFVARIABLE(accumulator, VariableType::JS_ANY(), Undefined()); + DEFVARIABLE(k, VariableType::INT32(), Int32(0)); + Label atLeastOneArg(env); + Label callbackFnHandleHeapObject(env); + Label callbackFnHandleCallable(env); + Label noTypeError(env); + Label updateAccumulator(env); + Label thisIsStable(env); + Label thisNotStable(env); + thisLen = GetArrayLength(thisValue); + BRANCH(Int64GreaterThanOrEqual(numArgs, IntPtr(1)), &atLeastOneArg, slowPath); + Bind(&atLeastOneArg); + GateRef callbackFnHandle = GetCallArg0(numArgs); + BRANCH(TaggedIsHeapObject(callbackFnHandle), &callbackFnHandleHeapObject, slowPath); + Bind(&callbackFnHandleHeapObject); + BRANCH(IsCallable(callbackFnHandle), &callbackFnHandleCallable, slowPath); + Bind(&callbackFnHandleCallable); + GateRef thisLenIsZero = Int32Equal(*thisLen, Int32(0)); + GateRef numArgsLessThanTwo = Int64LessThan(numArgs, IntPtr(2)); // 2: callbackFn initialValue + BRANCH(BoolAnd(thisLenIsZero, numArgsLessThanTwo), slowPath, &noTypeError); + Bind(&noTypeError); + k = Int32Sub(*thisLen, Int32(1)); + BRANCH(Int64Equal(numArgs, IntPtr(2)), &updateAccumulator, slowPath); // 2: callbackFn initialValue + Bind(&updateAccumulator); + accumulator = GetCallArg1(numArgs); + Jump(&thisIsStable); + + Bind(&thisIsStable); + { + DEFVARIABLE(kValue, VariableType::JS_ANY(), Hole()); + GateRef argsLength = Int32(4); + NewObjectStubBuilder newBuilder(this); + GateRef argList = newBuilder.NewTaggedArray(glue, argsLength); + Label loopHead(env); + Label next(env); + Label loopEnd(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Label nextStep(env); + Label kValueIsHole(env); + Label callDispatch(env); + Label hasProperty(env); + Label hasException0(env); + Label notHasException0(env); + Label hasException1(env); + Label notHasException1(env); + GateRef newLen = GetArrayLength(thisValue); + BRANCH(BoolAnd(IsStableJSArray(glue, thisValue), Int32Equal(*thisLen, newLen)), + &nextStep, &thisNotStable); + Bind(&nextStep); + BRANCH(Int32GreaterThanOrEqual(*k, Int32(0)), &next, &loopExit); + Bind(&next); + kValue = GetTaggedValueWithElementsKind(thisValue, *k); + BRANCH(TaggedIsHole(*kValue), &kValueIsHole, &callDispatch); + Bind(&kValueIsHole); + { + GateRef hasProp = CallRuntime(glue, RTSTUB_ID(HasProperty), { thisValue, IntToTaggedInt(*k) }); + BRANCH(TaggedIsTrue(hasProp), &hasProperty, &loopEnd); + Bind(&hasProperty); + kValue = FastGetPropertyByIndex(glue, thisValue, *k, ProfileOperation()); + BRANCH(HasPendingException(glue), &hasException0, ¬HasException0); + Bind(&hasException0); + result->WriteVariable(Exception()); + Jump(exit); + Bind(¬HasException0); + BRANCH(TaggedIsHole(*kValue), &loopEnd, &callDispatch); + } + Bind(&callDispatch); + { + // callback param 0: accumulator + SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(0), *accumulator); + // callback param 1: currentValue + SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(1), *kValue); + // callback param 2: index + SetValueToTaggedArray(VariableType::INT32(), glue, argList, Int32(2), IntToTaggedInt(*k)); + // callback param 3: array + SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(3), thisValue); + GateRef argv = PtrAdd(argList, IntPtr(TaggedArray::DATA_OFFSET)); + GateRef callResult = JSCallDispatch(glue, callbackFnHandle, argsLength, 0, + Circuit::NullGate(), JSCallMode::CALL_THIS_ARGV_WITH_RETURN, + {argsLength, argv, Undefined()}); + BRANCH(HasPendingException(glue), &hasException1, ¬HasException1); + Bind(&hasException1); + { + result->WriteVariable(Exception()); + Jump(exit); + } + + Bind(¬HasException1); + { + accumulator = callResult; + Jump(&loopEnd); + } + } + } + Bind(&loopEnd); + k = Int32Sub(*k, Int32(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + result->WriteVariable(*accumulator); + Jump(exit); + } + + Bind(&thisNotStable); + { + DEFVARIABLE(kValue, VariableType::JS_ANY(), Hole()); + GateRef argsLength = Int32(4); + NewObjectStubBuilder newBuilder(this); + GateRef argList = newBuilder.NewTaggedArray(glue, argsLength); + Label loopHead(env); + Label next(env); + Label loopEnd(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Label hasProperty(env); + Label hasException0(env); + Label notHasException0(env); + Label callDispatch(env); + Label hasException1(env); + Label notHasException1(env); + BRANCH(Int32GreaterThanOrEqual(*k, Int32(0)), &next, &loopExit); + Bind(&next); + GateRef hasProp = CallRuntime(glue, RTSTUB_ID(HasProperty), { thisValue, IntToTaggedInt(*k) }); + BRANCH(TaggedIsTrue(hasProp), &hasProperty, &loopEnd); + Bind(&hasProperty); + kValue = FastGetPropertyByIndex(glue, thisValue, *k, ProfileOperation()); + BRANCH(HasPendingException(glue), &hasException0, ¬HasException0); + Bind(&hasException0); + result->WriteVariable(Exception()); + Jump(exit); + Bind(¬HasException0); + BRANCH(TaggedIsHole(*kValue), &loopEnd, &callDispatch); + Bind(&callDispatch); + { + // callback param 0: accumulator + SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(0), *accumulator); + // callback param 1: currentValue + SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(1), *kValue); + // callback param 2: index + SetValueToTaggedArray(VariableType::INT32(), glue, argList, Int32(2), IntToTaggedInt(*k)); + // callback param 3: array + SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(3), thisValue); + GateRef argv = PtrAdd(argList, IntPtr(TaggedArray::DATA_OFFSET)); + GateRef callResult = JSCallDispatch(glue, callbackFnHandle, argsLength, 0, + Circuit::NullGate(), JSCallMode::CALL_THIS_ARGV_WITH_RETURN, + {argsLength, argv, Undefined()}); + BRANCH(HasPendingException(glue), &hasException1, ¬HasException1); + Bind(&hasException1); + { + result->WriteVariable(Exception()); + Jump(exit); + } + + Bind(¬HasException1); + { + accumulator = callResult; + Jump(&loopEnd); + } + } + } + Bind(&loopEnd); + k = Int32Sub(*k, Int32(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + result->WriteVariable(*accumulator); + Jump(exit); + } +} + void BuiltinsArrayStubBuilder::FindLastIndex(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *result, Label *exit, Label *slowPath) { diff --git a/ecmascript/compiler/builtins/builtins_array_stub_builder.h b/ecmascript/compiler/builtins/builtins_array_stub_builder.h index 66e16bc00d..fb81b6813c 100644 --- a/ecmascript/compiler/builtins/builtins_array_stub_builder.h +++ b/ecmascript/compiler/builtins/builtins_array_stub_builder.h @@ -83,6 +83,8 @@ public: void Every(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *result, Label *exit, Label *slowPath); + void ReduceRight(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *result, Label *exit, Label *slowPath); + void Map(GateRef glue, GateRef thisValue, GateRef numArgs, Variable *result, Label *exit, Label *slowPath); void FindLastIndex(GateRef glue, GateRef thisValue, GateRef numArgs, diff --git a/ecmascript/compiler/builtins/builtins_call_signature.h b/ecmascript/compiler/builtins/builtins_call_signature.h index 46ae034eb0..450c8d3272 100644 --- a/ecmascript/compiler/builtins/builtins_call_signature.h +++ b/ecmascript/compiler/builtins/builtins_call_signature.h @@ -84,6 +84,7 @@ namespace panda::ecmascript::kungfu { V(ArrayIncludes) \ V(ArrayFrom) \ V(ArraySplice) \ + V(ArrayReduceRight) \ V(ArrayCopyWithin) \ V(ArrayEvery) \ V(ArrayFindLastIndex) \ diff --git a/ecmascript/compiler/builtins/builtins_stubs.cpp b/ecmascript/compiler/builtins/builtins_stubs.cpp index 7796862bfe..bc96e0e890 100644 --- a/ecmascript/compiler/builtins/builtins_stubs.cpp +++ b/ecmascript/compiler/builtins/builtins_stubs.cpp @@ -343,6 +343,7 @@ DECLARE_BUILTINS(Array##Method) V(Every, JS_ANY) \ V(FindLastIndex, JS_ANY) \ V(FindLast, JS_ANY) \ + V(ReduceRight, JS_ANY) \ V(Map, JS_ANY) DECLARE_BUILTINS(SORT) diff --git a/ecmascript/js_stable_array.cpp b/ecmascript/js_stable_array.cpp index 692ff2d100..026459ca03 100644 --- a/ecmascript/js_stable_array.cpp +++ b/ecmascript/js_stable_array.cpp @@ -1226,4 +1226,54 @@ JSTaggedValue JSStableArray::HandleFindLastOfStable(JSThread *thread, JSHandle thisObjHandle, + JSHandle callbackFnHandle, + JSMutableHandle &accumulator, + JSHandle thisArgHandle, int64_t &k) +{ + JSHandle thisObjVal(thisObjHandle); + JSMutableHandle kValue(thread, JSTaggedValue::Hole()); + JSMutableHandle key(thread, JSTaggedValue::Undefined()); + JSTaggedValue callResult = JSTaggedValue::Undefined(); + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + const int32_t argsLength = 4; // 4: «accumulator, kValue, k, O» + int64_t len = static_cast(base::ArrayHelper::GetArrayLength(thread, thisObjVal)); + while (k >= 0) { + key.Update(JSTaggedValue(k)); + kValue.Update(ElementAccessor::Get(thisObjHandle, k)); + if (!kValue.GetTaggedValue().IsHole()) { + EcmaRuntimeCallInfo *info = + EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, thisArgHandle, undefined, argsLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(), + key.GetTaggedValue(), thisObjVal.GetTaggedValue()); + callResult = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + accumulator.Update(callResult); + } else { + bool exists = JSTaggedValue::HasProperty(thread, thisObjVal, key); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (exists) { + auto res = JSArray::FastGetPropertyByValue(thread, thisObjVal, key).GetTaggedValue(); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + kValue.Update(res); + EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, + thisArgHandle, undefined, argsLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + info->SetCallArg(accumulator.GetTaggedValue(), kValue.GetTaggedValue(), + key.GetTaggedValue(), thisObjVal.GetTaggedValue()); + callResult = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + accumulator.Update(callResult); + } + } + k--; + int64_t newLen = static_cast(base::ArrayHelper::GetArrayLength(thread, thisObjVal)); + if (!thisObjVal->IsStableJSArray(thread) || newLen != len) { + return base::BuiltinsBase::GetTaggedBoolean(false); + } + } + return base::BuiltinsBase::GetTaggedBoolean(true); +} } // namespace panda::ecmascript diff --git a/ecmascript/js_stable_array.h b/ecmascript/js_stable_array.h index f332122f0f..3c0782029e 100644 --- a/ecmascript/js_stable_array.h +++ b/ecmascript/js_stable_array.h @@ -79,6 +79,10 @@ public: JSHandle callbackFnHandle, JSHandle thisArgHandle, JSMutableHandle &kValue, int64_t &k); + static JSTaggedValue HandleReduceRightOfStable(JSThread *thread, JSHandle thisObjHandle, + JSHandle callbackFnHandle, + JSMutableHandle &accumulator, + JSHandle thisArgHandle, int64_t &k); private: static void SetSepValue(JSHandle sepStringHandle, int &sep, uint32_t &sepLength); diff --git a/test/moduletest/array/array.js b/test/moduletest/array/array.js index 01266f7549..63321d1f84 100644 --- a/test/moduletest/array/array.js +++ b/test/moduletest/array/array.js @@ -530,4 +530,37 @@ print(result_every2); var result_every3 = arr_every3.every(testEvery); print(result_every3); var result_every4 = arr_every4.every(testEvery4); -print(result_every4); \ No newline at end of file +print(result_every4); + +// Test case for reduceRight() +var arr_reduceRight1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +var arr_reduceRight2 = new Array(); +function testReduceRight(accumulator, element, index, array) { + if (index == 0) { + array.length = 6; + } + return accumulator + element; +} +function testReduceRight4(accumulator, element, index, array) { + array.pop(); + array.pop(); + return accumulator + element; +} +for (let i = 0; i < 10; i++) arr_reduceRight2[i] = i; +var arr_reduceRight3 = new Array(); +for (let i = 0; i < 10; i++) { + if (i < 9) { + continue; + } + arr_reduceRight3[i] = i; +} +var arr_reduceRight4 = new Array(); +for (let i = 0; i < 10; i++) arr_reduceRight4[i] = i; +var result_reduceRight1 = arr_reduceRight1.reduceRight(testReduceRight, 100); +print(result_reduceRight1); +var result_reduceRight2 = arr_reduceRight2.reduceRight(testReduceRight, 100); +print(result_reduceRight2); +var result_reduceRight3 = arr_reduceRight3.reduceRight(testReduceRight, 100); +print(result_reduceRight3); +var result_reduceRight4 = arr_reduceRight4.reduceRight(testReduceRight4, 100); +print(result_reduceRight4); \ No newline at end of file diff --git a/test/moduletest/array/expect_output.txt b/test/moduletest/array/expect_output.txt index cc863e6856..79ea1e882d 100644 --- a/test/moduletest/array/expect_output.txt +++ b/test/moduletest/array/expect_output.txt @@ -155,3 +155,7 @@ true true true true +145 +145 +109 +125