diff --git a/ecmascript/base/array_helper.cpp b/ecmascript/base/array_helper.cpp index ce74b02e5a2df9f434d982ea09440ce9f33f1dd9..342ed600418d6a8fc5900ad71bf607d73f709302 100644 --- a/ecmascript/base/array_helper.cpp +++ b/ecmascript/base/array_helper.cpp @@ -375,6 +375,94 @@ JSTaggedValue ArrayHelper::FlattenIntoArray(JSThread *thread, const JSHandle +JSTaggedValue ArrayHelper::FlatMapFromIndex(JSThread *thread, const JSHandle& srcValue, + const JSHandle& resValue, + [[maybe_unused]] const JSHandle& mapFunc, + [[maybe_unused]] const JSHandle& thisArg, + int64_t& targetIdx, int64_t curSrcValueIdx, int64_t srcValueLen) +{ + ASSERT((isFirstLayer && mapFunc->IsCallable()) || (!isFirstLayer && mapFunc->IsUndefined())); + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + JSMutableHandle element(thread, JSTaggedValue::Undefined()); + JSMutableHandle targetIndexHandle(thread, JSTaggedValue::Undefined()); + JSMutableHandle sourceIndexHandle(thread, JSTaggedValue::Undefined()); + for (int64_t i = curSrcValueIdx; i < srcValueLen; ++i) { + sourceIndexHandle.Update(JSTaggedValue(i)); + JSHandle sourceIndexStr(JSTaggedValue::ToString(thread, sourceIndexHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + bool exists = JSTaggedValue::HasProperty(thread, srcValue, sourceIndexStr); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (!exists) { + continue; + } + element.Update(JSArray::FastGetPropertyByValue(thread, srcValue, sourceIndexStr)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if constexpr (isFirstLayer) { + // call mapFunc + const int32_t argsLength = 3; // 3: « element, sourceIndexStr, srcValue » + EcmaRuntimeCallInfo *info = + EcmaInterpreter::NewRuntimeCallInfo(thread, mapFunc, thisArg, undefined, argsLength); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + info->SetCallArg(element.GetTaggedValue(), sourceIndexStr.GetTaggedValue(), srcValue.GetTaggedValue()); + JSTaggedValue obj = JSFunction::Call(info); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + element.Update(obj); + // check IsArray and continue flat + bool shouldFlatten = element->IsArray(thread); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (shouldFlatten) { + int64_t elementLen = ArrayHelper::GetLength(thread, element); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + FlatMapFromIndex(thread, element, resValue, undefined, undefined, targetIdx, 0, elementLen); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + continue; + } + } + if (targetIdx > base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + targetIndexHandle.Update(JSTaggedValue(targetIdx)); + JSHandle targetIndexStr(JSTaggedValue::ToString(thread, targetIndexHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, resValue, targetIndexStr, element); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ++targetIdx; + } + return resValue.GetTaggedValue(); +} + +JSTaggedValue ArrayHelper::FlatMapFromIndexAfterCall(JSThread *thread, const JSHandle& srcValue, + const JSHandle& resValue, + const JSHandle& mapFunc, + const JSHandle& thisArg, + const JSHandle& curItem, + int64_t& targetIdx, int64_t curSrcValueIdx, int64_t srcValueLen) +{ + // curItem is the result of mapFunc(srcValue[curSrcValueIdx]), continue to do flat + bool shouldFlatten = curItem->IsArray(thread); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + if (shouldFlatten) { + int64_t curItemLen = ArrayHelper::GetLength(thread, curItem); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSHandle undefined = thread->GlobalConstants()->GetHandledUndefined(); + FlatMapFromIndex(thread, curItem, resValue, undefined, undefined, targetIdx, 0, curItemLen); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + } else { + if (targetIdx > base::MAX_SAFE_INTEGER) { + THROW_TYPE_ERROR_AND_RETURN(thread, "out of range.", JSTaggedValue::Exception()); + } + JSHandle targetIndexHandle(thread, JSTaggedValue(targetIdx)); + JSHandle targetIndexStr(JSTaggedValue::ToString(thread, targetIndexHandle)); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + JSObject::CreateDataPropertyOrThrow(thread, resValue, targetIndexStr, curItem); + RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); + ++targetIdx; + } + return FlatMapFromIndex(thread, srcValue, resValue, mapFunc, thisArg, + targetIdx, curSrcValueIdx + 1, srcValueLen); +} + JSHandle ArrayHelper::SortIndexedProperties(JSThread *thread, const JSHandle &thisObj, int64_t len, const JSHandle &callbackFnHandle, HolesType holes) diff --git a/ecmascript/base/array_helper.h b/ecmascript/base/array_helper.h index eeffa204ca44ed3043adbcb6286134ac9106b612..5dcee88e9be7ca2344c514fd3a3a4fcf6bbf3037 100644 --- a/ecmascript/base/array_helper.h +++ b/ecmascript/base/array_helper.h @@ -70,6 +70,18 @@ public: const JSHandle &thisObjVal, const FlattenArgs &args, const JSHandle &mapperFunctionHandle, const JSHandle &thisArg); + template + static JSTaggedValue FlatMapFromIndex(JSThread *thread, const JSHandle& srcValue, + const JSHandle& resValue, + const JSHandle& mapFunc, + const JSHandle& thisArg, + int64_t& targetIdx, int64_t curSrcValueIdx, int64_t srcValueLen); + static JSTaggedValue FlatMapFromIndexAfterCall(JSThread *thread, const JSHandle& srcValue, + const JSHandle& resValue, + const JSHandle& mapFunc, + const JSHandle& thisArg, + const JSHandle& curItem, + int64_t& targetIdx, int64_t curSrcValueIdx, int64_t srcValueLen); static JSHandle SortIndexedProperties(JSThread *thread, const JSHandle &thisObj, int64_t len, const JSHandle &callbackFnHandle, HolesType holes); diff --git a/ecmascript/builtins/builtins_array.cpp b/ecmascript/builtins/builtins_array.cpp index 37532713a5a302a3442c7c58178ff306ad71dc5e..e44a6c3fefb90017e89bead6a38b4c3ef20163f5 100644 --- a/ecmascript/builtins/builtins_array.cpp +++ b/ecmascript/builtins/builtins_array.cpp @@ -2890,15 +2890,15 @@ JSTaggedValue BuiltinsArray::FlatMap(EcmaRuntimeCallInfo *argv) THROW_TYPE_ERROR_AND_RETURN(thread, "the mapperFunction is not callable.", JSTaggedValue::Exception()); } // 4. Let A be ? ArraySpeciesCreate(O, 0). - uint32_t arrayLen = 0; - JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(arrayLen)); + JSTaggedValue newArray = JSArray::ArraySpeciesCreate(thread, thisObjHandle, JSTaggedNumber(0U)); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); base::FlattenArgs args = { sourceLen, 0, 1 }; JSHandle newArrayHandle(thread, newArray); // 5. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, thisArg). - ArrayHelper::FlattenIntoArray(thread, newArrayHandle, thisObjVal, args, - mapperFunctionHandle, GetCallArg(argv, 1)); + int64_t targetIdx = 0; + ArrayHelper::FlatMapFromIndex(thread, thisObjVal, newArrayHandle, mapperFunctionHandle, + GetCallArg(argv, 1), targetIdx, 0, sourceLen); RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); // 6. Return A. return newArrayHandle.GetTaggedValue(); diff --git a/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp b/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp index 81ce3b244e876fe0fc9a3625b3602aed54bd3c44..57e7738d661fe9670bec9f863035803fd1291b8f 100644 --- a/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp +++ b/ecmascript/compiler/builtins/builtins_array_stub_builder.cpp @@ -4975,6 +4975,261 @@ void BuiltinsArrayStubBuilder::FindLast(GateRef glue, GateRef thisValue, GateRef } } +void BuiltinsArrayStubBuilder::FlatMap(GateRef glue, GateRef thisValue, GateRef numArgs, + Variable *result, Label *exit, Label *slowPath) +{ + auto env = GetEnvironment(); + Label isStable(env); + Label defaultConstr(env); + Label notJSCOWArray(env); + Label mapFuncIsHeapObject(env); + Label mapFuncIsCallable(env); + BRANCH_LIKELY(IsStableJSArray(glue, thisValue), &isStable, slowPath); + Bind(&isStable); + // need check constructor, "FlatMap" should use ArraySpeciesCreate + BRANCH_LIKELY(HasConstructor(glue, thisValue), slowPath, &defaultConstr); + Bind(&defaultConstr); + BRANCH(IsJsCOWArray(glue, thisValue), slowPath, ¬JSCOWArray); + Bind(¬JSCOWArray); + GateRef mapFunc = GetCallArg0(numArgs); + BRANCH_LIKELY(TaggedIsHeapObject(mapFunc), &mapFuncIsHeapObject, slowPath); + Bind(&mapFuncIsHeapObject); + BRANCH_LIKELY(IsCallable(glue, mapFunc), &mapFuncIsCallable, slowPath); + Bind(&mapFuncIsCallable); + GateRef argHandle = GetCallArg1(numArgs); + + GateRef srcLength = GetArrayLength(thisValue); + GateRef resArray = NewArray(glue, srcLength); + DEFVARIABLE(resElements, VariableType::JS_POINTER(), GetElementsArray(glue, resArray)); + DEFVARIABLE(resElementsCap, VariableType::INT32(), GetLengthOfTaggedArray(*resElements)); + DEFVARIABLE(i, VariableType::INT32(), Int32(0)); + DEFVARIABLE(targetIndex, VariableType::INT32(), Int32(0)); + DEFVARIABLE(kValue, VariableType::JS_ANY(), Hole()); + + Label loopHead(env); + Label loopEnd(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Label next(env); + BRANCH_LIKELY(Int32UnsignedLessThan(*i, srcLength), &next, &loopExit); + Bind(&next); + Label isStableInLoop(env); + Label notStableInLoop(env); + // array may be modified, recheck if the array is still stable + BRANCH_LIKELY(IsStableJSArray(glue, thisValue), &isStableInLoop, ¬StableInLoop); + Bind(¬StableInLoop); + { + // set current elements to resArray and CallRuntime + SetArrayLength(glue, resArray, *targetIndex); + SetElementsArray(VariableType::JS_POINTER(), glue, resArray, *resElements); + GateRef runtimeResult = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(), + RTSTUB_ID(ContinueFlatMapBeforeCall), {thisValue, resArray, mapFunc, argHandle, + IntUnsignedToTaggedInt(*targetIndex), IntUnsignedToTaggedInt(*i), IntUnsignedToTaggedInt(srcLength)}); + result->WriteVariable(runtimeResult); + Jump(exit); + } + Bind(&isStableInLoop); + Label getElement(env); + // array may be modified, and the current length needs to be checked + BRANCH_LIKELY(Int32UnsignedLessThan(*i, GetArrayLength(thisValue)), &getElement, &loopEnd); + Bind(&getElement); + kValue = GetTaggedValueWithElementsKind(glue, thisValue, *i); + // call mapFunc + Label callDispatch(env); + BRANCH_UNLIKELY(TaggedIsHole(*kValue), &loopEnd, &callDispatch); + Bind(&callDispatch); + GateRef key = IntUnsignedToTaggedInt(*i); + JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN); + callArgs.callThisArg3WithReturnArgs = { argHandle, *kValue, key, thisValue }; + CallStubBuilder callBuilder(this, glue, mapFunc, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, nullptr, + Circuit::NullGate(), callArgs, ProfileOperation(), false); + GateRef element = callBuilder.JSCallDispatch(); + Label hasException(env); + Label noException(env); + BRANCH_UNLIKELY(HasPendingException(glue), &hasException, &noException); + Bind(&hasException); + { + result->WriteVariable(Exception()); + Jump(exit); + } + Bind(&noException); + // do flat + Label elementIsHeapObj(env); + Label elementNotHeapObj(env); + Label flatInnerArray(env); + Label setElementToRes(env); + BRANCH(TaggedIsHeapObject(element), &elementIsHeapObj, &elementNotHeapObj); + Bind(&elementIsHeapObj); + { + Label elementNotJSArray(env); + BRANCH(IsJsArray(glue, element), &flatInnerArray, &elementNotJSArray); + Bind(&elementNotJSArray); + { + Label elementIsProxy(env); + BRANCH_UNLIKELY(IsJsProxy(glue, element), &elementIsProxy, &setElementToRes); + Bind(&elementIsProxy); + // set current elements to resArray and CallRuntime + SetArrayLength(glue, resArray, *targetIndex); + SetElementsArray(VariableType::JS_POINTER(), glue, resArray, *resElements); + GateRef runtimeResult = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(), + RTSTUB_ID(ContinueFlatMapAfterCall), {thisValue, resArray, mapFunc, argHandle, + IntUnsignedToTaggedInt(*targetIndex), IntUnsignedToTaggedInt(*i), + IntUnsignedToTaggedInt(srcLength), element}); + result->WriteVariable(runtimeResult); + Jump(exit); + } + } + Bind(&elementNotHeapObj); + { + BRANCH(TaggedIsHole(element), &loopEnd, &setElementToRes); + } + Bind(&flatInnerArray); + { + Label exception(env); + FlatInnerArray(glue, resElements, targetIndex, resElementsCap, element, &loopEnd, &exception); + Bind(&exception); + result->WriteVariable(Exception()); + Jump(exit); + } + Bind(&setElementToRes); + { + // Due to space limitations, targetIndex > MAX_SAFE_INTEGER is impossible, so we skip this check + Label setTarget(env); + Label needExpend(env); + BRANCH_LIKELY(Int32UnsignedLessThan(*targetIndex, *resElementsCap), &setTarget, &needExpend); + Bind(&needExpend); + resElementsCap = ComputeElementCapacity(*resElementsCap); + NewObjectStubBuilder newBuilder(this); + resElements = newBuilder.ExtendArrayWithOptimizationCheck(glue, *resElements, *resElementsCap); + Jump(&setTarget); + Bind(&setTarget); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, *resElements, *targetIndex, element); + targetIndex = Int32Add(*targetIndex, Int32(1)); + Jump(&loopEnd); + } + } + Bind(&loopEnd); + i = Int32Add(*i, Int32(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + + Label needTrim(env); + Label setElements(env); + GateRef unused = Int32Sub(*resElementsCap, *targetIndex); + BRANCH(Int32UnsignedGreaterThan(unused, Int32(TaggedArray::MAX_END_UNUSED)), &needTrim, &setElements); + Bind(&needTrim); + { + CallNGCRuntime(glue, RTSTUB_ID(ArrayTrim), {glue, *resElements, ZExtInt32ToInt64(*targetIndex)}); + Jump(&setElements); + } + Bind(&setElements); + SetArrayLength(glue, resArray, *targetIndex); + SetElementsArray(VariableType::JS_POINTER(), glue, resArray, *resElements); + result->WriteVariable(resArray); + Jump(exit); +} + +void BuiltinsArrayStubBuilder::FlatInnerArray(GateRef glue, Variable &resElements, Variable &targetIndex, + Variable &resElementsCap, GateRef value, Label *exit, Label *exception) +{ + auto env = GetEnvironment(); + Label isStable(env); + Label notStable(env); + Label out(env); + DEFVARIABLE(i, VariableType::INT32(), Int32(0)); + GateRef valueLength = GetArrayLength(value); + BRANCH(IsStableJSArray(glue, value), &isStable, ¬Stable); + Bind(&isStable); + { + Label loopHead(env); + Label loopEnd(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Label next(env); + BRANCH_LIKELY(Int32UnsignedLessThan(*i, valueLength), &next, &loopExit); + Bind(&next); + GateRef kValue = GetTaggedValueWithElementsKind(glue, value, *i); + Label setElement(env); + BRANCH(TaggedIsHole(kValue), &loopEnd, &setElement); + Bind(&setElement); + { + Label setTarget(env); + Label needExpend(env); + BRANCH_LIKELY(Int32UnsignedLessThan(*targetIndex, *resElementsCap), &setTarget, &needExpend); + Bind(&needExpend); + resElementsCap = ComputeElementCapacity(*resElementsCap); + NewObjectStubBuilder newBuilder(this); + resElements = newBuilder.ExtendArrayWithOptimizationCheck(glue, *resElements, *resElementsCap); + Jump(&setTarget); + Bind(&setTarget); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, *resElements, *targetIndex, kValue); + targetIndex = Int32Add(*targetIndex, Int32(1)); + } + Jump(&loopEnd); + } + Bind(&loopEnd); + i = Int32Add(*i, Int32(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + Jump(&out); + } + Bind(¬Stable); + { + Label loopHead(env); + Label loopEnd(env); + Label loopExit(env); + Jump(&loopHead); + LoopBegin(&loopHead); + { + Label next(env); + Label hasProperty(env); + Label noException1(env); + Label noException2(env); + BRANCH_LIKELY(Int32UnsignedLessThan(*i, valueLength), &next, &loopExit); + Bind(&next); + GateRef hasProp = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(), RTSTUB_ID(HasProperty), + { value, IntToTaggedInt(*i) }); + BRANCH_UNLIKELY(HasPendingException(glue), exception, &noException1); + Bind(&noException1); + BRANCH(TaggedIsTrue(hasProp), &hasProperty, &loopEnd); + Bind(&hasProperty); + { + GateRef kValue = FastGetPropertyByIndex(glue, value, *i, ProfileOperation()); + BRANCH_UNLIKELY(HasPendingException(glue), exception, &noException2); + Bind(&noException2); + { + Label setTarget(env); + Label needExpend(env); + BRANCH_LIKELY(Int32UnsignedLessThan(*targetIndex, *resElementsCap), &setTarget, &needExpend); + Bind(&needExpend); + resElementsCap = ComputeElementCapacity(*resElementsCap); + NewObjectStubBuilder newBuilder(this); + resElements = newBuilder.ExtendArrayWithOptimizationCheck(glue, *resElements, *resElementsCap); + Jump(&setTarget); + Bind(&setTarget); + SetValueToTaggedArray(VariableType::JS_ANY(), glue, *resElements, *targetIndex, kValue); + targetIndex = Int32Add(*targetIndex, Int32(1)); + } + Jump(&loopEnd); + } + } + Bind(&loopEnd); + i = Int32Add(*i, Int32(1)); + LoopEnd(&loopHead); + Bind(&loopExit); + Jump(&out); + } + Bind(&out); + { + Jump(exit); + } +} + + void BuiltinsArrayStubBuilder::FastCreateArrayWithArgv(GateRef glue, Variable *res, GateRef argc, GateRef hclass, Label *exit) { @@ -5182,314 +5437,6 @@ void BuiltinsArrayStubBuilder::GenArrayConstructor(GateRef glue, GateRef nativeC Return(*res); } -void BuiltinsArrayStubBuilder::FlatMap(GateRef glue, GateRef thisValue, GateRef numArgs, - Variable *result, Label *exit, Label *slowPath) -{ - auto env = GetEnvironment(); - Label isHeapObject(env); - Label isJsArray(env); - Label defaultConstr(env); - Label isStability(env); - Label notCOWArray(env); - Label equalCls(env); - Label isGeneric(env); - BRANCH_LIKELY(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath); - Bind(&isHeapObject); - BRANCH_LIKELY(IsJsArray(glue, thisValue), &isJsArray, slowPath); - Bind(&isJsArray); - // need check constructor, "FlatMap" should use ArraySpeciesCreate - BRANCH_LIKELY(HasConstructor(glue, thisValue), slowPath, &defaultConstr); - Bind(&defaultConstr); - BRANCH_LIKELY(IsStableJSArray(glue, thisValue), &isStability, slowPath); - Bind(&isStability); - BRANCH(IsJsCOWArray(glue, thisValue), slowPath, ¬COWArray); - Bind(¬COWArray); - Label arg0HeapObject(env); - Label callable(env); - Label thisNotStable(env); - Label doFlat(env); - GateRef callbackFnHandle = GetCallArg0(numArgs); - BRANCH_LIKELY(TaggedIsHeapObject(callbackFnHandle), &arg0HeapObject, slowPath); - Bind(&arg0HeapObject); - BRANCH_LIKELY(IsCallable(glue, callbackFnHandle), &callable, slowPath); - Bind(&callable); - GateRef argHandle = GetCallArg1(numArgs); - - DEFVARIABLE(i, VariableType::INT64(), Int64(0)); - DEFVARIABLE(thisArrLen, VariableType::INT64(), ZExtInt32ToInt64(GetArrayLength(thisValue))); - DEFVARIABLE(newArrLen, VariableType::INT64(), ZExtInt32ToInt64(GetArrayLength(thisValue))); - GateRef mappedArray = NewArray(glue, *thisArrLen); - GateRef mappedElements = GetElementsArray(glue, mappedArray); - // fast path for stable array - { - DEFVARIABLE(kValue, VariableType::JS_ANY(), Hole()); - Label loopHead(env); - Label loopEnd(env); - Label next(env); - Label loopExit(env); - Jump(&loopHead); - LoopBegin(&loopHead); - { - Label nextStep(env); - Label kValueIsHole(env); - Label callDispatch(env); - Label hasProperty(env); - Label changeNewArrLen(env); - Label hasException0(env); - Label notHasException0(env); - Label hasException1(env); - Label notHasException1(env); - BRANCH_LIKELY(IsStableJSArray(glue, thisValue), &nextStep, &thisNotStable); - Bind(&nextStep); - BRANCH_LIKELY(Int64LessThan(*i, *thisArrLen), &next, &loopExit); - Bind(&next); - kValue = GetTaggedValueWithElementsKind(glue, thisValue, *i); - BRANCH_UNLIKELY(TaggedIsHole(*kValue), &kValueIsHole, &callDispatch); - Bind(&kValueIsHole); - { - newArrLen = Int64Sub(*newArrLen, Int64(1)); - Jump(&loopEnd); - } - Bind(&callDispatch); - { - GateRef key = Int64ToTaggedInt(*i); - JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN); - callArgs.callThisArg3WithReturnArgs = { argHandle, *kValue, key, thisValue }; - CallStubBuilder callBuilder(this, glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, nullptr, - Circuit::NullGate(), callArgs, ProfileOperation(), false); - GateRef retValue = callBuilder.JSCallDispatch(); - BRANCH_UNLIKELY(HasPendingException(glue), &hasException1, ¬HasException1); - Bind(&hasException1); - { - result->WriteVariable(Exception()); - Jump(exit); - } - Bind(¬HasException1); - { - DEFVARIABLE(newLen, VariableType::INT64(), ZExtInt32ToInt64(GetArrayLength(thisValue))); - Label changeThisLen(env); - Label afterChangeLen(env); - Label retValueIsHeapObject(env); - Label retValueIsJsArray(env); - BRANCH_UNLIKELY(Int64LessThan(*newLen, *thisArrLen), &changeThisLen, &afterChangeLen); - Bind(&changeThisLen); - { - newArrLen = Int64Sub(*newArrLen, Int64Sub(*thisArrLen, *newLen)); - thisArrLen = *newLen; - Jump(&afterChangeLen); - } - Bind(&afterChangeLen); - { - SetValueToTaggedArray(VariableType::JS_ANY(), glue, mappedElements, *i, retValue); - BRANCH(TaggedIsHeapObject(retValue), &retValueIsHeapObject, &loopEnd); - Bind(&retValueIsHeapObject); - { - BRANCH_NO_WEIGHT(IsJsArray(glue, retValue), &retValueIsJsArray, &loopEnd); - } - Bind(&retValueIsJsArray); - { - // newArray only contains non-hole elements - // but elementsLength is bigger than number of non-hole elements - // so should trim after flat - GateRef retElements = GetElementsArray(glue, retValue); - GateRef elementsLength = GetLengthOfTaggedArray(retElements); - newArrLen = Int64Sub(Int64Add(*newArrLen, elementsLength), Int64(1)); - Jump(&loopEnd); - } - } - } - } - } - Bind(&loopEnd); - i = Int64Add(*i, Int64(1)); - LoopEnd(&loopHead); - Bind(&loopExit); - Jump(&doFlat); - } - - Bind(&thisNotStable); - { - DEFVARIABLE(kValue, VariableType::JS_ANY(), Hole()); - Label loopHead(env); - Label loopEnd(env); - Label next(env); - Label loopExit(env); - Jump(&loopHead); - LoopBegin(&loopHead); - { - Label hasProperty(env); - Label changeNewArrLen(env); - Label hasException0(env); - Label notHasException0(env); - Label callDispatch(env); - Label hasException1(env); - Label notHasException1(env); - BRANCH(Int64LessThan(*i, *thisArrLen), &next, &loopExit); - Bind(&next); - GateRef hasProp = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(), RTSTUB_ID(HasProperty), - { thisValue, IntToTaggedInt(*i) }); - BRANCH(TaggedIsTrue(hasProp), &hasProperty, &changeNewArrLen); - Bind(&hasProperty); - { - kValue = FastGetPropertyByIndex(glue, thisValue, TruncInt64ToInt32(*i), ProfileOperation()); - BRANCH(HasPendingException(glue), &hasException0, ¬HasException0); - Bind(&hasException0); - { - result->WriteVariable(Exception()); - Jump(exit); - } - Bind(¬HasException0); - { - BRANCH(TaggedIsHole(*kValue), &changeNewArrLen, &callDispatch); - } - } - Bind(&changeNewArrLen); - { - newArrLen = Int64Sub(*newArrLen, Int64(1)); - Jump(&loopEnd); - } - Bind(&callDispatch); - { - GateRef key = Int64ToTaggedInt(*i); - JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN); - callArgs.callThisArg3WithReturnArgs = { argHandle, *kValue, key, thisValue }; - CallStubBuilder callBuilder(this, glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, nullptr, - Circuit::NullGate(), callArgs); - GateRef retValue = callBuilder.JSCallDispatch(); - BRANCH_UNLIKELY(HasPendingException(glue), &hasException1, ¬HasException1); - Bind(&hasException1); - { - result->WriteVariable(Exception()); - Jump(exit); - } - Bind(¬HasException1); - { - Label retValueIsHeapObject(env); - Label retValueIsJsArray(env); - SetValueToTaggedArray(VariableType::JS_ANY(), glue, mappedElements, *i, retValue); - BRANCH(TaggedIsHeapObject(retValue), &retValueIsHeapObject, &loopEnd); - Bind(&retValueIsHeapObject); - { - BRANCH(IsJsArray(glue, retValue), &retValueIsJsArray, &loopEnd); - } - Bind(&retValueIsJsArray); - { - // newArray only contains non-hole elements - // but elementsLength is bigger than number of non-hole elements - // so should trim after flat - GateRef retElements = GetElementsArray(glue, retValue); - GateRef elementsLength = GetLengthOfTaggedArray(retElements); - newArrLen = Int64Sub(Int64Add(*newArrLen, elementsLength), Int64(1)); - Jump(&loopEnd); - } - } - } - } - Bind(&loopEnd); - i = Int64Add(*i, Int64(1)); - LoopEnd(&loopHead); - Bind(&loopExit); - Jump(&doFlat); - } - - Bind(&doFlat); - { - i = Int64(0); - DEFVARIABLE(j, VariableType::INT64(), Int64(0)); - DEFVARIABLE(retValueItem, VariableType::JS_ANY(), Hole()); - GateRef newArray = NewArray(glue, *newArrLen); - Label loopHead2(env); - Label loopEnd2(env); - Label next2(env); - Label loopExit2(env); - Jump(&loopHead2); - LoopBegin(&loopHead2); - { - Label nextStep(env); - Label retValueIsHeapObject(env); - Label retValueIsJsArray(env); - Label retValueIsNotJsArray(env); - BRANCH_LIKELY(Int64LessThan(*i, *thisArrLen), &next2, &loopExit2); - Bind(&next2); - GateRef retValue = GetValueFromTaggedArray(glue, mappedElements, *i); - BRANCH(TaggedIsHole(retValue), &loopEnd2, &nextStep); - Bind(&nextStep); - BRANCH_NO_WEIGHT(TaggedIsHeapObject(retValue), &retValueIsHeapObject, &retValueIsNotJsArray); - Bind(&retValueIsHeapObject); - { - BRANCH_NO_WEIGHT(IsJsArray(glue, retValue), &retValueIsJsArray, &retValueIsNotJsArray); - Bind(&retValueIsJsArray); - { - Label retValueIsStableArray(env); - Label retValueNotStableArray(env); - GateRef retValueIsStable = IsStableJSArray(glue, retValue); - GateRef arrLen = ZExtInt32ToInt64(GetArrayLength(retValue)); - DEFVARIABLE(k, VariableType::INT64(), Int64(0)); - Label loopHead3(env); - Label loopEnd3(env); - Label next3(env); - Label loopExit3(env); - Label setValue(env); - Label itemExist(env); - Jump(&loopHead3); - LoopBegin(&loopHead3); - { - BRANCH_LIKELY(Int64LessThan(*k, arrLen), &next3, &loopExit3); - Bind(&next3); - BRANCH_LIKELY(retValueIsStable, &retValueIsStableArray, &retValueNotStableArray); - Bind(&retValueIsStableArray); - retValueItem = GetTaggedValueWithElementsKind(glue, retValue, *k); - BRANCH_NO_WEIGHT(TaggedIsHole(*retValueItem), &loopEnd3, &setValue); - Bind(&retValueNotStableArray); - GateRef hasProp = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(), RTSTUB_ID(HasProperty), - { retValue, IntToTaggedInt(*k) }); - BRANCH_NO_WEIGHT(TaggedIsTrue(hasProp), &itemExist, &loopEnd3); - Bind(&itemExist); - retValueItem = - FastGetPropertyByIndex(glue, retValue, TruncInt64ToInt32(*k), ProfileOperation()); - Jump(&setValue); - Bind(&setValue); - SetValueWithElementsKind(glue, newArray, *retValueItem, *j, Boolean(true), - Int32(Elements::ToUint(ElementsKind::NONE))); - j = Int64Add(*j, Int64(1)); - Jump(&loopEnd3); - } - Bind(&loopEnd3); - k = Int64Add(*k, Int64(1)); - LoopEnd(&loopHead3); - Bind(&loopExit3); - Jump(&loopEnd2); - } - } - Bind(&retValueIsNotJsArray); - { - SetValueWithElementsKind(glue, newArray, retValue, *j, Boolean(true), - Int32(Elements::ToUint(ElementsKind::NONE))); - j = Int64Add(*j, Int64(1)); - Jump(&loopEnd2); - } - } - Bind(&loopEnd2); - i = Int64Add(*i, Int64(1)); - LoopEnd(&loopHead2); - Bind(&loopExit2); - Label trim(env); - Label noTrim(env); - BRANCH(Int32GreaterThan(*newArrLen, *j), &trim, &noTrim); - Bind(&trim); - { - GateRef elements = GetElementsArray(glue, newArray); - CallNGCRuntime(glue, RTSTUB_ID(ArrayTrim), {glue, elements, *j}); - SetArrayLength(glue, newArray, TruncInt64ToInt32(*j)); - result->WriteVariable(newArray); - Jump(exit); - } - Bind(&noTrim); - result->WriteVariable(newArray); - Jump(exit); - } -} - void BuiltinsArrayStubBuilder::IsArray(GateRef glue, [[maybe_unused]] 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 463de52b4aa8ddf7cdd2d2abe26ff804abb1ed46..c9b5a66c12c657533047286ec79406a639fb8232 100644 --- a/ecmascript/compiler/builtins/builtins_array_stub_builder.h +++ b/ecmascript/compiler/builtins/builtins_array_stub_builder.h @@ -114,6 +114,9 @@ BUILTINS_WITH_ARRAY_STUB_BUILDER(DECLARE_BUILTINS_ARRAY_STUB_BUILDER) GateRef IndexOfGeneric( GateRef glue, GateRef elements, GateRef target, GateRef fromIndex, GateRef len, IndexOfOptions options); + void FlatInnerArray(GateRef glue, Variable &resElements, Variable &targetIndex, + Variable &resElementsCap, GateRef value, Label *exit, Label *exception); + private: static constexpr uint32_t MAX_LENGTH_ZERO = 0; static constexpr uint32_t MAX_LENGTH_ONE = 1; diff --git a/ecmascript/compiler/stub_builder-inl.h b/ecmascript/compiler/stub_builder-inl.h index 90bf40f9f4e4fb56e07263499f889d9c49a12984..bbfe66cdacd77926cb2eaab1fce9041d93c507d6 100644 --- a/ecmascript/compiler/stub_builder-inl.h +++ b/ecmascript/compiler/stub_builder-inl.h @@ -1072,6 +1072,12 @@ inline GateRef StubBuilder::IntToTaggedInt(GateRef x) return env_->GetBuilder()->ToTaggedInt(val); } +inline GateRef StubBuilder::IntUnsignedToTaggedInt(GateRef x) +{ + GateRef val = ZExtInt32ToInt64(x); + return env_->GetBuilder()->ToTaggedInt(val); +} + inline GateRef StubBuilder::Int64ToTaggedInt(GateRef x) { return env_->GetBuilder()->ToTaggedInt(x); diff --git a/ecmascript/compiler/stub_builder.cpp b/ecmascript/compiler/stub_builder.cpp index fc3618c2996397701b52ef86b9bf617f2bed6078..fc4b9708914e7e4b257476e672bd571f0fb7603b 100644 --- a/ecmascript/compiler/stub_builder.cpp +++ b/ecmascript/compiler/stub_builder.cpp @@ -1231,7 +1231,7 @@ GateRef StubBuilder::ComputeElementCapacity(GateRef oldLength) GateRef newL = Int32Add(oldLength, Int32LSR(oldLength, Int32(1))); Label reachMin(env); Label notReachMin(env); - BRANCH(Int32GreaterThan(newL, Int32(JSObject::MIN_ELEMENTS_LENGTH)), &reachMin, ¬ReachMin); + BRANCH(Int32UnsignedGreaterThan(newL, Int32(JSObject::MIN_ELEMENTS_LENGTH)), &reachMin, ¬ReachMin); { Bind(&reachMin); result = newL; diff --git a/ecmascript/compiler/stub_builder.h b/ecmascript/compiler/stub_builder.h index a909b492ca9d3d7ddb2f7f7a2846928b78b7300f..eb1b202f7dbaf4d735670f91a7f6d1bffe660e14 100644 --- a/ecmascript/compiler/stub_builder.h +++ b/ecmascript/compiler/stub_builder.h @@ -338,6 +338,7 @@ public: GateRef Int16ToTaggedInt(GateRef x); GateRef IntToTaggedPtr(GateRef x); GateRef IntToTaggedInt(GateRef x); + GateRef IntUnsignedToTaggedInt(GateRef x); GateRef Int64ToTaggedInt(GateRef x); GateRef Int64ToTaggedIntPtr(GateRef x); GateRef DoubleToTaggedDouble(GateRef x); diff --git a/ecmascript/stubs/runtime_stub_list.h b/ecmascript/stubs/runtime_stub_list.h index 993612e26ee989dd315873315e12a3d3ba2ff5cf..16646d68143048b8a6f790b58fa73be72cdcbd36 100644 --- a/ecmascript/stubs/runtime_stub_list.h +++ b/ecmascript/stubs/runtime_stub_list.h @@ -428,7 +428,9 @@ namespace panda::ecmascript { V(FunctionPrototypeBind) \ V(FunctionPrototypeCall) \ V(SuperCallForwardAllArgs) \ - V(OptSuperCallForwardAllArgs) + V(OptSuperCallForwardAllArgs) \ + V(ContinueFlatMapBeforeCall) \ + V(ContinueFlatMapAfterCall) // When ASM enters C++ via CallRuntime, if the C++ process requires GetGlobalEnv(), // the current globalenv in ASM must be set to glue before CallRuntime! diff --git a/ecmascript/stubs/runtime_stubs.cpp b/ecmascript/stubs/runtime_stubs.cpp index 4f213f59f51f88a4faf33212bbd8a087536a6d01..3ddffbca4855f642ab8830a6422442549c3f411c 100644 --- a/ecmascript/stubs/runtime_stubs.cpp +++ b/ecmascript/stubs/runtime_stubs.cpp @@ -1129,6 +1129,35 @@ DEF_RUNTIME_STUBS(OptSuperCallForwardAllArgs) return RuntimeSuperCallForwardAllArgs(thread, sp, superFunc, newTarget, restNumArgs, startIdx).GetRawData(); } +DEF_RUNTIME_STUBS(ContinueFlatMapBeforeCall) +{ + RUNTIME_STUBS_HEADER(ContinueFlatMapBeforeCall); + JSHandle srcValue = GetHArg(argv, argc, 0); // 0: the 0th parameter + JSHandle resValue(GetHArg(argv, argc, 1)); // 1: the 1st parameter + JSHandle mapFunc = GetHArg(argv, argc, 2); // 2: the 2nd parameter + JSHandle thisArg = GetHArg(argv, argc, 3); // 3: the 3th parameter + int64_t targetIdx = static_cast(JSTaggedNumber(GetArg(argv, argc, 4)).ToUint32()); // 4: the 4th para + int64_t curSrcValueIdx = static_cast(JSTaggedNumber(GetArg(argv, argc, 5)).ToUint32()); // 5: the 5th para + int64_t srcValueLen = static_cast(JSTaggedNumber(GetArg(argv, argc, 6)).ToUint32()); // 6: the 6th para + return ArrayHelper::FlatMapFromIndex(thread, srcValue, resValue, mapFunc, thisArg, + targetIdx, curSrcValueIdx, srcValueLen).GetRawData(); +} + +DEF_RUNTIME_STUBS(ContinueFlatMapAfterCall) +{ + RUNTIME_STUBS_HEADER(ContinueFlatMapBeforeCall); + JSHandle srcValue = GetHArg(argv, argc, 0); // 0: the 0th parameter + JSHandle resValue(GetHArg(argv, argc, 1)); // 1: the 1st parameter + JSHandle mapFunc = GetHArg(argv, argc, 2); // 2: the 2nd parameter + JSHandle thisArg = GetHArg(argv, argc, 3); // 3: the 3th parameter + int64_t targetIdx = static_cast(JSTaggedNumber(GetArg(argv, argc, 4)).ToUint32()); // 4: the 4th para + int64_t curSrcValueIdx = static_cast(JSTaggedNumber(GetArg(argv, argc, 5)).ToUint32()); // 5: the 5th para + int64_t srcValueLen = static_cast(JSTaggedNumber(GetArg(argv, argc, 6)).ToUint32()); // 6: the 6th para + JSHandle curItem = GetHArg(argv, argc, 7); // 7: the 7th parameter + return ArrayHelper::FlatMapFromIndexAfterCall(thread, srcValue, resValue, mapFunc, thisArg, curItem, + targetIdx, curSrcValueIdx, srcValueLen).GetRawData(); +} + DEF_RUNTIME_STUBS(GetCallSpreadArgs) { RUNTIME_STUBS_HEADER(GetCallSpreadArgs); diff --git a/test/moduletest/arrayflatmap/arrayflatmap.js b/test/moduletest/arrayflatmap/arrayflatmap.js index 372ee0891f421654f50ac25af9f4c7ccc9b2d59c..711f7d7a9d4c6979ec496e4eaec286d0954f110e 100644 --- a/test/moduletest/arrayflatmap/arrayflatmap.js +++ b/test/moduletest/arrayflatmap/arrayflatmap.js @@ -85,4 +85,608 @@ let res = arr.flatMap((x)=>{ print(res); print(res.length) +// stable array without proxy +{ + let arr = [[1, 2], [3, 4]] + let res = arr.flatMap(x => x); + print(res); +} + +{ + let arr = [[1, 2], [3, 4]] + let res = arr.flatMap(x => { + x.push(0) + return x + }); + print(res); +} + +{ + let arr = [[1, 2, 3], [4, 5, 6]] + let res = arr.flatMap(x => { + x.pop() + return x + }); + print(res); +} + +{ + let arr = [[1, 2], [3, 4], 5] + let res = arr.flatMap(x => { + arr.length = 10 + return x + }); + print(res); +} + +{ + let arr = [[1, 2], [3, 4], 5, 6, 7] + let res = arr.flatMap(x => { + arr.length = 3 + return x + }); + print(res); +} + +{ + let idx = 0; + let arr = [[1, 2], [3, 4], 5]; + let res = arr.flatMap(x => { + if(idx === 0) { + arr.pop(); + } + if(idx === 1) { + arr.push(15); + } + idx++; + return x; + }); + print(res); +} + +{ + let idx = 0; + let arr = [[1, 2], [3, 4], 7, 8] + let res = arr.flatMap(x => { + if(idx === 0) { + arr[1] = [3, 4, 5, 6] + } + return x + }); + print(res); +} + +{ + let idx = 0; + let arr = [[1, 2], [3, 4], 7, 8] + let res = arr.flatMap(x => { + if(idx === 0) { + arr[1] = { a: 1 } + } + return x + }); + print(JSON.stringify(res)); +} + +{ + let arr = [[1, 2], [3, 4]] + let res = arr.flatMap(x => { + return { arr: x } + }); + print(JSON.stringify(res)); +} + +{ + let arr = [[1, 2], [3, 4]] + let res = arr.flatMap(x => { + arr = { a: 1 } + return x + }); + print(res); +} + +// stable array with proxy +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}) ]; + let res = arr.flatMap(x=>x); + print(res); +} + +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}) ]; + let res = arr.flatMap(x => { + x.push(0) + return x + }); + print(res); +} + +{ + let arr = [ new Proxy([1, 2, 3], {}), new Proxy([4, 5, 6], {}) ]; + let res = arr.flatMap(x => { + x.pop() + return x + }); + print(res); +} + +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}), 5 ]; + let res = arr.flatMap(x => { + arr.length = 10 + return x + }); + print(res); +} + +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}), 5, 6, 7 ]; + let res = arr.flatMap(x => { + arr.length = 3 + return x + }); + print(res); +} + +{ + let idx = 0; + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}), 5]; + let res = arr.flatMap(x => { + if(idx === 0) { + arr.pop(); + } + if(idx === 1) { + arr.push(15); + } + idx ++; + return x; + }); + print(res); +} + +{ + let idx = 0; + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}), 7, 8] + let res = arr.flatMap(x => { + if(idx === 0) { + arr[1] = new Proxy([3, 4, 5, 6], {}) + } + return x + }); + print(res); +} + +{ + let idx = 0; + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}), 7, 8] + let res = arr.flatMap(x => { + if(idx === 0) { + arr[1] = new Proxy({ a: 1 }, {}) + } + return x + }); + print(JSON.stringify(res)); +} + +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}) ]; + let res = arr.flatMap(x => { + return { arr: x } + }); + print(JSON.stringify(res)); +} + +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}) ]; + let res = arr.flatMap(x => { + arr = { a: 1 } + return x + }); + print(res); +} + +{ + const handler = { + get(trapTarget, property, receiver) { + trapTarget.push(0) + return trapTarget[property]; + }, + } + let arr = [ new Proxy([1, 2], handler), new Proxy([3, 4], handler) ]; + let res = arr.flatMap(x=>x); + print(res); +} + +{ + const handler = { + get(trapTarget, property, receiver) { + trapTarget.pop() + return trapTarget[property]; + }, + } + let arr = [ new Proxy([1, 2, 3], handler), new Proxy([4, 5, 6], handler) ]; + let res = arr.flatMap(x=>x); + print(res); +} + +{ + const handler = { + get(trapTarget, property, receiver) { + trapTarget.push(0) + return trapTarget[property]; + }, + } + let arr = [ new Proxy([1, 2], handler), new Proxy([3, 4], handler) ]; + let res = arr.flatMap(x => { + x.push(0) + return x + }); + print(res); +} + +{ + const handler = { + get(trapTarget, property, receiver) { + trapTarget.pop() + return trapTarget[property]; + }, + } + let arr = [ new Proxy([1, 2, 3, 4, 5, 6], handler), new Proxy([7, 8, 9, 10, 11, 12], handler) ]; + let res = arr.flatMap(x => { + x.pop() + return x + }); + print(res); +} + +// unstable array without proxy +{ + let arr = [[1, 2], [3, 4]] + arr.__proto__ = []; + let res = arr.flatMap(x => x); + print(res); +} + +{ + let arr = [[1, 2], [3, 4]] + arr.__proto__ = []; + let res = arr.flatMap(x => { + x.push(0) + return x + }); + print(res); +} + +{ + let arr = [[1, 2, 3], [4, 5, 6]] + arr.__proto__ = []; + let res = arr.flatMap(x => { + x.pop() + return x + }); + print(res); +} + +{ + let arr = [[1, 2], [3, 4], 5] + arr.__proto__ = []; + let res = arr.flatMap(x => { + arr.length = 10 + return x + }); + print(res); +} +{ + let arr = [[1, 2], [3, 4], 5, 6, 7] + arr.__proto__ = []; + let res = arr.flatMap(x => { + arr.length = 3 + return x + }); + print(res); +} + +{ + let idx = 0; + let arr = [[1, 2], [3, 4], 5]; + arr.__proto__ = []; + let res = arr.flatMap(x => { + if(idx === 0) { + arr.pop(); + } + if(idx === 1) { + arr.push(15); + } + idx ++; + return x; + }); + print(res); +} + +{ + let idx = 0; + let arr = [[1, 2], [3, 4], 7, 8] + arr.__proto__ = []; + let res = arr.flatMap(x => { + if(idx === 0) { + arr[1] = [3, 4, 5, 6] + } + return x + }); + print(res); +} + +{ + let idx = 0; + let arr = [[1, 2], [3, 4], 7, 8] + arr.__proto__ = []; + let res = arr.flatMap(x => { + if(idx === 0) { + arr[1] = { a: 1 } + } + return x + }); + print(JSON.stringify(res)); +} + +{ + let arr = [[1, 2], [3, 4]] + arr.__proto__ = []; + let res = arr.flatMap(x => { + return { arr: x } + }); + print(JSON.stringify(res)); +} + +{ + let arr = [[1, 2], [3, 4]] + arr.__proto__ = []; + let res = arr.flatMap(x => { + arr = { a: 1 } + return x + }); + print(res); +} + +{ + let arr = [[1, 2], [3, 4]] + let res = arr.flatMap(x => { + arr.__proto__ = []; + return x + }); + print(res); +} + +{ + let arr = [[1, 2], [3, 4]] + let res = arr.flatMap(x => { + x.__proto__ = []; + return x + }); + print(res); +} + +{ + let idx = 0; + let arr = [[1, 2], [3, 4], [5, 6], [7, 8]] + let res = arr.flatMap(x => { + idx++ + if (idx === 2) { + arr.__proto__ = []; + } + return x + }); + print(res); // [1, 2, 3, 4, 5, 6, 7, 8] +} + +// unstable array with proxy +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}) ]; + arr.__proto__ = []; + let res = arr.flatMap(x=>x); + print(res); +} + +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}) ]; + arr.__proto__ = []; + let res = arr.flatMap(x => { + x.push(0) + return x + }); + print(res); +} + +{ + let arr = [ new Proxy([1, 2, 3], {}), new Proxy([4, 5, 6], {}) ]; + arr.__proto__ = []; + let res = arr.flatMap(x => { + x.pop() + return x + }); + print(res); +} + +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}), 5 ]; + arr.__proto__ = []; + let res = arr.flatMap(x => { + arr.length = 10 + return x + }); + print(res); +} + +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}), 5, 6, 7 ]; + arr.__proto__ = []; + let res = arr.flatMap(x => { + arr.length = 3 + return x + }); + print(res); +} + +{ + let idx = 0; + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}), 5]; + arr.__proto__ = []; + let res = arr.flatMap(x => { + if(idx === 0) { + arr.pop(); + } + if(idx === 1) { + arr.push(15); + } + idx ++; + return x; + }); + print(res); +} + +{ + let idx = 0; + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}), 7, 8] + arr.__proto__ = []; + let res = arr.flatMap(x => { + if(idx === 0) { + arr[1] = new Proxy([3, 4, 5, 6], {}) + } + return x + }); + print(res); +} + +{ + let idx = 0; + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}), 7, 8] + arr.__proto__ = []; + let res = arr.flatMap(x => { + if(idx === 0) { + arr[1] = new Proxy({ a: 1 }, {}) + } + return x + }); + print(JSON.stringify(res)); +} + +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}) ]; + arr.__proto__ = []; + let res = arr.flatMap(x => { + return { arr: x } + }); + print(JSON.stringify(res)); +} + +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}) ]; + arr.__proto__ = []; + let res = arr.flatMap(x => { + arr = { a: 1 } + return x + }); + print(res); +} + +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}) ]; + let res = arr.flatMap(x => { + arr.__proto__ = []; + return x + }); + print(res); +} + +{ + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}) ]; + let res = arr.flatMap(x => { + x.__proto__ = []; + return x + }); + print(res); +} + +{ + const handler = { + get(trapTarget, property, receiver) { + trapTarget.push(0) + return trapTarget[property]; + }, + } + let arr = [ new Proxy([1, 2], handler), new Proxy([3, 4], handler) ]; + arr.proto = []; + let res = arr.flatMap(x=>x); + print(res); +} + +{ + const handler = { + get(trapTarget, property, receiver) { + trapTarget.pop() + return trapTarget[property]; + }, + } + let arr = [ new Proxy([1, 2, 3], handler), new Proxy([4, 5, 6], handler) ]; + arr.proto = []; + let res = arr.flatMap(x=>x); + print(res); +} + +{ + const handler = { + get(trapTarget, property, receiver) { + trapTarget.push(0) + return trapTarget[property]; + }, + } + let arr = [ new Proxy([1, 2], handler), new Proxy([3, 4], handler) ]; + arr.proto = []; + let res = arr.flatMap(x => { + x.push(0) + return x + }); + print(res); +} + +{ + const handler = { + get(trapTarget, property, receiver) { + trapTarget.pop() + return trapTarget[property]; + }, + } + let arr = [ new Proxy([1, 2, 3, 4, 5, 6], handler), new Proxy([7, 8, 9, 10, 11, 12], handler) ]; + arr.proto = []; + let res = arr.flatMap(x => { + x.pop() + return x + }); + print(res); +} + +{ + const handler = { + get(trapTarget, property, receiver) { + trapTarget.proto = []; + return trapTarget[property]; + }, + } + let arr = [ new Proxy([1, 2], handler), new Proxy([3, 4], handler) ]; + let res = arr.flatMap(x=>x); + print(res); +} + +{ + let idx = 0; + let arr = [ new Proxy([1, 2], {}), new Proxy([3, 4], {}), new Proxy([5, 6], {}), new Proxy([7, 8], {}) ]; + let res = arr.flatMap(x => { + idx++ + if (idx === 2) { + arr.__proto__ = []; + } + return x + }); + print(res); +} \ No newline at end of file diff --git a/test/moduletest/arrayflatmap/expect_output.txt b/test/moduletest/arrayflatmap/expect_output.txt index d48a4bf50308d0c508466bd1eaba259aca821fa0..78f079b1c2dabb9ede62374216e9b3bb1974ac96 100644 --- a/test/moduletest/arrayflatmap/expect_output.txt +++ b/test/moduletest/arrayflatmap/expect_output.txt @@ -20,3 +20,58 @@ true 1,1,1,3,9,4,16,5,25 1,2,3,4,5,6,7,8 8 +1,2,3,4 +1,2,0,3,4,0 +1,2,4,5 +1,2,3,4,5 +1,2,3,4,5 +1,2,3,4,15 +1,2,3,4,5,6,7,8 +[1,2,{"a":1},7,8] +[{"arr":[1,2]},{"arr":[3,4]}] +1,2,3,4 +1,2,3,4 +1,2,0,3,4,0 +1,2,4,5 +1,2,3,4,5 +1,2,3,4,5 +1,2,3,4,15 +1,2,3,4,5,6,7,8 +[1,2,{"a":1},7,8] +[{"arr":[1,2]},{"arr":[3,4]}] +1,2,3,4 +1,2,0,3,4,0 +1,4 +1,2,0,0,0,0,3,4,0,0,0,0 +1,7 +1,2,3,4 +1,2,0,3,4,0 +1,2,4,5 +1,2,3,4,5 +1,2,3,4,5 +1,2,3,4,15 +1,2,3,4,5,6,7,8 +[1,2,{"a":1},7,8] +[{"arr":[1,2]},{"arr":[3,4]}] +1,2,3,4 +1,2,3,4 +1,2,3,4 +1,2,3,4,5,6,7,8 +1,2,3,4 +1,2,0,3,4,0 +1,2,4,5 +1,2,3,4,5 +1,2,3,4,5 +1,2,3,4,15 +1,2,3,4,5,6,7,8 +[1,2,{"a":1},7,8] +[{"arr":[1,2]},{"arr":[3,4]}] +1,2,3,4 +1,2,3,4 +1,2,3,4 +1,2,0,3,4,0 +1,4 +1,2,0,0,0,0,3,4,0,0,0,0 +1,7 +1,2,3,4 +1,2,3,4,5,6,7,8 \ No newline at end of file