* Copyright (c) 2025 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/compiler/builtins/containers_stub_builder.h"
#include "ecmascript/compiler/call_stub_builder.h"
namespace panda::ecmascript::kungfu {
void ContainersCommonStubBuilder::ContainersCommonFuncCall(GateRef glue, GateRef thisValue,
GateRef numArgs, Variable* result, Label *exit, Label *slowPath, ContainersType type)
{
auto env = GetEnvironment();
DEFVARIABLE(thisObj, VariableType::JS_ANY(), thisValue);
DEFVARIABLE(thisArg, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(key, VariableType::INT64(), Int64(0));
DEFVARIABLE(kValue, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(length, VariableType::INT32(), Int32(0));
DEFVARIABLE(k, VariableType::INT32(), Int32(0));
Label valueIsJSAPIVector(env);
Label valueNotJSAPIVector(env);
Label objIsJSProxy(env);
Label objNotJSProxy(env);
Label objIsJSAPIVector(env);
Label thisArgUndefined(env);
Label thisArgNotUndefined(env);
Label callbackUndefined(env);
Label callbackNotUndefined(env);
Label nextCount(env);
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label afterLoop(env);
Label thisValueIsHeapObj(env);
GateRef callbackFnHandle;
BRANCH(TaggedIsHeapObject(thisValue), &thisValueIsHeapObj, slowPath);
Bind(&thisValueIsHeapObj);
BRANCH(IsContainer(glue, *thisObj, type), &valueIsJSAPIVector, &valueNotJSAPIVector);
Bind(&valueNotJSAPIVector);
{
BRANCH(IsJsProxy(glue, *thisObj), &objIsJSProxy, &objNotJSProxy);
Bind(&objIsJSProxy);
{
GateRef tempObj = GetTarget(glue, *thisObj);
BRANCH(IsContainer(glue, tempObj, type), &objIsJSAPIVector, slowPath);
Bind(&objIsJSAPIVector);
{
thisObj = tempObj;
Jump(&valueIsJSAPIVector);
}
}
Bind(&objNotJSProxy);
Jump(slowPath);
}
Bind(&valueIsJSAPIVector);
{
BRANCH(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &callbackUndefined, &callbackNotUndefined);
Bind(&callbackUndefined);
Jump(slowPath);
Bind(&callbackNotUndefined);
{
Label isCall(env);
Label notCall(env);
Label isHeapObj(env);
callbackFnHandle = GetCallArg0(numArgs);
BRANCH(TaggedIsHeapObject(callbackFnHandle), &isHeapObj, slowPath);
Bind(&isHeapObj);
BRANCH(IsCallable(glue, callbackFnHandle), &isCall, ¬Call);
Bind(¬Call);
Jump(slowPath);
Bind(&isCall);
{
BRANCH(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &thisArgUndefined, &thisArgNotUndefined);
Bind(&thisArgUndefined);
Jump(&nextCount);
Bind(&thisArgNotUndefined);
thisArg = GetCallArg1(numArgs);
Jump(&nextCount);
}
}
}
Bind(&nextCount);
{
length = ContainerGetSize(glue, *thisObj, type);
Jump(&loopHead);
LoopBegin(&loopHead);
{
Label lenChange(env);
Label hasException(env);
Label notHasException(env);
Label setValue(env);
BRANCH(Int32LessThan(*k, *length), &next, &afterLoop);
Bind(&next);
{
kValue = ContainerGetValue(glue, *thisObj, *k, type);
if (IsPlainArray(type)) {
key = PlainArrayGetKey(glue, *thisObj, *k);
} else {
key = IntToTaggedInt(*k);
}
JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN);
callArgs.callThisArg3WithReturnArgs = { *thisArg, *kValue, *key, *thisObj };
CallStubBuilder callBuilder(this, glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, nullptr,
Circuit::NullGate(), callArgs);
GateRef retValue = callBuilder.JSCallDispatch();
BRANCH(HasPendingException(glue), &hasException, ¬HasException);
Bind(&hasException);
{
result->WriteVariable(retValue);
Jump(exit);
}
Bind(¬HasException);
GateRef tempLen = ContainerGetSize(glue, *thisObj, type);
BRANCH(Int32NotEqual(tempLen, *length), &lenChange, &setValue);
Bind(&lenChange);
if (!IsArrayListReplaceAllelements(type)) {
length = tempLen;
}
BRANCH(Int32GreaterThanOrEqual(*k, *length), &afterLoop, &setValue);
Bind(&setValue);
if (IsReplaceAllElements(type)) {
GateRef elementsOffset = IntPtr(JSObject::ELEMENTS_OFFSET);
GateRef elements = Load(VariableType::JS_POINTER(), glue, *thisObj, elementsOffset);
SetValueToTaggedArray(VariableType::JS_ANY(), glue, elements, *k, retValue);
}
Jump(&loopEnd);
}
}
Bind(&loopEnd);
k = Int32Add(*k, Int32(1));
LoopEndWithCheckSafePoint(&loopHead, env, glue);
}
Bind(&afterLoop);
Jump(exit);
}
void ContainersCommonStubBuilder::ContainersLightWeightCall(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable* result, Label *exit, Label *slowPath,
ContainersType type)
{
auto env = GetEnvironment();
DEFVARIABLE(thisObj, VariableType::JS_ANY(), thisValue);
DEFVARIABLE(thisArg, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(key, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(value, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(length, VariableType::INT32(), Int32(0));
DEFVARIABLE(index, VariableType::INT32(), Int32(0));
Label valueIsJSAPILightWeight(env);
Label valueNotJSAPILightWeight(env);
Label objIsJSProxy(env);
Label objNotJSProxy(env);
Label objIsJSAPILightWeight(env);
Label thisArgUndefined(env);
Label thisArgNotUndefined(env);
Label callbackUndefined(env);
Label callbackNotUndefined(env);
Label nextCount(env);
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label afterLoop(env);
Label thisValueIsHeapObj(env);
GateRef callbackFnHandle;
BRANCH(TaggedIsHeapObject(thisValue), &thisValueIsHeapObj, slowPath);
Bind(&thisValueIsHeapObj);
BRANCH(IsContainer(glue, *thisObj, type), &valueIsJSAPILightWeight, &valueNotJSAPILightWeight);
Bind(&valueNotJSAPILightWeight);
{
BRANCH(IsJsProxy(glue, *thisObj), &objIsJSProxy, &objNotJSProxy);
Bind(&objIsJSProxy);
{
GateRef tempObj = GetTarget(glue, *thisObj);
BRANCH(IsContainer(glue, tempObj, type), &objIsJSAPILightWeight, slowPath);
Bind(&objIsJSAPILightWeight);
{
thisObj = tempObj;
Jump(&valueIsJSAPILightWeight);
}
}
Bind(&objNotJSProxy);
Jump(slowPath);
}
Bind(&valueIsJSAPILightWeight);
{
BRANCH(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &callbackUndefined, &callbackNotUndefined);
Bind(&callbackUndefined);
Jump(slowPath);
Bind(&callbackNotUndefined);
{
Label isCall(env);
Label notCall(env);
Label isHeapObj(env);
callbackFnHandle = GetCallArg0(numArgs);
BRANCH(TaggedIsHeapObject(callbackFnHandle), &isHeapObj, slowPath);
Bind(&isHeapObj);
BRANCH(IsCallable(glue, callbackFnHandle), &isCall, ¬Call);
Bind(¬Call);
Jump(slowPath);
Bind(&isCall);
{
BRANCH(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &thisArgUndefined, &thisArgNotUndefined);
Bind(&thisArgUndefined);
Jump(&nextCount);
Bind(&thisArgNotUndefined);
thisArg = GetCallArg1(numArgs);
Jump(&nextCount);
}
}
}
Bind(&nextCount);
{
length = ContainerGetSize(glue, *thisObj, type);
Jump(&loopHead);
LoopBegin(&loopHead);
{
Label lenChange(env);
Label hasException(env);
Label notHasException(env);
BRANCH(Int32LessThan(*index, *length), &next, &afterLoop);
Bind(&next);
{
value = ContainerGetValue(glue, *thisObj, *index, type);
key = ContainerGetKey(glue, *thisObj, *index, type);
JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN);
callArgs.callThisArg3WithReturnArgs = { *thisArg, *value, *key, *thisObj };
CallStubBuilder callBuilder(this, glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, nullptr,
Circuit::NullGate(), callArgs);
GateRef retValue = callBuilder.JSCallDispatch();
BRANCH(HasPendingException(glue), &hasException, ¬HasException);
Bind(&hasException);
{
result->WriteVariable(retValue);
Jump(exit);
}
Bind(¬HasException);
GateRef currentLen = ContainerGetSize(glue, *thisObj, type);
BRANCH(Int32NotEqual(currentLen, *length), &lenChange, &loopEnd);
Bind(&lenChange);
length = currentLen;
Jump(&loopEnd);
}
}
Bind(&loopEnd);
index = Int32Add(*index, Int32(1));
LoopEndWithCheckSafePoint(&loopHead, env, glue);
}
Bind(&afterLoop);
Jump(exit);
}
void ContainersCommonStubBuilder::ContainersHashCall(GateRef glue, GateRef thisValue, GateRef numArgs, Variable* result,
Label *exit, Label *slowPath, ContainersType type)
{
auto env = GetEnvironment();
DEFVARIABLE(thisObj, VariableType::JS_ANY(), thisValue);
DEFVARIABLE(thisArg, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(node, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(key, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(value, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(length, VariableType::INT32(), Int32(0));
DEFVARIABLE(index, VariableType::INT32(), Int32(0));
Label valueIsJSAPIHash(env);
Label valueNotJSAPIHash(env);
Label objIsJSProxy(env);
Label objNotJSProxy(env);
Label objIsJSAPIHash(env);
Label thisArgUndefined(env);
Label thisArgNotUndefined(env);
Label callbackUndefined(env);
Label callbackNotUndefined(env);
Label nextCount(env);
Label nodeNotHole(env);
Label nodeIsLinked(env);
Label nodeIsRBTree(env);
Label loopLinked(env);
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label afterLoop(env);
Label thisValueIsHeapObj(env);
GateRef callbackFnHandle;
BRANCH(TaggedIsHeapObject(thisValue), &thisValueIsHeapObj, slowPath);
Bind(&thisValueIsHeapObj);
BRANCH(IsContainer(glue, *thisObj, type), &valueIsJSAPIHash, &valueNotJSAPIHash);
Bind(&valueNotJSAPIHash);
{
BRANCH(IsJsProxy(glue, *thisObj), &objIsJSProxy, &objNotJSProxy);
Bind(&objIsJSProxy);
{
GateRef tempObj = GetTarget(glue, *thisObj);
BRANCH(IsContainer(glue, tempObj, type), &objIsJSAPIHash, slowPath);
Bind(&objIsJSAPIHash);
{
thisObj = tempObj;
Jump(&valueIsJSAPIHash);
}
}
Bind(&objNotJSProxy);
Jump(slowPath);
}
Bind(&valueIsJSAPIHash);
{
BRANCH(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &callbackUndefined, &callbackNotUndefined);
Bind(&callbackUndefined);
Jump(slowPath);
Bind(&callbackNotUndefined);
{
Label isCall(env);
Label notCall(env);
Label isHeapObj(env);
callbackFnHandle = GetCallArg0(numArgs);
BRANCH(TaggedIsHeapObject(callbackFnHandle), &isHeapObj, ¬Call);
Bind(&isHeapObj);
BRANCH(IsCallable(glue, callbackFnHandle), &isCall, ¬Call);
Bind(¬Call);
Jump(slowPath);
Bind(&isCall);
{
BRANCH(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &thisArgUndefined, &thisArgNotUndefined);
Bind(&thisArgUndefined);
Jump(&nextCount);
Bind(&thisArgNotUndefined);
thisArg = GetCallArg1(numArgs);
Jump(&nextCount);
}
}
}
Bind(&nextCount);
{
length = ContainerGetSize(glue, *thisObj, type);
Jump(&loopHead);
LoopBegin(&loopHead);
{
Label hasExceptionLinked(env);
Label notHasExceptionLinked(env);
Label hasExceptionRBTree(env);
Label notHasExceptionRBTree(env);
BRANCH(Int32LessThan(*index, *length), &next, &afterLoop);
Bind(&next);
{
node = ContainerGetNode(glue, *thisObj, *index, type);
BRANCH(TaggedIsHole(*node), &loopEnd, &nodeNotHole);
Bind(&nodeNotHole);
BRANCH(IsLinkedNode(glue, *node), &nodeIsLinked, &nodeIsRBTree);
LoopBegin(&nodeIsLinked);
{
value = Load(VariableType::JS_POINTER(), glue, *node, IntPtr(
type == ContainersType::HASHSET_FOREACH ? LinkedNode::KEY_OFFSET : LinkedNode::VALUE_OFFSET));
key = Load(VariableType::JS_POINTER(), glue, *node, IntPtr(LinkedNode::KEY_OFFSET));
JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN);
callArgs.callThisArg3WithReturnArgs = { *thisArg, *value, *key, *thisObj };
CallStubBuilder callBuilder(this, glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0,
nullptr, Circuit::NullGate(), callArgs);
GateRef retValue = callBuilder.JSCallDispatch();
BRANCH(HasPendingException(glue), &hasExceptionLinked, ¬HasExceptionLinked);
Bind(&hasExceptionLinked);
{
result->WriteVariable(retValue);
Jump(exit);
}
Bind(¬HasExceptionLinked);
node = Load(VariableType::JS_POINTER(), glue, *node, IntPtr(LinkedNode::NEXT_OFFSET));
BRANCH(TaggedIsHole(*node), &loopEnd, &loopLinked);
}
Bind(&loopLinked);
LoopEnd(&nodeIsLinked);
Bind(&nodeIsRBTree);
{
GateRef retValue = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(),
RTSTUB_ID(ContainerRBTreeForEach), { *node, callbackFnHandle, *thisArg, *thisObj,
IntToTaggedInt(Int32(static_cast<int32_t>(type))) });
BRANCH(HasPendingException(glue), &hasExceptionRBTree, ¬HasExceptionRBTree);
Bind(&hasExceptionRBTree);
{
result->WriteVariable(retValue);
Jump(exit);
}
Bind(¬HasExceptionRBTree);
Jump(&loopEnd);
}
}
}
Bind(&loopEnd);
index = Int32Add(*index, Int32(1));
LoopEndWithCheckSafePoint(&loopHead, env, glue);
}
Bind(&afterLoop);
Jump(exit);
}
void ContainersCommonStubBuilder::ContainersLinkedListCall(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable* result, Label *exit, Label *slowPath,
ContainersType type)
{
auto env = GetEnvironment();
DEFVARIABLE(thisObj, VariableType::JS_ANY(), thisValue);
DEFVARIABLE(thisArg, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(valueNode, VariableType::INT32(), Int32(0));
DEFVARIABLE(key, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(value, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(length, VariableType::INT32(), Int32(0));
DEFVARIABLE(index, VariableType::INT32(), Int32(0));
Label valueIsJSAPILinkedList(env);
Label valueNotJSAPILinkedList(env);
Label objIsJSProxy(env);
Label objNotJSProxy(env);
Label objIsJSAPILinkedList(env);
Label thisArgUndefined(env);
Label thisArgNotUndefined(env);
Label callbackUndefined(env);
Label callbackNotUndefined(env);
Label nextCount(env);
Label valueNotHole(env);
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label afterLoop(env);
Label thisValueIsHeapObj(env);
GateRef callbackFnHandle;
BRANCH(TaggedIsHeapObject(thisValue), &thisValueIsHeapObj, slowPath);
Bind(&thisValueIsHeapObj);
BRANCH(IsContainer(glue, *thisObj, type), &valueIsJSAPILinkedList, &valueNotJSAPILinkedList);
Bind(&valueNotJSAPILinkedList);
{
BRANCH(IsJsProxy(glue, *thisObj), &objIsJSProxy, &objNotJSProxy);
Bind(&objIsJSProxy);
{
GateRef tempObj = GetTarget(glue, *thisObj);
BRANCH(IsContainer(glue, tempObj, type), &objIsJSAPILinkedList, slowPath);
Bind(&objIsJSAPILinkedList);
{
thisObj = tempObj;
Jump(&valueIsJSAPILinkedList);
}
}
Bind(&objNotJSProxy);
Jump(slowPath);
}
Bind(&valueIsJSAPILinkedList);
{
BRANCH(Int64GreaterThanOrEqual(IntPtr(0), numArgs), &callbackUndefined, &callbackNotUndefined);
Bind(&callbackUndefined);
Jump(slowPath);
Bind(&callbackNotUndefined);
{
Label isCall(env);
Label notCall(env);
Label isHeapObj(env);
callbackFnHandle = GetCallArg0(numArgs);
BRANCH(TaggedIsHeapObject(callbackFnHandle), &isHeapObj, ¬Call);
Bind(&isHeapObj);
BRANCH(IsCallable(glue, callbackFnHandle), &isCall, ¬Call);
Bind(¬Call);
Jump(slowPath);
Bind(&isCall);
{
BRANCH(Int64GreaterThanOrEqual(IntPtr(1), numArgs), &thisArgUndefined, &thisArgNotUndefined);
Bind(&thisArgUndefined);
Jump(&nextCount);
Bind(&thisArgNotUndefined);
thisArg = GetCallArg1(numArgs);
Jump(&nextCount);
}
}
}
Bind(&nextCount);
{
length = ContainerGetSize(glue, *thisObj, type);
valueNode = Int32(TaggedList<TaggedArray>::ELEMENTS_START_INDEX);
Jump(&loopHead);
LoopBegin(&loopHead);
{
Label hasException(env);
Label notHasException(env);
BRANCH(Int32LessThan(*index, *length), &next, &afterLoop);
Bind(&next);
{
valueNode = TaggedGetInt(ContainerGetNode(glue, *thisObj,
Int32Add(*valueNode, Int32(TaggedList<TaggedArray>::NEXT_PTR_OFFSET)), type));
value = ContainerGetNode(glue, *thisObj, *valueNode, type);
BRANCH(TaggedIsHole(*value), &loopEnd, &valueNotHole);
Bind(&valueNotHole);
key = IntToTaggedInt(*index);
JSCallArgs callArgs(JSCallMode::CALL_THIS_ARG3_WITH_RETURN);
callArgs.callThisArg3WithReturnArgs = { *thisArg, *value, *key, *thisObj };
CallStubBuilder callBuilder(this, glue, callbackFnHandle, Int32(NUM_MANDATORY_JSFUNC_ARGS), 0, nullptr,
Circuit::NullGate(), callArgs);
GateRef retValue = callBuilder.JSCallDispatch();
BRANCH(HasPendingException(glue), &hasException, ¬HasException);
Bind(&hasException);
{
result->WriteVariable(retValue);
Jump(exit);
}
Bind(¬HasException);
Jump(&loopEnd);
}
}
Bind(&loopEnd);
index = Int32Add(*index, Int32(1));
LoopEndWithCheckSafePoint(&loopHead, env, glue);
}
Bind(&afterLoop);
Jump(exit);
}
}