* Copyright (c) 2023 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/base/config.h"
#include "ecmascript/compiler/builtins/builtins_array_stub_builder.h"
#include "ecmascript/builtins/builtins_string.h"
#include "ecmascript/compiler/access_object_stub_builder.h"
#include "ecmascript/compiler/builtins/builtins_typedarray_stub_builder.h"
#include "ecmascript/compiler/call_stub_builder.h"
#include "ecmascript/compiler/new_object_stub_builder.h"
#include "ecmascript/js_array_iterator.h"
#include "ecmascript/js_iterator.h"
#include "ecmascript/platform/dfx_hisys_event.h"
namespace panda::ecmascript::kungfu {
void BuiltinsArrayStubBuilder::ElementsKindHclassCompare(GateRef glue, GateRef arrayCls,
Label *matchCls, Label *slowPath)
{
auto env = GetEnvironment();
Label isGeneric(env);
GateRef elementsKind = GetElementsKindFromHClass(arrayCls);
GateRef notGeneric = NotEqual(elementsKind, Int32(Elements::ToUint(ElementsKind::GENERIC)));
BRANCH(notGeneric, matchCls, &isGeneric);
Bind(&isGeneric);
{
GateRef globalEnv = GetCurrentGlobalEnv();
GateRef initialHClass = GetGlobalEnvValue(VariableType::JS_ANY(), glue, globalEnv,
static_cast<size_t>(GlobalEnvField::ELEMENT_HOLE_TAGGED_HCLASS_INDEX));
BRANCH(Equal(initialHClass, arrayCls), matchCls, slowPath);
}
}
void BuiltinsArrayStubBuilder::With(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
WithOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::Unshift(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
UnshiftOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::Shift(GateRef glue, GateRef thisValue,
[[maybe_unused]] GateRef numArgs, Variable *result, Label *exit, Label *slowPath)
{
ShiftOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::Concat(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
ConcatOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::Filter(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label isHeapObject(env);
Label isJsArray(env);
Label isprototypeJsArray(env);
Label defaultConstr(env);
BRANCH(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath);
Bind(&isHeapObject);
BRANCH(IsJsArray(glue, thisValue), &isJsArray, slowPath);
Bind(&isJsArray);
GateRef prototype = StubBuilder::GetPrototype(glue, thisValue);
GateRef protoIsJSArray =
LogicAndBuilder(env).And(TaggedIsHeapObject(prototype)).And(IsJsArray(glue, prototype)).Done();
BRANCH(protoIsJSArray, &isprototypeJsArray, slowPath);
Bind(&isprototypeJsArray);
BRANCH(HasConstructor(glue, thisValue), slowPath, &defaultConstr);
Bind(&defaultConstr);
GateRef callbackFnHandle = GetCallArg0(numArgs);
Label argOHeapObject(env);
Label callable(env);
Label notOverFlow(env);
BRANCH(TaggedIsHeapObject(callbackFnHandle), &argOHeapObject, slowPath);
Bind(&argOHeapObject);
BRANCH(IsCallable(glue, callbackFnHandle), &callable, slowPath);
Bind(&callable);
GateRef len = ZExtInt32ToInt64(GetArrayLength(thisValue));
Label isEmptyArray(env);
Label notEmptyArray(env);
BRANCH(Int64Equal(len, Int64(0)), &isEmptyArray, ¬EmptyArray);
Bind(&isEmptyArray);
{
NewObjectStubBuilder newBuilder(this, GetCurrentGlobalEnv());
result->WriteVariable(newBuilder.CreateEmptyArray(glue));
Jump(exit);
}
Bind(¬EmptyArray);
BRANCH(Int64GreaterThan(len, Int64(JSObject::MAX_GAP)), slowPath, ¬OverFlow);
Bind(¬OverFlow);
GateRef argHandle = GetCallArg1(numArgs);
GateRef kind = GetElementsKindFromHClass(LoadHClass(glue, thisValue));
GateRef newHClass = GetElementsKindHClass(glue, kind);
GateRef newArray = NewArrayWithHClass(glue, newHClass, TruncInt64ToInt32(len));
GateRef newArrayEles = GetElementsArray(glue, newArray);
Label stableJSArray(env);
Label notStableJSArray(env);
DEFVARIABLE(i, VariableType::INT64(), Int64(0));
DEFVARIABLE(toIndex, VariableType::INT64(), Int64(0));
BRANCH(IsStableJSArray(glue, thisValue), &stableJSArray, slowPath);
Bind(&stableJSArray);
{
DEFVARIABLE(thisArrLenVar, VariableType::INT64(), len);
DEFVARIABLE(kValue, VariableType::JS_ANY(), Hole());
Label enabledMutantArray(env);
Label disabledMutantArray(env);
BRANCH(IsEnableMutantArray(glue), &enabledMutantArray, &disabledMutantArray);
Bind(&enabledMutantArray);
{
Label kValueIsHole(env);
Label kValueNotHole(env);
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label loopExit(env);
Jump(&loopHead);
LoopBegin(&loopHead);
{
BRANCH(Int64LessThan(*i, *thisArrLenVar), &next, &loopExit);
Bind(&next);
kValue = GetTaggedValueWithElementsKind(glue, thisValue, *i);
BRANCH(TaggedIsHole(*kValue), &kValueIsHole, &kValueNotHole);
Bind(&kValueNotHole);
{
GateRef key = Int64ToTaggedInt(*i);
Label checkArray(env);
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();
Label find(env);
Label hasException1(env);
Label notHasException1(env);
BRANCH(HasPendingException(glue), &hasException1, ¬HasException1);
Bind(&hasException1);
{
result->WriteVariable(Exception());
Jump(exit);
}
Bind(¬HasException1);
BRANCH(TaggedIsTrue(FastToBoolean(glue, retValue)), &find, &checkArray);
Bind(&find);
{
SetValueWithElementsKind(glue, newArray, *kValue, *toIndex, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
toIndex = Int64Add(*toIndex, Int64(1));
Jump(&checkArray);
}
Bind(&checkArray);
{
Label lenChange(env);
GateRef tmpArrLen = ZExtInt32ToInt64(GetArrayLength(thisValue));
BRANCH(Int64LessThan(tmpArrLen, *thisArrLenVar), &lenChange, &kValueIsHole);
Bind(&lenChange);
{
thisArrLenVar = tmpArrLen;
Jump(&kValueIsHole);
}
}
}
Bind(&kValueIsHole);
i = Int64Add(*i, Int64(1));
BRANCH(IsStableJSArray(glue, thisValue), &loopEnd, ¬StableJSArray);
}
Bind(&loopEnd);
LoopEnd(&loopHead);
Bind(&loopExit);
Jump(¬StableJSArray);
}
Bind(&disabledMutantArray);
{
Label kValueIsHole(env);
Label kValueNotHole(env);
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label loopExit(env);
Jump(&loopHead);
LoopBegin(&loopHead);
{
BRANCH(Int64LessThan(*i, *thisArrLenVar), &next, &loopExit);
Bind(&next);
GateRef elements = GetElementsArray(glue, thisValue);
kValue = GetValueFromTaggedArray(glue, elements, *i);
BRANCH(TaggedIsHole(*kValue), &kValueIsHole, &kValueNotHole);
Bind(&kValueNotHole);
{
GateRef key = Int64ToTaggedInt(*i);
Label checkArray(env);
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();
Label find(env);
Label hasException1(env);
Label notHasException1(env);
BRANCH(HasPendingException(glue), &hasException1, ¬HasException1);
Bind(&hasException1);
{
result->WriteVariable(Exception());
Jump(exit);
}
Bind(¬HasException1);
BRANCH(TaggedIsTrue(FastToBoolean(glue, retValue)), &find, &checkArray);
Bind(&find);
{
SetValueToTaggedArray(VariableType::JS_ANY(), glue, newArrayEles, *toIndex, *kValue);
toIndex = Int64Add(*toIndex, Int64(1));
Jump(&checkArray);
}
Bind(&checkArray);
{
Label lenChange(env);
GateRef tmpArrLen = ZExtInt32ToInt64(GetArrayLength(thisValue));
BRANCH(Int64LessThan(tmpArrLen, *thisArrLenVar), &lenChange, &kValueIsHole);
Bind(&lenChange);
{
thisArrLenVar = tmpArrLen;
Jump(&kValueIsHole);
}
}
}
Bind(&kValueIsHole);
i = Int64Add(*i, Int64(1));
BRANCH(IsStableJSArray(glue, thisValue), &loopEnd, ¬StableJSArray);
}
Bind(&loopEnd);
LoopEnd(&loopHead);
Bind(&loopExit);
Jump(¬StableJSArray);
}
}
Bind(¬StableJSArray);
{
Label finish(env);
Label callRT(env);
BRANCH(Int64LessThan(*i, len), &callRT, &finish);
Bind(&callRT);
{
CallNGCRuntime(glue, RTSTUB_ID(ArrayTrim), {glue, newArrayEles, *toIndex});
Store(VariableType::INT32(), glue, newArray, IntPtr(JSArray::LENGTH_OFFSET), TruncInt64ToInt32(*toIndex));
GateRef ret = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(), RTSTUB_ID(JSArrayFilterUnStable),
{ argHandle, thisValue, IntToTaggedInt(*i), IntToTaggedInt(len),
IntToTaggedInt(*toIndex), newArray, callbackFnHandle });
result->WriteVariable(ret);
Jump(exit);
}
Bind(&finish);
{
result->WriteVariable(newArray);
Label needTrim(env);
BRANCH(Int64LessThan(*toIndex, len), &needTrim, exit);
Bind(&needTrim);
CallNGCRuntime(glue, RTSTUB_ID(ArrayTrim), {glue, newArrayEles, *toIndex});
Store(VariableType::INT32(), glue, newArray, IntPtr(JSArray::LENGTH_OFFSET), TruncInt64ToInt32(*toIndex));
Jump(exit);
}
}
}
void BuiltinsArrayStubBuilder::Map(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label isHeapObject(env);
Label isJsArray(env);
Label isprototypeJsArray(env);
Label defaultConstr(env);
BRANCH(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath);
Bind(&isHeapObject);
BRANCH(IsJsArray(glue, thisValue), &isJsArray, slowPath);
Bind(&isJsArray);
GateRef prototype = StubBuilder::GetPrototype(glue, thisValue);
GateRef protoIsJSArray =
LogicAndBuilder(env).And(TaggedIsHeapObject(prototype)).And(IsJsArray(glue, prototype)).Done();
BRANCH(protoIsJSArray, &isprototypeJsArray, slowPath);
Bind(&isprototypeJsArray);
BRANCH(HasConstructor(glue, thisValue), slowPath, &defaultConstr);
Bind(&defaultConstr);
GateRef callbackFnHandle = GetCallArg0(numArgs);
Label argOHeapObject(env);
Label callable(env);
Label notOverFlow(env);
BRANCH(TaggedIsHeapObject(callbackFnHandle), &argOHeapObject, slowPath);
Bind(&argOHeapObject);
BRANCH(IsCallable(glue, callbackFnHandle), &callable, slowPath);
Bind(&callable);
GateRef len = ZExtInt32ToInt64(GetArrayLength(thisValue));
Label isEmptyArray(env);
Label notEmptyArray(env);
BRANCH(Int64Equal(len, Int64(0)), &isEmptyArray, ¬EmptyArray);
Bind(&isEmptyArray);
{
NewObjectStubBuilder newBuilder(this, GetCurrentGlobalEnv());
result->WriteVariable(newBuilder.CreateEmptyArray(glue));
Jump(exit);
}
Bind(¬EmptyArray);
BRANCH(Int64GreaterThan(len, Int64(JSObject::MAX_GAP)), slowPath, ¬OverFlow);
Bind(¬OverFlow);
GateRef argHandle = GetCallArg1(numArgs);
GateRef newArray = NewArray(glue, len);
Label stableJSArray(env);
Label notStableJSArray(env);
DEFVARIABLE(i, VariableType::INT64(), Int64(0));
BRANCH(IsStableJSArray(glue, thisValue), &stableJSArray, slowPath);
Bind(&stableJSArray);
{
DEFVARIABLE(thisArrLenVar, VariableType::INT64(), len);
DEFVARIABLE(kValue, VariableType::JS_ANY(), Hole());
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label loopExit(env);
Jump(&loopHead);
LoopBegin(&loopHead);
{
BRANCH(Int64LessThan(*i, *thisArrLenVar), &next, &loopExit);
Bind(&next);
kValue = GetTaggedValueWithElementsKind(glue, thisValue, *i);
Label kValueIsHole(env);
Label kValueNotHole(env);
BRANCH(TaggedIsHole(*kValue), &kValueIsHole, &kValueNotHole);
Bind(&kValueNotHole);
{
GateRef key = Int64ToTaggedInt(*i);
Label checkArray(env);
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();
Label hasException1(env);
Label notHasException1(env);
BRANCH(HasPendingException(glue), &hasException1, ¬HasException1);
Bind(&hasException1);
{
result->WriteVariable(Exception());
Jump(exit);
}
Bind(¬HasException1);
SetValueWithElementsKind(glue, newArray, retValue, *i, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Label lenChange(env);
GateRef tmpArrLen = ZExtInt32ToInt64(GetArrayLength(thisValue));
BRANCH(Int64LessThan(tmpArrLen, *thisArrLenVar), &lenChange, &kValueIsHole);
Bind(&lenChange);
{
thisArrLenVar = tmpArrLen;
Jump(&kValueIsHole);
}
}
Bind(&kValueIsHole);
i = Int64Add(*i, Int64(1));
BRANCH(IsStableJSArray(glue, thisValue), &loopEnd, ¬StableJSArray);
}
Bind(&loopEnd);
LoopEnd(&loopHead);
Bind(&loopExit);
Jump(¬StableJSArray);
}
Bind(¬StableJSArray);
{
GateRef ret = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(), RTSTUB_ID(JSArrayMapUnStable),
{ argHandle, thisValue, IntToTaggedInt(*i), IntToTaggedInt(len), newArray, callbackFnHandle });
result->WriteVariable(ret);
Jump(exit);
}
}
void BuiltinsArrayStubBuilder::ForEach([[maybe_unused]] GateRef glue, GateRef thisValue, GateRef numArgs,
[[maybe_unused]] Variable *result, Label *exit, Label *slowPath)
{
ForEachOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::ArrayIteratorNext(GateRef glue, GateRef thisValue,
[[maybe_unused]] GateRef numArgs, Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
DEFVARIABLE(res, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(iterValue, VariableType::JS_ANY(), Undefined());
Label thisIsArrayIterator(env);
Label arrayNotUndefined(env);
Label isStableJsArray(env);
Label NotStableJsArray(env);
Label isTypedArray(env);
Label isArrayLikeObject(env);
Label throwException(env);
Label checkNeedCreateEntry(env);
Label iterDone(env);
Label createIterResult(env);
GateRef array = Circuit::NullGate();
GateRef index = Circuit::NullGate();
BRANCH(TaggedIsArrayIterator(glue, thisValue), &thisIsArrayIterator, slowPath);
Bind(&thisIsArrayIterator);
{
array = Load(VariableType::JS_POINTER(), glue, thisValue, IntPtr(JSArrayIterator::ITERATED_ARRAY_OFFSET));
BRANCH(TaggedIsUndefined(array), &iterDone, &arrayNotUndefined);
}
Bind(&arrayNotUndefined);
{
index = LoadPrimitive(VariableType::INT32(), thisValue, IntPtr(JSArrayIterator::NEXT_INDEX_OFFSET));
BRANCH(IsStableJSArray(glue, array), &isStableJsArray, &NotStableJsArray);
}
Bind(&isStableJsArray);
{
Label indexIsValid(env);
Label kindIsNotKey(env);
Label eleIsHole(env);
Label hasProperty(env);
GateRef length = GetArrayLength(array);
BRANCH(Int32UnsignedLessThan(index, length), &indexIsValid, &iterDone);
Bind(&indexIsValid);
{
IncreaseArrayIteratorIndex(glue, thisValue, index);
iterValue = IntToTaggedPtr(index);
BRANCH(Int32Equal(GetArrayIterationKind(thisValue), Int32(static_cast<int32_t>(IterationKind::KEY))),
&createIterResult, &kindIsNotKey);
Bind(&kindIsNotKey);
{
Label resultIsHole(env);
iterValue = GetTaggedValueWithElementsKind(glue, array, index);
BRANCH(TaggedIsHole(*iterValue), &resultIsHole, &checkNeedCreateEntry);
Bind(&resultIsHole);
{
iterValue = Undefined();
Jump(&checkNeedCreateEntry);
}
}
}
}
Bind(&NotStableJsArray);
BRANCH(IsTypedArray(glue, array), &isTypedArray, &isArrayLikeObject);
Bind(&isTypedArray);
{
Label indexIsValid(env);
Label kindIsNotKey(env);
BuiltinsTypedArrayStubBuilder typedArrayBuilder(this, GetCurrentGlobalEnv());
GateRef length = typedArrayBuilder.GetArrayLength(array);
BRANCH(Int32UnsignedLessThan(index, length), &indexIsValid, &iterDone);
Bind(&indexIsValid);
{
IncreaseArrayIteratorIndex(glue, thisValue, index);
iterValue = IntToTaggedPtr(index);
BRANCH(Int32Equal(GetArrayIterationKind(thisValue), Int32(static_cast<int32_t>(IterationKind::KEY))),
&createIterResult, &kindIsNotKey);
Bind(&kindIsNotKey);
{
iterValue = typedArrayBuilder.FastGetPropertyByIndex(glue, array, index,
GetObjectType(LoadHClass(glue, array)));
BRANCH(HasPendingException(glue), &throwException, &checkNeedCreateEntry);
}
}
}
Bind(&isArrayLikeObject);
{
Label indexIsValid(env);
Label kindIsNotKey(env);
Label propertyToLength(env);
Label noPendingException(env);
GateRef rawLength = Circuit::NullGate();
GateRef lengthString = GetGlobalConstantValue(VariableType::JS_POINTER(), glue,
ConstantIndex::LENGTH_STRING_INDEX);
GateRef value = FastGetPropertyByName(glue, array, lengthString, ProfileOperation());
BRANCH(HasPendingException(glue), &throwException, &propertyToLength);
Bind(&propertyToLength);
{
rawLength = ToLength(glue, value);
BRANCH(HasPendingException(glue), &throwException, &noPendingException);
}
Bind(&noPendingException);
{
GateRef length = GetInt32OfTNumber(rawLength);
BRANCH(Int32UnsignedLessThan(index, length), &indexIsValid, &iterDone);
Bind(&indexIsValid);
{
IncreaseArrayIteratorIndex(glue, thisValue, index);
iterValue = IntToTaggedPtr(index);
BRANCH(Int32Equal(GetArrayIterationKind(thisValue), Int32(static_cast<int32_t>(IterationKind::KEY))),
&createIterResult, &kindIsNotKey);
Bind(&kindIsNotKey);
{
iterValue = FastGetPropertyByIndex(glue, array, index, ProfileOperation());
BRANCH(HasPendingException(glue), &throwException, &checkNeedCreateEntry);
}
}
}
}
Bind(&throwException);
{
result->WriteVariable(Exception());
Jump(exit);
}
Bind(&checkNeedCreateEntry);
{
Label kindIsEntry(env);
BRANCH(Int32Equal(GetArrayIterationKind(thisValue), Int32(static_cast<int32_t>(IterationKind::KEY_AND_VALUE))),
&kindIsEntry, &createIterResult);
Bind(&kindIsEntry);
{
Label afterCreate(env);
NewObjectStubBuilder newBuilder(this, GetCurrentGlobalEnv());
GateRef elements = newBuilder.NewTaggedArray(glue, Int32(2));
SetValueToTaggedArray(VariableType::JS_ANY(), glue, elements, Int32(0), IntToTaggedPtr(index));
SetValueToTaggedArray(VariableType::JS_ANY(), glue, elements, Int32(1), *iterValue);
iterValue = newBuilder.CreateArrayFromList(glue, elements,
Int32(Elements::ToUint(ElementsKind::TAGGED)));
Jump(&createIterResult);
}
}
Bind(&createIterResult);
{
Label afterCreate(env);
NewObjectStubBuilder newBuilder(this, GetCurrentGlobalEnv());
newBuilder.CreateJSIteratorResult(glue, &res, *iterValue, TaggedFalse(), &afterCreate);
Bind(&afterCreate);
result->WriteVariable(*res);
Jump(exit);
}
Bind(&iterDone);
{
SetIteratedArrayOfArrayIterator(glue, thisValue, Undefined());
Label afterCreate(env);
NewObjectStubBuilder newBuilder(this, GetCurrentGlobalEnv());
newBuilder.CreateJSIteratorResult(glue, &res, Undefined(), TaggedTrue(), &afterCreate);
Bind(&afterCreate);
result->WriteVariable(*res);
Jump(exit);
}
}
void BuiltinsArrayStubBuilder::IndexOf([[maybe_unused]] GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
IndexOfOptions options;
options.compType = ComparisonType::STRICT_EQUAL;
options.returnType = IndexOfReturnType::TAGGED_FOUND_INDEX;
options.holeAsUndefined = false;
options.reversedOrder = false;
return IndexOfOptimised(glue, thisValue, numArgs, result, exit, slowPath, options);
}
void BuiltinsArrayStubBuilder::LastIndexOf([[maybe_unused]] GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
IndexOfOptions options;
options.compType = ComparisonType::STRICT_EQUAL;
options.returnType = IndexOfReturnType::TAGGED_FOUND_INDEX;
options.holeAsUndefined = false;
options.reversedOrder = true;
return IndexOfOptimised(glue, thisValue, numArgs, result, exit, slowPath, options);
}
void BuiltinsArrayStubBuilder::Pop(GateRef glue, GateRef thisValue,
[[maybe_unused]] GateRef numArgs, Variable *result, Label *exit, Label *slowPath)
{
PopOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::Slice(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
SliceOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::Sort(GateRef glue, GateRef thisValue,
GateRef numArgs, Variable *result, Label *exit, Label *slowPath)
{
GateRef callbackFnHandle = GetCallArg0(numArgs);
SortAfterArgs(glue, thisValue, callbackFnHandle, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::SortAfterArgs(GateRef glue, GateRef thisValue,
GateRef callbackFnHandle, Variable *result, Label *exit, Label *slowPath, GateRef hir)
{
auto env = GetEnvironment();
Label isHeapObject(env);
Label isJsArray(env);
Label isStability(env);
Label notCOWArray(env);
Label argUndefined(env);
BRANCH(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath);
Bind(&isHeapObject);
BRANCH(IsJsArray(glue, thisValue), &isJsArray, slowPath);
Bind(&isJsArray);
BRANCH(IsStableJSArray(glue, thisValue), &isStability, slowPath);
Bind(&isStability);
BRANCH(IsJsCOWArray(glue, thisValue), slowPath, ¬COWArray);
Bind(¬COWArray);
BRANCH(TaggedIsUndefined(callbackFnHandle), &argUndefined, slowPath);
Bind(&argUndefined);
result->WriteVariable(DoSort(glue, thisValue, false, result, exit, slowPath, hir));
Jump(exit);
}
void BuiltinsArrayStubBuilder::ToSorted(GateRef glue, GateRef thisValue,
GateRef numArgs, Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label isStability(env);
Label notCOWArray(env);
Label argUndefined(env);
BRANCH(IsStableJSArray(glue, thisValue), &isStability, slowPath);
Bind(&isStability);
BRANCH(IsJsCOWArray(glue, thisValue), slowPath, ¬COWArray);
Bind(¬COWArray);
GateRef callbackFnHandle = GetCallArg0(numArgs);
BRANCH(TaggedIsUndefined(callbackFnHandle), &argUndefined, slowPath);
Bind(&argUndefined);
GateRef thisArrLen = ZExtInt32ToInt64(GetArrayLength(thisValue));
GateRef receiver = NewArray(glue, thisArrLen);
DEFVARIABLE(i, VariableType::INT64(), Int64(0));
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label loopExit(env);
Jump(&loopHead);
LoopBegin(&loopHead);
{
BRANCH(Int64LessThan(*i, thisArrLen), &next, &loopExit);
Bind(&next);
{
GateRef ele = GetTaggedValueWithElementsKind(glue, thisValue, *i);
SetValueWithElementsKind(glue, receiver, ele, *i, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&loopEnd);
}
}
Bind(&loopEnd);
i = Int64Add(*i, Int64(1));
LoopEnd(&loopHead);
Bind(&loopExit);
result->WriteVariable(DoSort(glue, receiver, true, result, exit, slowPath));
Jump(exit);
}
GateRef BuiltinsArrayStubBuilder::DoSort(GateRef glue, GateRef receiver, bool isToSorted,
Variable *result, Label *exit, Label *slowPath, GateRef hir)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label exit1(env);
Label fastPath(env);
Label slowPath1(env);
DEFVARIABLE(res, VariableType::JS_ANY(), Undefined());
if (isToSorted) {
res = DoSortOptimised(glue, receiver, Boolean(true), result, exit, slowPath, hir);
Jump(&exit1);
} else {
BRANCH(IsEnableMutantArray(glue), &slowPath1, &fastPath);
Bind(&slowPath1);
{
res = DoSortOptimised(glue, receiver, Boolean(false), result, exit, slowPath, hir);
Jump(&exit1);
}
Bind(&fastPath);
{
res = DoSortOptimisedFast(glue, receiver, result, exit, slowPath, hir);
Jump(&exit1);
}
}
Bind(&exit1);
auto ret = *res;
env->SubCfgExit();
return ret;
}
void BuiltinsArrayStubBuilder::Reduce(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
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);
BRANCH(Int64GreaterThanOrEqual(numArgs, IntPtr(1)), &atLeastOneArg, slowPath);
Bind(&atLeastOneArg);
GateRef callbackFnHandle = GetCallArg0(numArgs);
BRANCH(TaggedIsHeapObject(callbackFnHandle), &callbackFnHandleHeapObject, slowPath);
Bind(&callbackFnHandleHeapObject);
BRANCH(IsCallable(glue, callbackFnHandle), &callbackFnHandleCallable, slowPath);
Bind(&callbackFnHandleCallable);
GateRef thisLen = GetArrayLength(thisValue);
GateRef thisLenIsZero = Int32Equal(thisLen, Int32(0));
GateRef numArgsLessThanTwo = Int64LessThan(numArgs, IntPtr(2));
BRANCH(BitAnd(thisLenIsZero, numArgsLessThanTwo), slowPath, &noTypeError);
Bind(&noTypeError);
{
DEFVARIABLE(accumulator, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(k, VariableType::INT32(), Int32(0));
Label updateAccumulator(env);
Label checkForStableJSArray(env);
BRANCH(Int64Equal(numArgs, IntPtr(2)), &updateAccumulator, slowPath);
Bind(&updateAccumulator);
{
accumulator = GetCallArg1(numArgs);
Jump(&checkForStableJSArray);
}
Bind(&checkForStableJSArray);
{
Label isStableJSArray(env);
Label notStableJSArray(env);
BRANCH(IsStableJSArray(glue, thisValue), &isStableJSArray, ¬StableJSArray);
Bind(&isStableJSArray);
{
GateRef argsLength = Int32(4);
NewObjectStubBuilder newBuilder(this);
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_LIKELY(Int32UnsignedLessThan(*k, thisLen), &next, &loopExit);
Bind(&next);
BRANCH_LIKELY(Int32UnsignedLessThan(*k, GetArrayLength(thisValue)), &doCall, &writeResult);
Bind(&doCall);
{
Label updateK(env);
Label notHole(env);
Label changeThisLen(env);
Label updateCallResult(env);
GateRef kValue = GetTaggedValueWithElementsKind(glue, thisValue, *k);
BRANCH(TaggedIsHole(kValue), &loopEnd, ¬Hole);
Bind(¬Hole);
{
SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(0), *accumulator);
SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(1), kValue);
SetValueToTaggedArray(VariableType::INT32(), glue, argList, Int32(2), IntToTaggedInt(*k));
SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(3), thisValue);
GateRef argv = PtrAdd(argList, IntPtr(TaggedArray::DATA_OFFSET));
JSCallArgs callArgs(JSCallMode::CALL_THIS_ARGV_WITH_RETURN);
callArgs.callThisArgvWithReturnArgs = { argsLength, argv, Undefined() };
CallStubBuilder callBuilder(this, glue, callbackFnHandle, argsLength, 0, nullptr,
Circuit::NullGate(), callArgs);
GateRef callResult = callBuilder.JSCallDispatch();
Label hasException(env);
Label notHasException(env);
BRANCH(HasPendingException(glue), &hasException, ¬HasException);
Bind(&hasException);
{
result->WriteVariable(Exception());
Jump(exit);
}
Bind(¬HasException);
accumulator = callResult;
Jump(&loopEnd);
}
}
}
Bind(&loopEnd);
{
k = Int32Add(*k, Int32(1));
Label isStableJSArray1(env);
Label notStableJSArray1(env);
BRANCH(IsStableJSArray(glue, thisValue), &isStableJSArray1, ¬StableJSArray1);
Bind(¬StableJSArray1);
{
Jump(&loopExit);
}
Bind(&isStableJSArray1);
LoopEndWithCheckSafePoint(&loopHead, env, glue);
}
Bind(&loopExit);
Jump(¬StableJSArray);
}
Bind(¬StableJSArray);
{
Label callRT(env);
BRANCH(Int32UnsignedLessThan(*k, thisLen), &callRT, &writeResult);
Bind(&callRT);
{
accumulator = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(),
RTSTUB_ID(JSArrayReduceUnStable), { thisValue, thisValue, IntToTaggedInt(*k),
IntToTaggedInt(thisLen), *accumulator, callbackFnHandle });
Jump(&writeResult);
}
}
}
Bind(&writeResult);
result->WriteVariable(*accumulator);
Jump(exit);
}
}
void BuiltinsArrayStubBuilder::Reverse(GateRef glue, GateRef thisValue, [[maybe_unused]] GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label isStability(env);
Label notCOWArray(env);
BRANCH(IsStableJSArray(glue, thisValue), &isStability, slowPath);
Bind(&isStability);
BRANCH(IsJsCOWArray(glue, thisValue), slowPath, ¬COWArray);
Bind(¬COWArray);
GateRef thisLen = GetArrayLength(thisValue);
GateRef thisArrLen = ZExtInt32ToInt64(thisLen);
Label useReversBarrier(env);
Label noReverseBarrier(env);
BRANCH(Int32LessThan(thisLen, Int32(10)), &noReverseBarrier, &useReversBarrier);
Bind(&useReversBarrier);
ReverseOptimised(glue, thisValue, thisLen, result, exit);
Bind(&noReverseBarrier);
DEFVARIABLE(i, VariableType::INT64(), Int64(0));
DEFVARIABLE(j, VariableType::INT64(), Int64Sub(thisArrLen, Int64(1)));
GateRef hclass = LoadHClass(glue, thisValue);
GateRef kind = GetElementsKindFromHClass(hclass);
Label isInt(env);
Label isNotInt(env);
Label notFastKind(env);
GateRef isMutantArrayEnabled = IsEnableMutantArray(glue);
GateRef checkIntKind = LogicAndBuilder(env)
.And(isMutantArrayEnabled)
.And(Int32GreaterThanOrEqual(kind, Int32(Elements::ToUint(ElementsKind::INT))))
.And(Int32LessThanOrEqual(kind, Int32(Elements::ToUint(ElementsKind::HOLE_INT))))
.Done();
BRANCH(checkIntKind, &isInt, &isNotInt);
Bind(&isInt);
{
FastReverse(glue, thisValue, thisArrLen, ElementsKind::INT, result, exit);
}
Bind(&isNotInt);
{
Label isNumber(env);
Label isNotNumber(env);
GateRef checkNumberKind = LogicAndBuilder(env)
.And(isMutantArrayEnabled)
.And(Int32GreaterThanOrEqual(kind, Int32(Elements::ToUint(ElementsKind::NUMBER))))
.And(Int32LessThanOrEqual(kind, Int32(Elements::ToUint(ElementsKind::HOLE_NUMBER))))
.Done();
BRANCH(checkNumberKind, &isNumber, &isNotNumber);
Bind(&isNumber);
{
FastReverse(glue, thisValue, thisArrLen, ElementsKind::NUMBER, result, exit);
}
Bind(&isNotNumber);
{
FastReverse(glue, thisValue, thisArrLen, ElementsKind::TAGGED, result, exit);
}
}
}
void BuiltinsArrayStubBuilder::FastReverse(GateRef glue, GateRef thisValue, GateRef len,
ElementsKind kind, Variable *result, Label *exit)
{
auto env = GetEnvironment();
DEFVARIABLE(i, VariableType::INT64(), Int64(0));
DEFVARIABLE(j, VariableType::INT64(), Int64Sub(len, Int64(1)));
GateRef elements = GetElementsArray(glue, thisValue);
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label loopExit(env);
Jump(&loopHead);
LoopBegin(&loopHead);
{
Label arrayValue(env);
Label valueEqual(env);
BRANCH(Int64LessThan(*i, *j), &next, &loopExit);
Bind(&next);
{
if (kind == ElementsKind::INT || kind == ElementsKind::NUMBER) {
GateRef lower = GetValueFromMutantTaggedArray(elements, *i);
GateRef upper = GetValueFromMutantTaggedArray(elements, *j);
FastSetValueWithElementsKind(glue, thisValue, elements, upper, *i, kind);
FastSetValueWithElementsKind(glue, thisValue, elements, lower, *j, kind);
Jump(&loopEnd);
} else {
GateRef lower = GetValueFromTaggedArray(glue, elements, *i);
GateRef upper = GetValueFromTaggedArray(glue, elements, *j);
FastSetValueWithElementsKind(glue, thisValue, elements, upper, *i, kind);
FastSetValueWithElementsKind(glue, thisValue, elements, lower, *j, kind);
Jump(&loopEnd);
}
}
}
Bind(&loopEnd);
i = Int64Add(*i, Int64(1));
j = Int64Sub(*j, Int64(1));
LoopEndWithCheckSafePoint(&loopHead, env, glue);
Bind(&loopExit);
result->WriteVariable(thisValue);
Jump(exit);
}
void BuiltinsArrayStubBuilder::ToReversed(GateRef glue, GateRef thisValue, [[maybe_unused]] GateRef numArgs,
Variable* result, Label* exit, Label* slowPath)
{
ToReversedOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
GateRef BuiltinsArrayStubBuilder::DoReverse(GateRef glue, GateRef thisValue, GateRef receiver,
GateRef receiverState, Variable *result, Label *exit)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
DEFVARIABLE(i, VariableType::INT64(), Int64(0));
DEFVARIABLE(j, VariableType::INT64(), Int64Sub(ZExtInt32ToInt64(GetArrayLength(thisValue)), Int64(1)));
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label loopExit(env);
Jump(&loopHead);
LoopBegin(&loopHead);
{
DEFVARIABLE(lower, VariableType::JS_ANY(), Hole());
DEFVARIABLE(upper, VariableType::JS_ANY(), Hole());
Label lowerValueIsHole(env);
Label afterGettingLower(env);
Label lowerHasProperty(env);
Label lowerHasException0(env);
Label upperValueIsHole(env);
Label afterGettingUpper(env);
Label upperHasProperty(env);
Label upperHasException0(env);
Label receiverIsNew(env);
Label receiverIsOrigin(env);
Label lowerIsHole(env);
Label lowerIsNotHole(env);
Label dealWithUpper(env);
Label upperIsHole(env);
Label upperIsNotHole(env);
BRANCH(Int64LessThanOrEqual(*i, *j), &next, &loopExit);
Bind(&next);
{
lower = GetTaggedValueWithElementsKind(glue, thisValue, *i);
BRANCH(TaggedIsHole(*lower), &lowerValueIsHole, &afterGettingLower);
Bind(&lowerValueIsHole);
{
GateRef lowerHasProp = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(), RTSTUB_ID(HasProperty),
{ thisValue, IntToTaggedInt(*i) });
BRANCH(TaggedIsTrue(lowerHasProp), &lowerHasProperty, &afterGettingLower);
Bind(&lowerHasProperty);
{
lower = FastGetPropertyByIndex(glue, thisValue, TruncInt64ToInt32(*i), ProfileOperation());
BRANCH(HasPendingException(glue), &lowerHasException0, &afterGettingLower);
Bind(&lowerHasException0);
{
result->WriteVariable(Exception());
Jump(exit);
}
}
}
Bind(&afterGettingLower);
{
upper = GetTaggedValueWithElementsKind(glue, thisValue, *j);
BRANCH(TaggedIsHole(*upper), &upperValueIsHole, &afterGettingUpper);
Bind(&upperValueIsHole);
{
GateRef upperHasProp = CallRuntimeWithGlobalEnv(glue, GetCurrentGlobalEnv(),
RTSTUB_ID(HasProperty), { thisValue, IntToTaggedInt(*j) });
BRANCH(TaggedIsTrue(upperHasProp), &upperHasProperty, &afterGettingUpper);
Bind(&upperHasProperty);
{
upper = FastGetPropertyByIndex(glue, thisValue, TruncInt64ToInt32(*j), ProfileOperation());
BRANCH(HasPendingException(glue), &upperHasException0, &afterGettingUpper);
}
Bind(&upperHasException0);
{
result->WriteVariable(Exception());
Jump(exit);
}
}
Bind(&afterGettingUpper);
{
BRANCH(receiverState, &receiverIsNew, &receiverIsOrigin);
Bind(&receiverIsNew);
{
BRANCH(TaggedIsHole(*lower), &lowerIsHole, &lowerIsNotHole);
Bind(&lowerIsHole);
{
SetValueWithElementsKind(glue, receiver, Undefined(), *j, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&dealWithUpper);
}
Bind(&lowerIsNotHole);
{
SetValueWithElementsKind(glue, receiver, *lower, *j, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&dealWithUpper);
}
Bind(&dealWithUpper);
{
BRANCH(TaggedIsHole(*upper), &upperIsHole, &upperIsNotHole);
Bind(&upperIsHole);
{
SetValueWithElementsKind(glue, receiver, Undefined(), *i, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&loopEnd);
}
Bind(&upperIsNotHole);
{
SetValueWithElementsKind(glue, receiver, *upper, *i, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&loopEnd);
}
}
}
Bind(&receiverIsOrigin);
{
SetValueWithElementsKind(glue, receiver, *upper, *i, Boolean(false),
Int32(Elements::ToUint(ElementsKind::NONE)));
SetValueWithElementsKind(glue, receiver, *lower, *j, Boolean(false),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&loopEnd);
}
}
}
}
}
Bind(&loopEnd);
i = Int64Add(*i, Int64(1));
j = Int64Sub(*j, Int64(1));
LoopEnd(&loopHead);
Bind(&loopExit);
env->SubCfgExit();
return receiver;
}
GateRef BuiltinsArrayStubBuilder::IsJsArrayWithLengthLimit(GateRef glue, GateRef object,
uint32_t maxLength, JsArrayRequirements requirements)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
Label isHeapObject(env);
Label isJsArray(env);
Label stabilityCheckPassed(env);
Label defaultConstructorCheckPassed(env);
Label exit(env);
DEFVARIABLE(result, VariableType::BOOL(), False());
BRANCH(TaggedIsHeapObject(object), &isHeapObject, &exit);
Bind(&isHeapObject);
BRANCH(IsJsArray(glue, object), &isJsArray, &exit);
Bind(&isJsArray);
if (requirements.stable) {
BRANCH(IsStableJSArray(glue, object), &stabilityCheckPassed, &exit);
} else {
Jump(&stabilityCheckPassed);
}
Bind(&stabilityCheckPassed);
if (requirements.defaultConstructor) {
BRANCH(HasConstructor(glue, object), &exit, &defaultConstructorCheckPassed);
} else {
Jump(&defaultConstructorCheckPassed);
}
Bind(&defaultConstructorCheckPassed);
result.WriteVariable(Int32UnsignedLessThanOrEqual(GetArrayLength(object), Int32(maxLength)));
Jump(&exit);
Bind(&exit);
GateRef ret = *result;
env->SubCfgExit();
return ret;
}
void BuiltinsArrayStubBuilder::Values(GateRef glue, GateRef thisValue,
[[maybe_unused]] GateRef numArgs, Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label isHeapObject(env);
Label isJsArray(env);
Label defaultConstr(env);
BRANCH(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath);
Bind(&isHeapObject);
BRANCH(IsJsArray(glue, thisValue), &isJsArray, slowPath);
Bind(&isJsArray);
ConstantIndex iterClassIdx = ConstantIndex::JS_ARRAY_ITERATOR_CLASS_INDEX;
GateRef iteratorHClass = GetGlobalConstantValue(VariableType::JS_POINTER(), glue, iterClassIdx);
NewObjectStubBuilder newBuilder(this);
newBuilder.SetParameters(glue, 0);
GateRef globalEnv = GetCurrentGlobalEnv();
GateRef prototype = GetGlobalEnvValue(VariableType::JS_POINTER(), glue, globalEnv,
GlobalEnv::ARRAY_ITERATOR_PROTOTYPE_INDEX);
SetPrototypeToHClass(VariableType::JS_POINTER(), glue, iteratorHClass, prototype);
GateRef iter = newBuilder.NewJSObject(glue, iteratorHClass);
SetIteratedArrayOfArrayIterator(glue, iter, thisValue);
SetNextIndexOfArrayIterator(glue, iter, Int32(0));
GateRef kind = Int32(static_cast<int32_t>(IterationKind::VALUE));
SetBitFieldOfArrayIterator(glue, iter, kind);
result->WriteVariable(iter);
Jump(exit);
}
void BuiltinsArrayStubBuilder::Find(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
FindOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::FindIndex(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
FindIndexOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::Push(GateRef glue, GateRef thisValue,
GateRef numArgs, Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label isStability(env);
Label setLength(env);
Label smallArgs(env);
Label checkSmallArgs(env);
Label isLengthWritable(env);
BRANCH_LIKELY(IsStableJSArray(glue, thisValue), &isStability, slowPath);
Bind(&isStability);
BRANCH_LIKELY(IsArrayLengthWritable(glue, thisValue), &isLengthWritable, slowPath);
Bind(&isLengthWritable);
GateRef oldLength = GetArrayLength(thisValue);
*result = IntToTaggedPtr(oldLength);
BRANCH_UNLIKELY(Int32Equal(ChangeIntPtrToInt32(numArgs), Int32(0)), exit, &checkSmallArgs);
Bind(&checkSmallArgs);
BRANCH_LIKELY(Int32LessThanOrEqual(ChangeIntPtrToInt32(numArgs), Int32(2)), &smallArgs, slowPath);
Bind(&smallArgs);
GateRef newLength = Int32Add(oldLength, ChangeIntPtrToInt32(numArgs));
DEFVARIABLE(elements, VariableType::JS_ANY(), GetElementsArray(glue, thisValue));
GateRef capacity = GetLengthOfTaggedArray(*elements);
Label grow(env);
Label setValue(env);
BRANCH_UNLIKELY(Int32GreaterThan(newLength, capacity), &grow, &setValue);
Bind(&grow);
{
elements = CallCommonStub(glue, CommonStubCSigns::GrowElementsCapacity,
{glue, thisValue, GetCurrentGlobalEnv(), newLength});
Jump(&setValue);
}
Bind(&setValue);
{
Label oneArg(env);
Label twoArg(env);
DEFVARIABLE(index, VariableType::INT32(), Int32(0));
DEFVARIABLE(value, VariableType::JS_ANY(), Undefined());
BRANCH_LIKELY(Int64Equal(numArgs, IntPtr(1)), &oneArg, &twoArg);
Bind(&oneArg);
{
value = GetCallArg0(numArgs);
index = Int32Add(oldLength, Int32(0));
SetValueWithElementsKind(glue, thisValue, *value, *index, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&setLength);
}
Bind(&twoArg);
{
DEFVARIABLE(index2, VariableType::INT32(), Int32(0));
DEFVARIABLE(value2, VariableType::JS_ANY(), Undefined());
value = GetCallArg0(numArgs);
index = Int32Add(oldLength, Int32(0));
value2 = GetCallArg1(numArgs);
index2 = Int32Add(oldLength, Int32(1));
SetValueWithElementsKind(glue, thisValue, *value, *index, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
SetValueWithElementsKind(glue, thisValue, *value2, *index2, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&setLength);
}
}
Bind(&setLength);
SetArrayLength(glue, thisValue, newLength);
result->WriteVariable(IntToTaggedPtr(newLength));
Jump(exit);
}
GateRef BuiltinsArrayStubBuilder::IsConcatSpreadable(GateRef glue, GateRef obj)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
DEFVARIABLE(result, VariableType::BOOL(), False());
Label exit(env);
Label isEcmaObj(env);
BRANCH(IsEcmaObject(glue, obj), &isEcmaObj, &exit);
Bind(&isEcmaObj);
{
GateRef globalEnv = GetCurrentGlobalEnv();
GateRef isConcatsprKey =
GetGlobalConstantValue(VariableType::JS_ANY(), glue, ConstantIndex::ISCONCAT_SYMBOL_INDEX);
AccessObjectStubBuilder builder(this, globalEnv);
GateRef spreadable =
builder.LoadObjByValue(glue, obj, isConcatsprKey, Undefined(), Int32(0), ProfileOperation());
Label isDefined(env);
Label isUnDefined(env);
BRANCH(TaggedIsUndefined(spreadable), &isUnDefined, &isDefined);
Bind(&isUnDefined);
{
Label IsArray(env);
BRANCH(IsJsArray(glue, obj), &IsArray, &exit);
Bind(&IsArray);
result = True();
Jump(&exit);
}
Bind(&isDefined);
{
result = TaggedIsTrue(spreadable);
Jump(&exit);
}
}
Bind(&exit);
auto res = *result;
env->SubCfgExit();
return res;
}
void BuiltinsArrayStubBuilder::InitializeArray(GateRef glue, GateRef count, Variable* result)
{
GateRef lengthOffset = IntPtr(JSArray::LENGTH_OFFSET);
Store(VariableType::INT32(), glue, result->ReadVariable(), lengthOffset, TruncInt64ToInt32(count));
GateRef accessor = GetGlobalConstantValue(VariableType::JS_ANY(), glue, ConstantIndex::ARRAY_LENGTH_ACCESSOR);
Store(VariableType::JS_ANY(), glue, result->ReadVariable(),
IntPtr(JSArray::GetInlinedPropertyOffset(JSArray::LENGTH_INLINE_PROPERTY_INDEX)), accessor);
SetExtensibleToBitfield(glue, result->ReadVariable(), true);
}
GateRef BuiltinsArrayStubBuilder::NewArray(GateRef glue, GateRef count)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
DEFVARIABLE(result, VariableType::JS_POINTER(), Undefined());
Label exit(env);
Label setProperties(env);
GateRef globalEnv = GetCurrentGlobalEnv();
auto arrayFunc = GetGlobalEnvValue(VariableType::JS_ANY(), glue, globalEnv, GlobalEnv::ARRAY_FUNCTION_INDEX);
GateRef initialHClass = Load(VariableType::JS_ANY(), glue, arrayFunc, IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET));
NewObjectStubBuilder newBuilder(this, globalEnv);
newBuilder.SetParameters(glue, 0);
result = newBuilder.NewJSArrayWithSize(initialHClass, count);
BRANCH(TaggedIsException(*result), &exit, &setProperties);
Bind(&setProperties);
{
InitializeArray(glue, count, &result);
Jump(&exit);
}
Bind(&exit);
auto res = *result;
env->SubCfgExit();
return res;
}
void BuiltinsArrayStubBuilder::Includes(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
IndexOfOptions options;
options.compType = ComparisonType::SAME_VALUE_ZERO;
options.returnType = IndexOfReturnType::TAGGED_FOUND_OR_NOT;
options.holeAsUndefined = true;
options.reversedOrder = false;
IndexOfOptimised(glue, thisValue, numArgs, result, exit, slowPath, options);
}
void BuiltinsArrayStubBuilder::From(GateRef glue, [[maybe_unused]] GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
GateRef item = GetCallArg0(numArgs);
Label stringItem(env);
BRANCH(TaggedIsString(glue, item), &stringItem, slowPath);
Bind(&stringItem);
Label undefFn(env);
GateRef fn = GetCallArg1(numArgs);
BRANCH(TaggedIsUndefined(fn), &undefFn, slowPath);
Bind(&undefFn);
GateRef strLen = GetLengthFromString(item);
Label lessStrLen(env);
BRANCH(Int32LessThan(strLen, Int32(builtins::StringToListResultCache::MAX_STRING_LENGTH)), &lessStrLen, slowPath);
Bind(&lessStrLen);
GateRef globalEnv = GetCurrentGlobalEnv();
auto cacheArray =
GetGlobalEnvValue(VariableType::JS_ANY(), glue, globalEnv, GlobalEnv::STRING_TO_LIST_RESULT_CACHE_INDEX);
Label cacheDef(env);
BRANCH(TaggedIsUndefined(cacheArray), slowPath, &cacheDef);
Bind(&cacheDef);
{
GateRef hash = GetHashcodeFromString(glue, item);
GateRef entry = Int32And(hash, Int32Sub(Int32(builtins::StringToListResultCache::CACHE_SIZE), Int32(1)));
GateRef index = Int32Mul(entry, Int32(builtins::StringToListResultCache::ENTRY_SIZE));
GateRef cacheStr = GetValueFromTaggedArray(glue, cacheArray,
Int32Add(index, Int32(builtins::StringToListResultCache::STRING_INDEX)));
Label cacheStrDef(env);
BRANCH(TaggedIsUndefined(cacheStr), slowPath, &cacheStrDef);
Bind(&cacheStrDef);
Label strEqual(env);
Label strSlowEqual(env);
BRANCH(Equal(cacheStr, item), &strEqual, &strSlowEqual);
Bind(&strSlowEqual);
BRANCH(FastStringEqual(glue, cacheStr, item), &strEqual, slowPath);
Bind(&strEqual);
GateRef cacheResArray = GetValueFromTaggedArray(glue, cacheArray,
Int32Add(index, Int32(builtins::StringToListResultCache::ARRAY_INDEX)));
auto arrayFunc = GetGlobalEnvValue(VariableType::JS_ANY(), glue, globalEnv, GlobalEnv::ARRAY_FUNCTION_INDEX);
GateRef initialHClass = Load(VariableType::JS_ANY(), glue, arrayFunc,
IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET));
NewObjectStubBuilder newBuilder(this);
newBuilder.SetParameters(glue, 0);
GateRef newArray = newBuilder.NewJSObject(glue, initialHClass);
Store(VariableType::INT32(), glue, newArray, IntPtr(JSArray::LENGTH_OFFSET), strLen);
GateRef accessor = GetGlobalConstantValue(VariableType::JS_ANY(), glue, ConstantIndex::ARRAY_LENGTH_ACCESSOR);
Store(VariableType::JS_ANY(), glue, newArray,
IntPtr(JSArray::GetInlinedPropertyOffset(JSArray::LENGTH_INLINE_PROPERTY_INDEX)), accessor);
SetExtensibleToBitfield(glue, newArray, true);
SetElementsArray(VariableType::JS_ANY(), glue, newArray, cacheResArray);
*result = newArray;
Jump(exit);
}
}
GateRef BuiltinsArrayStubBuilder::CreateSpliceDeletedArray(GateRef glue, GateRef thisValue, GateRef actualDeleteCount,
GateRef start)
{
auto env = GetEnvironment();
Label subentry(env);
Label exit(env);
env->SubCfgEntry(&subentry);
DEFVARIABLE(result, VariableType::JS_POINTER(), Undefined());
DEFVARIABLE(srcElements, VariableType::JS_ANY(), GetElementsArray(glue, thisValue));
GateRef newArray = NewArray(glue, actualDeleteCount);
result = newArray;
DEFVARIABLE(i, VariableType::INT32(), Int32(0));
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label loopExit(env);
Jump(&loopHead);
LoopBegin(&loopHead);
{
BRANCH(Int32LessThan(*i, actualDeleteCount), &next, &loopExit);
Bind(&next);
Label setHole(env);
Label setSrc(env);
BRANCH(Int32GreaterThanOrEqual(Int32Add(*i, start),
GetLengthOfTaggedArray(*srcElements)), &setHole, &setSrc);
Bind(&setHole);
{
SetValueWithElementsKind(glue, newArray, Hole(), *i, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&loopEnd);
}
Bind(&setSrc);
{
GateRef val = GetTaggedValueWithElementsKind(glue, thisValue, Int32Add(start, *i));
SetValueWithElementsKind(glue, newArray, val, *i, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&loopEnd);
}
}
Bind(&loopEnd);
i = Int32Add(*i, Int32(1));
LoopEndWithCheckSafePoint(&loopHead, env, glue);
Bind(&loopExit);
Jump(&exit);
Bind(&exit);
auto res = *result;
env->SubCfgExit();
return res;
}
void BuiltinsArrayStubBuilder::Fill(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
FillOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::Splice(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label isStability(env);
Label defaultConstr(env);
BRANCH(IsStableJSArray(glue, thisValue), &isStability, slowPath);
Bind(&isStability);
BRANCH(HasConstructor(glue, thisValue), slowPath, &defaultConstr);
Bind(&defaultConstr);
Label notCOWArray(env);
BRANCH(IsJsCOWArray(glue, thisValue), slowPath, ¬COWArray);
Bind(¬COWArray);
GateRef arrayLen = GetArrayLength(thisValue);
Label lessThreeArg(env);
DEFVARIABLE(start, VariableType::INT32(), Int32(0));
DEFVARIABLE(insertCount, VariableType::INT32(), Int32(0));
DEFVARIABLE(actualDeleteCount, VariableType::INT32(), Int32(0));
GateRef argc = ChangeIntPtrToInt32(numArgs);
BRANCH(Int32LessThanOrEqual(argc, Int32(3)), &lessThreeArg, slowPath);
Bind(&lessThreeArg);
{
Label checkOverflow(env);
Label greaterZero(env);
Label greaterOne(env);
Label checkGreaterOne(env);
Label notOverflow(env);
BRANCH(Int32GreaterThan(argc, Int32(0)), &greaterZero, &checkGreaterOne);
Bind(&greaterZero);
GateRef taggedStart = GetCallArg0(numArgs);
Label taggedStartInt(env);
BRANCH(TaggedIsInt(taggedStart), &taggedStartInt, slowPath);
Bind(&taggedStartInt);
{
GateRef intStart = GetInt32OfTInt(taggedStart);
start = CalArrayRelativePos(intStart, arrayLen);
}
actualDeleteCount = Int32Sub(arrayLen, *start);
Jump(&checkGreaterOne);
Bind(&checkGreaterOne);
BRANCH(Int32GreaterThan(argc, Int32(1)), &greaterOne, &checkOverflow);
Bind(&greaterOne);
insertCount = Int32Sub(argc, Int32(2));
GateRef argDeleteCount = GetCallArg1(numArgs);
Label argDeleteCountInt(env);
BRANCH(TaggedIsInt(argDeleteCount), &argDeleteCountInt, slowPath);
Bind(&argDeleteCountInt);
DEFVARIABLE(deleteCount, VariableType::INT32(), TaggedGetInt(argDeleteCount));
Label deleteCountLessZero(env);
Label calActualDeleteCount(env);
BRANCH(Int32LessThan(*deleteCount, Int32(0)), &deleteCountLessZero, &calActualDeleteCount);
Bind(&deleteCountLessZero);
deleteCount = Int32(0);
Jump(&calActualDeleteCount);
Bind(&calActualDeleteCount);
actualDeleteCount = *deleteCount;
Label lessArrayLen(env);
BRANCH(Int32LessThan(Int32Sub(arrayLen, *start), *deleteCount), &lessArrayLen, &checkOverflow);
Bind(&lessArrayLen);
actualDeleteCount = Int32Sub(arrayLen, *start);
Jump(&checkOverflow);
Bind(&checkOverflow);
BRANCH(Int64GreaterThan(Int64Sub(Int64Add(ZExtInt32ToInt64(arrayLen), ZExtInt32ToInt64(*insertCount)),
ZExtInt32ToInt64(*actualDeleteCount)), Int64(base::MAX_SAFE_INTEGER)), slowPath, ¬Overflow);
Bind(¬Overflow);
*result = CreateSpliceDeletedArray(glue, thisValue, *actualDeleteCount, *start);
DEFVARIABLE(srcElements, VariableType::JS_ANY(), GetElementsArray(glue, thisValue));
GateRef oldCapacity = GetLengthOfTaggedArray(*srcElements);
GateRef newCapacity = Int32Add(Int32Sub(arrayLen, *actualDeleteCount), *insertCount);
Label grow(env);
Label copy(env);
BRANCH(Int32GreaterThan(newCapacity, oldCapacity), &grow, ©);
Bind(&grow);
{
srcElements = GrowElementsCapacity(glue, thisValue, newCapacity);
Jump(©);
}
Bind(©);
GateRef srcElementsLen = GetLengthOfTaggedArray(*srcElements);
Label insertLessDelete(env);
Label insertGreaterDelete(env);
Label insertCountVal(env);
Label setArrayLen(env);
Label trimCheck(env);
BRANCH(Int32LessThan(*insertCount, *actualDeleteCount), &insertLessDelete, &insertGreaterDelete);
Bind(&insertLessDelete);
{
{
DEFVARIABLE(i, VariableType::INT32(), *start);
DEFVARIABLE(ele, VariableType::JS_ANY(), Hole());
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label loopExit(env);
Jump(&loopHead);
LoopBegin(&loopHead);
{
BRANCH(Int32LessThan(*i, Int32Sub(arrayLen, *actualDeleteCount)), &next, &loopExit);
Bind(&next);
ele = Hole();
Label getSrcEle(env);
Label setEle(env);
BRANCH(Int32LessThan(Int32Add(*i, *actualDeleteCount), srcElementsLen), &getSrcEle, &setEle);
Bind(&getSrcEle);
{
ele = GetTaggedValueWithElementsKind(glue, thisValue, Int32Add(*i, *actualDeleteCount));
Jump(&setEle);
}
Bind(&setEle);
{
Label setIndexLessLen(env);
BRANCH(Int32LessThan(Int32Add(*i, *insertCount), srcElementsLen), &setIndexLessLen, &loopEnd);
Bind(&setIndexLessLen);
{
SetValueWithElementsKind(glue, thisValue, *ele, Int32Add(*i, *insertCount), Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&loopEnd);
}
}
}
Bind(&loopEnd);
i = Int32Add(*i, Int32(1));
LoopEndWithCheckSafePoint(&loopHead, env, glue);
Bind(&loopExit);
Jump(&trimCheck);
}
Label trim(env);
Label noTrim(env);
Bind(&trimCheck);
GateRef needTrim = LogicAndBuilder(env)
.And(Int32GreaterThan(oldCapacity, newCapacity))
.And(Int32GreaterThan(Int32Sub(oldCapacity, newCapacity), Int32(TaggedArray::MAX_END_UNUSED)))
.Done();
BRANCH(needTrim, &trim, &noTrim);
Bind(&trim);
{
CallNGCRuntime(glue, RTSTUB_ID(ArrayTrim), {glue, *srcElements, ZExtInt32ToInt64(newCapacity)});
Jump(&insertCountVal);
}
Bind(&noTrim);
{
DEFVARIABLE(idx, VariableType::INT32(), newCapacity);
Label loopHead1(env);
Label loopEnd1(env);
Label next1(env);
Label loopExit1(env);
Jump(&loopHead1);
LoopBegin(&loopHead1);
{
BRANCH(Int32LessThan(*idx, arrayLen), &next1, &loopExit1);
Bind(&next1);
Label setHole(env);
BRANCH(Int32LessThan(*idx, srcElementsLen), &setHole, &loopEnd1);
Bind(&setHole);
{
SetValueWithElementsKind(glue, thisValue, Hole(), *idx, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&loopEnd1);
}
}
Bind(&loopEnd1);
idx = Int32Add(*idx, Int32(1));
LoopEnd(&loopHead1);
Bind(&loopExit1);
Jump(&insertCountVal);
}
Bind(&insertGreaterDelete);
{
DEFVARIABLE(j, VariableType::INT32(), Int32Sub(arrayLen, *actualDeleteCount));
DEFVARIABLE(ele, VariableType::JS_ANY(), Hole());
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label loopExit(env);
Jump(&loopHead);
LoopBegin(&loopHead);
{
BRANCH(Int32GreaterThan(*j, *start), &next, &loopExit);
Bind(&next);
ele = GetTaggedValueWithElementsKind(glue, thisValue, Int32Sub(Int32Add(*j, *actualDeleteCount),
Int32(1)));
SetValueWithElementsKind(glue, thisValue, *ele, Int32Sub(Int32Add(*j, *insertCount), Int32(1)),
Boolean(true), Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&loopEnd);
}
Bind(&loopEnd);
j = Int32Sub(*j, Int32(1));
LoopEndWithCheckSafePoint(&loopHead, env, glue);
Bind(&loopExit);
Jump(&insertCountVal);
}
Bind(&insertCountVal);
{
Label threeArgs(env);
BRANCH(Int32Equal(ChangeIntPtrToInt32(numArgs), Int32(3)), &threeArgs, &setArrayLen);
Bind(&threeArgs);
{
GateRef e = GetCallArg2(numArgs);
SetValueWithElementsKind(glue, thisValue, e, *start, Boolean(true),
Int32(Elements::ToUint(ElementsKind::NONE)));
Jump(&setArrayLen);
}
}
Bind(&setArrayLen);
SetArrayLength(glue, thisValue, newCapacity);
Jump(exit);
}
}
}
void BuiltinsArrayStubBuilder::ToSpliced(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
ToSplicedOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::CopyWithin(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
CopyWithinOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
GateRef BuiltinsArrayStubBuilder::CalculatePositionWithLength(GateRef position, GateRef length)
{
auto env = GetEnvironment();
Label entry(env);
env->SubCfgEntry(&entry);
DEFVARIABLE(result, VariableType::INT64(), Int64(0));
Label positionLessThanZero(env);
Label positionNotLessThanZero(env);
Label resultNotGreaterThanZero(env);
Label positionLessThanLength(env);
Label positionNotLessThanLength(env);
Label afterCalculatePosition(env);
BRANCH(Int64LessThan(position, Int64(0)), &positionLessThanZero, &positionNotLessThanZero);
Bind(&positionLessThanZero);
{
result = Int64Add(position, length);
BRANCH(Int64GreaterThan(*result, Int64(0)), &afterCalculatePosition, &resultNotGreaterThanZero);
Bind(&resultNotGreaterThanZero);
result = Int64(0);
Jump(&afterCalculatePosition);
}
Bind(&positionNotLessThanZero);
{
BRANCH(Int64LessThan(position, length), &positionLessThanLength, &positionNotLessThanLength);
Bind(&positionLessThanLength);
{
result = position;
Jump(&afterCalculatePosition);
}
Bind(&positionNotLessThanLength);
{
result = length;
Jump(&afterCalculatePosition);
}
}
Bind(&afterCalculatePosition);
auto ret = *result;
env->SubCfgExit();
return ret;
}
void BuiltinsArrayStubBuilder::Some(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
SomeOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
void BuiltinsArrayStubBuilder::Every(GateRef glue, GateRef thisValue, GateRef numArgs,
Variable *result, Label *exit, Label *slowPath)
{
EveryOptimised(glue, thisValue, numArgs, result, exit, slowPath);
}
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 notCOWArray(env);
Label isGeneric(env);
BRANCH(TaggedIsUndefinedOrNull(thisValue), slowPath, &thisExists);
Bind(&thisExists);
BRANCH(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath);
Bind(&isHeapObject);
BRANCH(IsJsArray(glue, thisValue), &isJsArray, slowPath);
Bind(&isJsArray);
BRANCH(IsJsCOWArray(glue, thisValue), slowPath, ¬COWArray);
Bind(¬COWArray);
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 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(glue, callbackFnHandle), &callbackFnHandleCallable, slowPath);
Bind(&callbackFnHandleCallable);
GateRef numArgsLessThanTwo = Int64LessThan(numArgs, IntPtr(2));
k = Int32Sub(*thisLen, Int32(1));
BRANCH(numArgsLessThanTwo, slowPath, &updateAccumulator);
Bind(&updateAccumulator);
accumulator = GetCallArg1(numArgs);
BRANCH(IsStableJSArray(glue, thisValue), &thisIsStable, &thisNotStable);
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 thisLenVal = *thisLen;
BRANCH(LogicAndBuilder(env).And(IsStableJSArray(glue, thisValue))
.And(Int32Equal(thisLenVal, GetArrayLength(thisValue))).Done(),
&nextStep, &thisNotStable);
Bind(&nextStep);
BRANCH(Int32GreaterThanOrEqual(*k, Int32(0)), &next, &loopExit);
Bind(&next);
kValue = GetTaggedValueWithElementsKind(glue, thisValue, *k);
BRANCH(TaggedIsHole(*kValue), &kValueIsHole, &callDispatch);
Bind(&kValueIsHole);
{
GateRef hasProp = CallCommonStub(glue, CommonStubCSigns::JSTaggedValueHasProperty,
{ glue, thisValue, IntToTaggedPtr(*k), GetCurrentGlobalEnv() });
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);
{
SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(0), *accumulator);
SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(1), *kValue);
SetValueToTaggedArray(VariableType::INT32(), glue, argList, Int32(2), IntToTaggedInt(*k));
SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(3), thisValue);
GateRef argv = PtrAdd(argList, IntPtr(TaggedArray::DATA_OFFSET));
JSCallArgs callArgs(JSCallMode::CALL_THIS_ARGV_WITH_RETURN);
callArgs.callThisArgvWithReturnArgs = { argsLength, argv, Undefined() };
CallStubBuilder callBuilder(this, glue, callbackFnHandle, argsLength, 0, nullptr, Circuit::NullGate(),
callArgs);
GateRef callResult = callBuilder.JSCallDispatch();
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 = CallCommonStub(glue, CommonStubCSigns::JSTaggedValueHasProperty,
{ glue, thisValue, IntToTaggedPtr(*k), GetCurrentGlobalEnv() });
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);
{
SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(0), *accumulator);
SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(1), *kValue);
SetValueToTaggedArray(VariableType::INT32(), glue, argList, Int32(2), IntToTaggedInt(*k));
SetValueToTaggedArray(VariableType::JS_ANY(), glue, argList, Int32(3), thisValue);
GateRef argv = PtrAdd(argList, IntPtr(TaggedArray::DATA_OFFSET));
JSCallArgs callArgs(JSCallMode::CALL_THIS_ARGV_WITH_RETURN);
callArgs.callThisArgvWithReturnArgs = { argsLength, argv, Undefined() };
CallStubBuilder callBuilder(this, glue, callbackFnHandle, argsLength, 0, nullptr, Circuit::NullGate(),
callArgs);
GateRef callResult = callBuilder.JSCallDispatch();
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)
{
auto env = GetEnvironment();
Label thisExists(env);
Label isHeapObject(env);
Label isJsArray(env);
Label notCOWArray(env);
BRANCH(TaggedIsUndefinedOrNull(thisValue), slowPath, &thisExists);
Bind(&thisExists);
BRANCH(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath);
Bind(&isHeapObject);
BRANCH(IsJsArray(glue, thisValue), &isJsArray, slowPath);
Bind(&isJsArray);
BRANCH(IsJsCOWArray(glue, thisValue), slowPath, ¬COWArray);
Bind(¬COWArray);
Label arg0HeapObject(env);
Label callable(env);
Label thisIsStable(env);
Label thisNotStable(env);
GateRef callbackFnHandle = GetCallArg0(numArgs);
BRANCH(TaggedIsHeapObject(callbackFnHandle), &arg0HeapObject, slowPath);
Bind(&arg0HeapObject);
BRANCH(IsCallable(glue, callbackFnHandle), &callable, slowPath);
Bind(&callable);
GateRef argHandle = GetCallArg1(numArgs);
DEFVARIABLE(i, VariableType::INT64(), Int64Sub(ZExtInt32ToInt64(GetArrayLength(thisValue)), Int64(1)));
BRANCH(IsStableJSArray(glue, thisValue), &thisIsStable, &thisNotStable);
Bind(&thisIsStable);
{
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 hasException0(env);
Label notHasException0(env);
Label useUndefined(env);
Label hasException1(env);
Label notHasException1(env);
BRANCH(IsStableJSArray(glue, thisValue), &nextStep, &thisNotStable);
Bind(&nextStep);
BRANCH(Int64LessThan(*i, Int64(0)), &loopExit, &next);
Bind(&next);
kValue = GetTaggedValueWithElementsKind(glue, thisValue, *i);
BRANCH(TaggedIsHole(*kValue), &kValueIsHole, &callDispatch);
Bind(&kValueIsHole);
{
GateRef hasProp = CallCommonStub(glue, CommonStubCSigns::JSTaggedValueHasProperty,
{ glue, thisValue, IntToTaggedPtr(*i), GetCurrentGlobalEnv() });
BRANCH(TaggedIsTrue(hasProp), &hasProperty, &useUndefined);
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), &useUndefined, &callDispatch);
Bind(&useUndefined);
kValue = Undefined();
Jump(&callDispatch);
}
}
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(HasPendingException(glue), &hasException1, ¬HasException1);
Bind(&hasException1);
{
result->WriteVariable(Exception());
Jump(exit);
}
Bind(¬HasException1);
{
DEFVARIABLE(newLen, VariableType::INT64(), ZExtInt32ToInt64(GetArrayLength(thisValue)));
Label checkRetValue(env);
Label find(env);
BRANCH(Int64LessThan(*newLen, Int64Add(*i, Int64(1))), &thisNotStable, &checkRetValue);
Bind(&checkRetValue);
BRANCH(TaggedIsTrue(FastToBoolean(glue, retValue)), &find, &loopEnd);
Bind(&find);
result->WriteVariable(IntToTaggedPtr(*i));
Jump(exit);
}
}
}
Bind(&loopEnd);
i = Int64Sub(*i, Int64(1));
LoopEnd(&loopHead);
Bind(&loopExit);
result->WriteVariable(IntToTaggedPtr(Int32(-1)));
Jump(exit);
}
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 hasException0(env);
Label notHasException0(env);
Label useUndefined(env);
Label callDispatch(env);
Label hasException1(env);
Label notHasException1(env);
BRANCH(Int64LessThan(*i, Int64(0)), &loopExit, &next);
Bind(&next);
GateRef hasProp = CallCommonStub(glue, CommonStubCSigns::JSTaggedValueHasProperty,
{ glue, thisValue, IntToTaggedPtr(*i), GetCurrentGlobalEnv() });
BRANCH(TaggedIsTrue(hasProp), &hasProperty, &useUndefined);
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), &useUndefined, &callDispatch);
Bind(&useUndefined);
kValue = Undefined();
Jump(&callDispatch);
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(HasPendingException(glue), &hasException1, ¬HasException1);
Bind(&hasException1);
{
result->WriteVariable(Exception());
Jump(exit);
}
Bind(¬HasException1);
{
Label find(env);
BRANCH(TaggedIsTrue(FastToBoolean(glue, retValue)), &find, &loopEnd);
Bind(&find);
result->WriteVariable(IntToTaggedPtr(*i));
Jump(exit);
}
}
}
Bind(&loopEnd);
i = Int64Sub(*i, Int64(1));
LoopEnd(&loopHead);
Bind(&loopExit);
result->WriteVariable(IntToTaggedPtr(Int32(-1)));
Jump(exit);
}
}
void BuiltinsArrayStubBuilder::FindLast(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 notCOWArray(env);
BRANCH(TaggedIsUndefinedOrNull(thisValue), slowPath, &thisExists);
Bind(&thisExists);
BRANCH(TaggedIsHeapObject(thisValue), &isHeapObject, slowPath);
Bind(&isHeapObject);
BRANCH(IsJsArray(glue, thisValue), &isJsArray, slowPath);
Bind(&isJsArray);
BRANCH(IsJsCOWArray(glue, thisValue), slowPath, ¬COWArray);
Bind(¬COWArray);
Label arg0HeapObject(env);
Label callable(env);
Label thisIsStable(env);
Label thisNotStable(env);
GateRef callbackFnHandle = GetCallArg0(numArgs);
BRANCH(TaggedIsHeapObject(callbackFnHandle), &arg0HeapObject, slowPath);
Bind(&arg0HeapObject);
BRANCH(IsCallable(glue, callbackFnHandle), &callable, slowPath);
Bind(&callable);
GateRef argHandle = GetCallArg1(numArgs);
DEFVARIABLE(i, VariableType::INT64(), Int64Sub(ZExtInt32ToInt64(GetArrayLength(thisValue)), Int64(1)));
BRANCH(IsStableJSArray(glue, thisValue), &thisIsStable, &thisNotStable);
Bind(&thisIsStable);
{
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 hasException0(env);
Label notHasException0(env);
Label useUndefined(env);
Label hasException1(env);
Label notHasException1(env);
BRANCH(IsStableJSArray(glue, thisValue), &nextStep, &thisNotStable);
Bind(&nextStep);
BRANCH(Int64LessThan(*i, Int64(0)), &loopExit, &next);
Bind(&next);
kValue = GetTaggedValueWithElementsKind(glue, thisValue, *i);
BRANCH(TaggedIsHole(*kValue), &kValueIsHole, &callDispatch);
Bind(&kValueIsHole);
{
GateRef hasProp = CallCommonStub(glue, CommonStubCSigns::JSTaggedValueHasProperty,
{ glue, thisValue, IntToTaggedPtr(*i), GetCurrentGlobalEnv() });
BRANCH(TaggedIsTrue(hasProp), &hasProperty, &useUndefined);
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), &useUndefined, &callDispatch);
Bind(&useUndefined);
kValue = Undefined();
Jump(&callDispatch);
}
}
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(HasPendingException(glue), &hasException1, ¬HasException1);
Bind(&hasException1);
{
result->WriteVariable(Exception());
Jump(exit);
}
Bind(¬HasException1);
{
DEFVARIABLE(newLen, VariableType::INT64(), ZExtInt32ToInt64(GetArrayLength(thisValue)));
Label checkRetValue(env);
Label find(env);
BRANCH(Int64LessThan(*newLen, Int64Add(*i, Int64(1))), &thisNotStable, &checkRetValue);
Bind(&checkRetValue);
BRANCH(TaggedIsTrue(FastToBoolean(glue, retValue)), &find, &loopEnd);
Bind(&find);
result->WriteVariable(*kValue);
Jump(exit);
}
}
}
Bind(&loopEnd);
i = Int64Sub(*i, Int64(1));
LoopEnd(&loopHead);
Bind(&loopExit);
Jump(exit);
}
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 hasException0(env);
Label notHasException0(env);
Label useUndefined(env);
Label callDispatch(env);
Label hasException1(env);
Label notHasException1(env);
BRANCH(Int64LessThan(*i, Int64(0)), &loopExit, &next);
Bind(&next);
GateRef hasProp = CallCommonStub(glue, CommonStubCSigns::JSTaggedValueHasProperty,
{ glue, thisValue, IntToTaggedPtr(*i), GetCurrentGlobalEnv() });
BRANCH(TaggedIsTrue(hasProp), &hasProperty, &useUndefined);
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), &useUndefined, &callDispatch);
Bind(&useUndefined);
{
kValue = Undefined();
Jump(&callDispatch);
}
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(HasPendingException(glue), &hasException1, ¬HasException1);
Bind(&hasException1);
{
result->WriteVariable(Exception());
Jump(exit);
}
Bind(¬HasException1);
{
Label find(env);
BRANCH(TaggedIsTrue(FastToBoolean(glue, retValue)), &find, &loopEnd);
Bind(&find);
result->WriteVariable(*kValue);
Jump(exit);
}
}
}
}
Bind(&loopEnd);
i = Int64Sub(*i, Int64(1));
LoopEnd(&loopHead);
Bind(&loopExit);
Jump(exit);
}
}
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);
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);
BRANCH_LIKELY(IsStableJSArray(glue, thisValue), &isStableInLoop, ¬StableInLoop);
Bind(¬StableInLoop);
{
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);
BRANCH_LIKELY(Int32UnsignedLessThan(*i, GetArrayLength(thisValue)), &getElement, &loopEnd);
Bind(&getElement);
kValue = GetTaggedValueWithElementsKind(glue, thisValue, *i);
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);
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);
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);
{
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)
{
auto env = GetEnvironment();
NewObjectStubBuilder newBuilder(this);
newBuilder.SetParameters(glue, 0);
GateRef len = TruncInt64ToInt32(argc);
GateRef elements = newBuilder.NewTaggedArray(glue, len);
DEFVARIABLE(i, VariableType::INT64(), Int64(0));
DEFVARIABLE(newHClass, VariableType::JS_ANY(), hclass);
DEFVARIABLE(value, VariableType::JS_ANY(), Undefined());
#if ECMASCRIPT_ENABLE_ELEMENTSKIND_ALWAY_GENERIC
DEFVARIABLE(elementKind, VariableType::INT32(), Int32(Elements::ToUint(ElementsKind::NONE)));
#else
DEFVARIABLE(elementKind, VariableType::INT32(), Int32(Elements::ToUint(ElementsKind::NONE)));
#endif
Label loopHead(env);
Label loopEnd(env);
Label setValue(env);
Label loopExit(env);
Jump(&loopHead);
LoopBegin(&loopHead);
{
BRANCH(Int64LessThan(*i, argc), &setValue, &loopExit);
Bind(&setValue);
Label isHole(env);
Label notHole(env);
value = GetArgFromArgv(glue, *i);
BRANCH(TaggedIsHole(*value), &isHole, ¬Hole);
Bind(&isHole);
value = TaggedUndefined();
Jump(¬Hole);
Bind(¬Hole);
elementKind = Int32Or(TaggedToElementKind(glue, *value), *elementKind);
SetValueToTaggedArray(VariableType::JS_ANY(), glue, elements, *i, *value);
i = Int64Add(*i, Int64(1));
Jump(&loopEnd);
}
Bind(&loopEnd);
LoopEnd(&loopHead);
Bind(&loopExit);
GateRef globalEnv = GetCurrentGlobalEnv();
GateRef noneHClass = GetGlobalEnvValue(VariableType::JS_ANY(), glue, globalEnv,
static_cast<size_t>(GlobalEnvField::ELEMENT_NONE_HCLASS_INDEX));
Label useElementsKindHClass(env);
Label createArray(env);
GateRef newHClassVal = *newHClass;
BRANCH_LIKELY(Equal(newHClassVal, noneHClass), &useElementsKindHClass, &createArray);
Bind(&useElementsKindHClass);
{
newHClass = GetElementsKindHClass(glue, *elementKind);
Jump(&createArray);
}
Bind(&createArray);
GateRef arr = newBuilder.NewJSObject(glue, *newHClass);
res->WriteVariable(arr);
GateRef lengthOffset = IntPtr(JSArray::LENGTH_OFFSET);
Store(VariableType::INT32(), glue, arr, lengthOffset, len);
GateRef accessor = GetGlobalConstantValue(VariableType::JS_ANY(), glue, ConstantIndex::ARRAY_LENGTH_ACCESSOR);
Store(VariableType::JS_ANY(), glue, arr,
IntPtr(JSArray::GetInlinedPropertyOffset(JSArray::LENGTH_INLINE_PROPERTY_INDEX)), accessor);
SetExtensibleToBitfield(glue, arr, true);
SetElementsArray(VariableType::JS_POINTER(), glue, arr, elements);
Jump(exit);
}
void BuiltinsArrayStubBuilder::GenArrayConstructor(GateRef glue, GateRef nativeCode,
GateRef func, GateRef newTarget, GateRef thisValue, GateRef numArgs)
{
auto env = GetEnvironment();
DEFVARIABLE(res, VariableType::JS_ANY(), Undefined());
Label newTargetIsHeapObject(env);
Label newTargetIsJSFunction(env);
Label slowPath(env);
Label slowPath1(env);
Label exit(env);
BRANCH(TaggedIsHeapObject(newTarget), &newTargetIsHeapObject, &slowPath);
Bind(&newTargetIsHeapObject);
BRANCH(IsJSFunction(glue, newTarget), &newTargetIsJSFunction, &slowPath);
Bind(&newTargetIsJSFunction);
{
Label fastGetHclass(env);
Label intialHClassIsHClass(env);
GateRef globalEnv = GetCurrentGlobalEnv();
auto arrayFunc = GetGlobalEnvValue(VariableType::JS_ANY(), glue, globalEnv,
GlobalEnv::ARRAY_FUNCTION_INDEX);
BRANCH(Equal(arrayFunc, newTarget), &fastGetHclass, &slowPath1);
Bind(&fastGetHclass);
GateRef initialHClass =
Load(VariableType::JS_ANY(), glue, newTarget, IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET));
DEFVARIABLE(arrayLength, VariableType::INT64(), Int64(0));
BRANCH(IsJSHClass(glue, initialHClass), &intialHClassIsHClass, &slowPath1);
Bind(&intialHClassIsHClass);
{
Label noArg(env);
Label hasArg(env);
Label arrayCreate(env);
BRANCH(Int64Equal(numArgs, IntPtr(0)), &noArg, &hasArg);
Bind(&noArg);
{
Jump(&arrayCreate);
}
Bind(&hasArg);
{
Label hasOneArg(env);
Label multiArg(env);
BRANCH(Int64Equal(numArgs, IntPtr(1)), &hasOneArg, &multiArg);
Bind(&hasOneArg);
{
Label argIsNumber(env);
GateRef arg0 = GetArgFromArgv(glue, IntPtr(0), numArgs, true);
BRANCH(TaggedIsNumber(arg0), &argIsNumber, &slowPath);
Bind(&argIsNumber);
{
Label argIsInt(env);
Label argIsDouble(env);
BRANCH(TaggedIsInt(arg0), &argIsInt, &argIsDouble);
Bind(&argIsInt);
{
Label validIntLength(env);
GateRef intLen = GetInt64OfTInt(arg0);
GateRef isGEZero = Int64GreaterThanOrEqual(intLen, Int64(0));
GateRef isLEMaxLen = Int64LessThanOrEqual(intLen, Int64(JSArray::MAX_ARRAY_INDEX));
BRANCH(BitAnd(isGEZero, isLEMaxLen), &validIntLength, &slowPath);
Bind(&validIntLength);
{
arrayLength = intLen;
Jump(&arrayCreate);
}
}
Bind(&argIsDouble);
{
Label validDoubleLength(env);
GateRef doubleLength = GetDoubleOfTDouble(arg0);
GateRef doubleToInt = DoubleToInt(glue, doubleLength);
GateRef intToDouble = CastInt64ToFloat64(SExtInt32ToInt64(doubleToInt));
GateRef doubleEqual = DoubleEqual(doubleLength, intToDouble);
GateRef doubleLEMaxLen =
DoubleLessThanOrEqual(doubleLength, Double(JSArray::MAX_ARRAY_INDEX));
BRANCH(BitAnd(doubleEqual, doubleLEMaxLen), &validDoubleLength, &slowPath);
Bind(&validDoubleLength);
{
arrayLength = SExtInt32ToInt64(doubleToInt);
Jump(&arrayCreate);
}
}
}
}
Bind(&multiArg);
{
Label lengthValid(env);
BRANCH(Int64LessThan(numArgs, IntPtr(JSObject::MAX_GAP)), &lengthValid, &slowPath);
Bind(&lengthValid);
{
FastCreateArrayWithArgv(glue, &res, numArgs, initialHClass, &exit);
}
}
}
Bind(&arrayCreate);
{
Label lengthValid(env);
BRANCH(Int64GreaterThan(*arrayLength, Int64(JSObject::MAX_GAP)), &slowPath, &lengthValid);
Bind(&lengthValid);
{
NewObjectStubBuilder newBuilder(this, globalEnv);
newBuilder.SetParameters(glue, 0);
res = newBuilder.NewJSArrayWithSize(initialHClass, *arrayLength);
GateRef lengthOffset = IntPtr(JSArray::LENGTH_OFFSET);
Store(VariableType::INT32(), glue, *res, lengthOffset, TruncInt64ToInt32(*arrayLength));
GateRef accessor = GetGlobalConstantValue(VariableType::JS_ANY(), glue,
ConstantIndex::ARRAY_LENGTH_ACCESSOR);
Store(VariableType::JS_ANY(), glue, *res,
IntPtr(JSArray::GetInlinedPropertyOffset(JSArray::LENGTH_INLINE_PROPERTY_INDEX)), accessor);
SetExtensibleToBitfield(glue, *res, true);
Jump(&exit);
}
}
}
Bind(&slowPath1);
{
GateRef argv = GetArgv();
res = CallBuiltinRuntimeWithNewTarget(glue,
{ glue, nativeCode, func, thisValue, numArgs, argv, newTarget });
Jump(&exit);
}
}
Bind(&slowPath);
{
GateRef argv = GetArgv();
res = CallBuiltinRuntime(glue, { glue, nativeCode, func, thisValue, numArgs, argv }, true);
Jump(&exit);
}
Bind(&exit);
Return(*res);
}
void BuiltinsArrayStubBuilder::IsArray(GateRef glue, [[maybe_unused]] GateRef thisValue,
GateRef numArgs, Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
GateRef obj = GetCallArg0(numArgs);
Label isHeapObj(env);
Label notHeapObj(env);
BRANCH(TaggedIsHeapObject(obj), &isHeapObj, ¬HeapObj);
Bind(&isHeapObj);
{
Label isJSArray(env);
Label notJSArray(env);
GateRef objectType = GetObjectType(LoadHClass(glue, obj));
BRANCH(Int32Equal(objectType, Int32(static_cast<int32_t>(JSType::JS_ARRAY))), &isJSArray, ¬JSArray);
Bind(&isJSArray);
{
result->WriteVariable(TaggedTrue());
Jump(exit);
}
Bind(¬JSArray);
BRANCH(Int32Equal(objectType, Int32(static_cast<int32_t>(JSType::JS_PROXY))), slowPath, ¬HeapObj);
}
Bind(¬HeapObj);
{
result->WriteVariable(TaggedFalse());
Jump(exit);
}
}
}