diff --git a/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp b/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp index 81ce3b244e876fe0fc9a3625b3602aed54bd3c44..7da1bce2488e8cab435024677d50055829f087b1 100644 --- a/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp +++ b/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp @@ -1883,20 +1883,19 @@ void BuiltinsArrayStubBuilder::Reduce(GateRef glue, GateRef thisValue, GateRef n Variable *result, Label *exit, Label *slowPath) { auto env = GetEnvironment(); - DEFVARIABLE(thisLen, VariableType::INT32(), Int32(0)); Label isHeapObject(env); Label isJsArray(env); Label atLeastOneArg(env); Label callbackFnHandleHeapObject(env); Label callbackFnHandleCallable(env); Label noTypeError(env); + Label writeResult(env); BRANCH(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath); Bind(&isHeapObject); BRANCH(IsJsArray(glue, thisValue), &isJsArray, slowPath); Bind(&isJsArray); // don't check constructor, "Reduce" won't create new array. - thisLen = GetArrayLength(thisValue); BRANCH(Int64GreaterThanOrEqual(numArgs, IntPtr(1)), &atLeastOneArg, slowPath); Bind(&atLeastOneArg); GateRef callbackFnHandle = GetCallArg0(numArgs); @@ -1904,7 +1903,8 @@ void BuiltinsArrayStubBuilder::Reduce(GateRef glue, GateRef thisValue, GateRef n Bind(&callbackFnHandleHeapObject); BRANCH(IsCallable(glue, callbackFnHandle), &callbackFnHandleCallable, slowPath); Bind(&callbackFnHandleCallable); - GateRef thisLenIsZero = Int32Equal(*thisLen, Int32(0)); + GateRef thisLen = GetArrayLength(thisValue); + GateRef thisLenIsZero = Int32Equal(thisLen, Int32(0)); GateRef numArgsLessThanTwo = Int64LessThan(numArgs, IntPtr(2)); BRANCH(BitAnd(thisLenIsZero, numArgsLessThanTwo), slowPath, &noTypeError); Bind(&noTypeError); @@ -1933,13 +1933,19 @@ void BuiltinsArrayStubBuilder::Reduce(GateRef glue, GateRef thisValue, GateRef n GateRef argList = newBuilder.NewTaggedArray(glue, argsLength); Label loopHead(env); Label next(env); + Label doCall(env); Label loopEnd(env); Label loopExit(env); Jump(&loopHead); LoopBegin(&loopHead); { - BRANCH(Int32LessThan(*k, *thisLen), &next, &loopExit); + BRANCH_LIKELY(Int32UnsignedLessThan(*k, thisLen), &next, &loopExit); Bind(&next); + // thisObj.length may change and needs to be rechecked. + // If the thisObj is stableArray and k >= current length, + // thisObj will have no chance to change and we can directly exit the entire loop. + BRANCH_LIKELY(Int32UnsignedLessThan(*k, GetArrayLength(thisValue)), &doCall, &writeResult); + Bind(&doCall); { Label updateK(env); Label notHole(env); @@ -1961,28 +1967,17 @@ void BuiltinsArrayStubBuilder::Reduce(GateRef glue, GateRef thisValue, GateRef n CallStubBuilder callBuilder(this, glue, callbackFnHandle, argsLength, 0, nullptr, Circuit::NullGate(), callArgs); GateRef callResult = callBuilder.JSCallDispatch(); - Label hasException1(env); - Label notHasException1(env); - BRANCH(HasPendingException(glue), &hasException1, ¬HasException1); - Bind(&hasException1); + Label hasException(env); + Label notHasException(env); + BRANCH(HasPendingException(glue), &hasException, ¬HasException); + Bind(&hasException); { result->WriteVariable(Exception()); Jump(exit); } - Bind(¬HasException1); - GateRef elements = GetElementsArray(glue, thisValue); - GateRef newLen = GetLengthOfTaggedArray(elements); - BRANCH(Int32LessThan(newLen, *thisLen), &changeThisLen, &updateCallResult); - Bind(&changeThisLen); - { - thisLen = newLen; - Jump(&updateCallResult); - } - Bind(&updateCallResult); - { - accumulator = callResult; - Jump(&loopEnd); - } + Bind(¬HasException); + accumulator = callResult; + Jump(&loopEnd); } } } @@ -2005,23 +2000,20 @@ void BuiltinsArrayStubBuilder::Reduce(GateRef glue, GateRef thisValue, GateRef n } Bind(¬StableJSArray); { - Label finish(env); Label callRT(env); - BRANCH(Int32LessThan(*k, *thisLen), &callRT, &finish); + BRANCH(Int32UnsignedLessThan(*k, thisLen), &callRT, &writeResult); Bind(&callRT); { accumulator = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(), RTSTUB_ID(JSArrayReduceUnStable), { thisValue, thisValue, IntToTaggedInt(*k), - IntToTaggedInt(*thisLen), *accumulator, callbackFnHandle }); - Jump(&finish); - } - Bind(&finish); - { - result->WriteVariable(*accumulator); - Jump(exit); + IntToTaggedInt(thisLen), *accumulator, callbackFnHandle }); + Jump(&writeResult); } } } + Bind(&writeResult); + result->WriteVariable(*accumulator); + Jump(exit); } } diff --git a/ecmascript/js_stable_array.cpp b/ecmascript/js_stable_array.cpp index 6ad5a1df3f7b8512478e5ffaf6b62c61765d6870..4f3f6ba334a3f90491274fa47a59f24e491527ee 100644 --- a/ecmascript/js_stable_array.cpp +++ b/ecmascript/js_stable_array.cpp @@ -1664,28 +1664,29 @@ JSTaggedValue JSStableArray::ToReversed(JSThread *thread, JSHandle rece return newArrayHandle.GetTaggedValue(); } -JSTaggedValue JSStableArray::Reduce(JSThread *thread, JSHandle thisObjHandle, - JSHandle callbackFnHandle, - JSMutableHandle accumulator, int64_t &k, int64_t &len) +void JSStableArray::Reduce(JSThread *thread, JSHandle thisObjHandle, JSHandle callbackFnHandle, + JSMutableHandle accumulator, int64_t &k, int64_t len) { - const GlobalEnvConstants *globalConst = thread->GlobalConstants(); + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); JSHandle thisObjVal(thisObjHandle); JSTaggedValue callResult = JSTaggedValue::Undefined(); while (k < len) { + // thisObj.length may change and needs to be rechecked. If the thisObj is stableArray and k >= current length, + // thisObj will have no chance to change and we can directly exit the entire loop. + if (UNLIKELY(k >= base::ArrayHelper::GetArrayLength(thread, thisObjVal))) { + k = len; + break; + } JSTaggedValue kValue(ElementAccessor::Get(thread, thisObjHandle, k)); if (!kValue.IsHole()) { - JSHandle undefined = globalConst->GetHandledUndefined(); const uint32_t argsLength = 4; // 4: «accumulator, kValue, k, O» EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, callbackFnHandle, undefined, undefined, argsLength); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + RETURN_IF_ABRUPT_COMPLETION(thread); info->SetCallArg(accumulator.GetTaggedValue(), kValue, JSTaggedValue(k), thisObjVal.GetTaggedValue()); callResult = JSFunction::Call(info); - RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); - if (ElementAccessor::GetElementsLength(thread, thisObjHandle) < len) { - len = ElementAccessor::GetElementsLength(thread, thisObjHandle); - } + RETURN_IF_ABRUPT_COMPLETION(thread); accumulator.Update(callResult); } k++; @@ -1693,7 +1694,6 @@ JSTaggedValue JSStableArray::Reduce(JSThread *thread, JSHandle thisObj break; } } - return base::BuiltinsBase::GetTaggedDouble(true); } JSTaggedValue JSStableArray::Slice(JSThread *thread, JSHandle thisObjHandle, diff --git a/ecmascript/js_stable_array.h b/ecmascript/js_stable_array.h index 149d732b04594c51f7da6b3e7b06dd68e9470c0c..f5e68a85e103cd941585ad6b7f847b4f87dad7a3 100644 --- a/ecmascript/js_stable_array.h +++ b/ecmascript/js_stable_array.h @@ -104,9 +104,8 @@ public: static JSTaggedValue ToSpliced(JSHandle receiver, EcmaRuntimeCallInfo *argv, int64_t argc, int64_t actualStart, int64_t actualSkipCount, int64_t insertCount); static JSTaggedValue ToReversed(JSThread *thread, JSHandle receiver, int64_t insertCount); - static JSTaggedValue Reduce(JSThread *thread, JSHandle thisObjHandle, - JSHandle callbackFnHandle, - JSMutableHandle accumulator, int64_t &k, int64_t &len); + static void Reduce(JSThread *thread, JSHandle thisObjHandle, JSHandle callbackFnHandle, + JSMutableHandle accumulator, int64_t &k, int64_t len); static JSTaggedValue Slice(JSThread *thread, JSHandle thisObjHandle, int64_t &k, int64_t &count); static JSHandle SortIndexedProperties(JSThread *thread, const JSHandle &thisObj, int64_t len, const JSHandle &callbackFnHandle, diff --git a/test/moduletest/arrayreducecase/arrayreducecase.js b/test/moduletest/arrayreducecase/arrayreducecase.js index 37e048dc9babe7d2f3f9549bb3b01ec83bc1b4d9..57651ac1ef03e46ff76bb425b338594b83a86336 100644 --- a/test/moduletest/arrayreducecase/arrayreducecase.js +++ b/test/moduletest/arrayreducecase/arrayreducecase.js @@ -95,3 +95,41 @@ try { } catch (error) { print(error); } + +{ + let arr = new Array(10000); + arr.fill(1); + let res = arr.reduce((e, v, idx)=>{ + if (idx == 1) { + for (let j = 0; j < 9990; ++j) { + arr.pop(); + } + } + if (idx == 2) { + arr.push(100); + } + return e + v; + }, 0); + print(res); +} + +{ + let arr = new Array(10000); + arr.fill(1); + let res = arr.reduce((e, v, idx)=>{ + if (idx == 1) { + // change to small dictionary + for (let j = 0; j < 9990; ++j) { + arr.pop(); + } + arr[1100] = 345; + } + if (idx == 2) { + for (let j = 0; j < 10000; ++j) { + arr[j] = 1; + } + } + return e + v; + }, 0); + print(res); +} \ No newline at end of file diff --git a/test/moduletest/arrayreducecase/expect_output.txt b/test/moduletest/arrayreducecase/expect_output.txt index 3a0c9a88c6a93ba8595066dd8623f576941cdca3..9684b01fe284e07eaf68c773065c3100f8bf40ba 100644 --- a/test/moduletest/arrayreducecase/expect_output.txt +++ b/test/moduletest/arrayreducecase/expect_output.txt @@ -20,3 +20,5 @@ a,b,c,e,d 12,4 1200 Reduce of empyt array with no initial value +110 +10000 \ No newline at end of file