* 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/compiler/builtins/builtins_number_stub_builder.h"
#include "ecmascript/compiler/new_object_stub_builder.h"
#include "ecmascript/js_primitive_ref.h"
namespace panda::ecmascript::kungfu {
void BuiltinsNumberStubBuilder::ParseFloat(Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label definedMsg(env);
Label undefinedMsg(env);
GateRef msg = GetCallArg0(numArgs_);
BRANCH(TaggedIsUndefined(msg), &undefinedMsg, &definedMsg);
Bind(&undefinedMsg);
{
*result = DoubleToTaggedDoublePtr(Double(base::NAN_VALUE));
Jump(exit);
}
Bind(&definedMsg);
{
Label heapObj(env);
Label stringObj(env);
BRANCH(TaggedIsHeapObject(msg), &heapObj, slowPath);
Bind(&heapObj);
BRANCH(IsString(glue_, msg), &stringObj, slowPath);
Bind(&stringObj);
{
*result = CallNGCRuntime(glue_, RTSTUB_ID(NumberHelperStringToDouble), { glue_, msg });
Jump(exit);
}
}
}
void BuiltinsNumberStubBuilder::ParseInt(Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label msgIsString(env);
Label radixIsSpecial(env);
Label radixIsSpecialInt(env);
DEFVARIABLE(radix, VariableType::INT32(), Int32(0));
GateRef msg = GetCallArg0(numArgs_);
GateRef arg2 = GetCallArg1(numArgs_);
Branch(TaggedIsString(glue_, msg), &msgIsString, slowPath);
Bind(&msgIsString);
Branch(TaggedIsUndefined(arg2), &radixIsSpecialInt, &radixIsSpecial);
Bind(&radixIsSpecial);
{
Label radixIsInt(env);
Branch(TaggedIsInt(arg2), &radixIsInt, slowPath);
Bind(&radixIsInt);
{
radix = GetInt32OfTInt(arg2);
Jump(&radixIsSpecialInt);
}
}
Bind(&radixIsSpecialInt);
{
*result = CallNGCRuntime(glue_, RTSTUB_ID(StringToNumber), { glue_, msg, *radix });
Jump(exit);
}
}
void BuiltinsNumberStubBuilder::IsFinite(Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
GateRef number = GetCallArg0(numArgs_);
Label noSlowPath(env);
BRANCH(False(), slowPath, &noSlowPath);
Bind(&noSlowPath);
Label retTrue(env);
Label retFalse(env);
Label isNotInt(env);
BRANCH(TaggedIsInt(number), &retTrue, &isNotInt);
Bind(&isNotInt);
{
Label isDouble(env);
BRANCH(TaggedIsDouble(number), &isDouble, &retFalse);
Bind(&isDouble);
{
GateRef f = GetDoubleOfTDouble(number);
BRANCH(DoubleIsNanOrInf(f), &retFalse, &retTrue);
}
}
Bind(&retTrue);
{
*result = TaggedTrue();
Jump(exit);
}
Bind(&retFalse);
{
*result = TaggedFalse();
Jump(exit);
}
}
void BuiltinsNumberStubBuilder::IsNaN(Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
GateRef number = GetCallArg0(numArgs_);
Label noSlowPath(env);
BRANCH(False(), slowPath, &noSlowPath);
Bind(&noSlowPath);
Label retTrue(env);
Label retFalse(env);
Label isDouble(env);
BRANCH(TaggedIsDouble(number), &isDouble, &retFalse);
Bind(&isDouble);
BRANCH(DoubleIsNAN(GetDoubleOfTDouble(number)), &retTrue, &retFalse);
Bind(&retTrue);
{
*result = TaggedTrue();
Jump(exit);
}
Bind(&retFalse);
{
*result = TaggedFalse();
Jump(exit);
}
}
void BuiltinsNumberStubBuilder::IsInteger(Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
GateRef number = GetCallArg0(numArgs_);
Label noSlowPath(env);
BRANCH(False(), slowPath, &noSlowPath);
Bind(&noSlowPath);
Label retTrue(env);
Label retFalse(env);
Label isNotInt(env);
BRANCH(TaggedIsInt(number), &retTrue, &isNotInt);
Bind(&isNotInt);
{
Label isDouble(env);
BRANCH(TaggedIsDouble(number), &isDouble, &retFalse);
Bind(&isDouble);
BRANCH(DoubleIsInteger(GetDoubleOfTDouble(number)), &retTrue, &retFalse);
}
Bind(&retTrue);
{
*result = TaggedTrue();
Jump(exit);
}
Bind(&retFalse);
{
*result = TaggedFalse();
Jump(exit);
}
}
void BuiltinsNumberStubBuilder::IsSafeInteger(Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
GateRef number = GetCallArg0(numArgs_);
Label noSlowPath(env);
BRANCH(False(), slowPath, &noSlowPath);
Bind(&noSlowPath);
Label retTrue(env);
Label retFalse(env);
Label isNotInt(env);
BRANCH(TaggedIsInt(number), &retTrue, &isNotInt);
Bind(&isNotInt);
{
Label isDouble(env);
BRANCH(TaggedIsDouble(number), &isDouble, &retFalse);
Bind(&isDouble);
{
Label isNotNanOrInf(env);
GateRef f = GetDoubleOfTDouble(number);
BRANCH(DoubleIsNanOrInf(f), &retFalse, &isNotNanOrInf);
Bind(&isNotNanOrInf);
{
Label checkSafe(env);
GateRef truncated = ChangeInt32ToFloat64(TruncFloatToInt64(f));
BRANCH(DoubleEqual(f, truncated), &checkSafe, &retFalse);
Bind(&checkSafe);
BRANCH(DoubleLessThanOrEqual(DoubleAbs(f), Double(base::MAX_SAFE_INTEGER)), &retTrue, &retFalse);
}
}
}
Bind(&retTrue);
{
*result = TaggedTrue();
Jump(exit);
}
Bind(&retFalse);
{
*result = TaggedFalse();
Jump(exit);
}
}
void BuiltinsNumberStubBuilder::GenNumberConstructor(GateRef nativeCode, GateRef func, GateRef newTarget)
{
auto env = GetEnvironment();
DEFVARIABLE(res, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(numberValue, VariableType::JS_ANY(), IntToTaggedPtr(IntPtr(0)));
Label thisCollectionObj(env);
Label slowPath(env);
Label exit(env);
Label hasArg(env);
Label numberCreate(env);
Label newTargetIsHeapObject(env);
BRANCH(TaggedIsHeapObject(newTarget), &newTargetIsHeapObject, &slowPath);
Bind(&newTargetIsHeapObject);
BRANCH(Int64GreaterThan(numArgs_, IntPtr(0)), &hasArg, &numberCreate);
Bind(&hasArg);
{
GateRef value = GetArgFromArgv(glue_, Int32(0));
Label number(env);
BRANCH(TaggedIsNumber(value), &number, &slowPath);
Bind(&number);
{
numberValue = value;
res = value;
Jump(&numberCreate);
}
}
Bind(&numberCreate);
Label newObj(env);
Label newTargetIsJSFunction(env);
BRANCH(TaggedIsUndefined(newTarget), &exit, &newObj);
Bind(&newObj);
{
BRANCH(IsJSFunction(glue_, newTarget), &newTargetIsJSFunction, &slowPath);
Bind(&newTargetIsJSFunction);
{
Label intialHClassIsHClass(env);
GateRef initialHClass = Load(VariableType::JS_ANY(), glue_, newTarget,
IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET));
BRANCH(IsJSHClass(glue_, initialHClass), &intialHClassIsHClass, &slowPath);
Bind(&intialHClassIsHClass);
{
NewObjectStubBuilder newBuilder(this);
newBuilder.SetParameters(glue_, 0);
Label afterNew(env);
newBuilder.NewJSObject(&res, &afterNew, initialHClass);
Bind(&afterNew);
{
GateRef valueOffset = IntPtr(JSPrimitiveRef::VALUE_OFFSET);
Store(VariableType::INT64(), glue_, *res, valueOffset, *numberValue);
Jump(&exit);
}
}
}
}
Bind(&slowPath);
{
GateRef argv = GetArgv();
res = CallBuiltinRuntimeWithNewTarget(glue_, {glue_, nativeCode, func, thisValue_, numArgs_, argv, newTarget});
Jump(&exit);
}
Bind(&exit);
Return(*res);
}
void BuiltinsNumberStubBuilder::ToStringFunc(Variable *result, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label definedMsg(env);
Label undefinedMsg(env);
Label thisIsInt(env);
Label msgIsInt(env);
BRANCH(TaggedIsInt(thisValue_), &thisIsInt, slowPath);
Bind(&thisIsInt);
GateRef thisValueInt = GetInt32OfTInt(thisValue_);
GateRef msg = GetCallArg0(numArgs_);
BRANCH(TaggedIsUndefined(msg), &undefinedMsg, &definedMsg);
Bind(&undefinedMsg);
{
*result = NumberToString(thisValueInt, Int32(10));
Jump(exit);
}
Bind(&definedMsg);
BRANCH(TaggedIsInt(msg), &msgIsInt, slowPath);
Bind(&msgIsInt);
{
Label throwError(env);
Label notThrowError(env);
GateRef msgValue = GetInt32OfTInt(msg);
GateRef outOfRange = BitOr(Int32LessThan(msgValue, Int32(base::MIN_RADIX)),
Int32GreaterThan(msgValue, Int32(base::MAX_RADIX)));
BRANCH(outOfRange, &throwError, ¬ThrowError);
Bind(&throwError);
{
GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(InvalidRadixLength));
CallRuntimeWithGlobalEnv(glue_, GetCurrentGlobalEnv(),
RTSTUB_ID(ThrowRangeError), { IntToTaggedInt(taggedId) });
Jump(exit);
}
Bind(¬ThrowError);
{
*result = NumberToString(thisValueInt, msgValue);
Jump(exit);
}
}
}
GateRef BuiltinsNumberStubBuilder::NumberToString(GateRef number, GateRef radix)
{
auto env = GetEnvironment();
Label subentry(env);
env->SubCfgEntry(&subentry);
DEFVARIABLE(result, VariableType::JS_POINTER(), Hole());
DEFVARIABLE(n, VariableType::INT32(), number);
Label exit(env);
Label numIsNegative(env);
Label numNotNegative(env);
Label afterFast(env);
Label afterNew(env);
GateRef isNegative = Int32LessThan(number, Int32(0));
BRANCH(isNegative, &numIsNegative, &numNotNegative);
Bind(&numIsNegative);
{
n = Int32Sub(Int32(0), *n);
Jump(&afterFast);
}
Bind(&numNotNegative);
{
Label thisIsZero(env);
Label thisNotZero(env);
Label thisIsSingle(env);
Label thisNotSingle(env);
BRANCH(Int32Equal(number, Int32(0)), &thisIsZero, &thisNotZero);
Bind(&thisIsZero);
{
result = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, ConstantIndex::ZERO_INDEX);
Jump(&exit);
}
Bind(&thisNotZero);
{
BRANCH(Int32LessThan(number, radix), &thisIsSingle, &afterFast);
Bind(&thisIsSingle);
GateRef singleCharTable = GetSingleCharTable(glue_);
GateRef index = ToCharCode(number);
result = GetValueFromTaggedArray(glue_, singleCharTable, index);
Jump(&exit);
}
}
Bind(&afterFast);
{
DEFVARIABLE(temp, VariableType::INT32(), *n);
DEFVARIABLE(length, VariableType::INT32(), Int32(0));
Label lenAddOne(env);
Label lenNotAddOne(env);
BRANCH(isNegative, &lenAddOne, &lenNotAddOne);
Bind(&lenAddOne);
{
length = Int32Add(*length, Int32(1));
Jump(&lenNotAddOne);
}
Bind(&lenNotAddOne);
{
Label loopHead(env);
Label loopEnd(env);
Label next(env);
Label loopExit(env);
Jump(&loopHead);
LoopBegin(&loopHead);
{
BRANCH(Int32GreaterThan(*temp, Int32(0)), &next, &loopExit);
Bind(&next);
{
temp = Int32Div(*temp, radix);
length = Int32Add(*length, Int32(1));
Jump(&loopEnd);
}
}
Bind(&loopEnd);
LoopEnd(&loopHead);
Bind(&loopExit);
{
NewObjectStubBuilder newBuilder(this);
newBuilder.SetParameters(glue_, 0);
newBuilder.AllocLineStringObject(&result, &afterNew, *length, true);
Bind(&afterNew);
{
GateRef dst = ChangeTaggedPointerToInt64(PtrAdd(*result, IntPtr(LineString::DATA_OFFSET)));
DEFVARIABLE(cursor, VariableType::INT32(), Int32Sub(*length, Int32(1)));
DEFVARIABLE(digit, VariableType::INT32(), Int32(0));
DEFVARIABLE(dstTmp, VariableType::NATIVE_POINTER(), dst);
dstTmp = PtrAdd(*dstTmp, PtrMul(ZExtInt32ToPtr(*cursor), IntPtr(sizeof(uint8_t))));
Label loopHead1(env);
Label loopEnd1(env);
Label next1(env);
Label loopExit1(env);
Jump(&loopHead1);
LoopBegin(&loopHead1);
{
BRANCH(Int32GreaterThan(*n, Int32(0)), &next1, &loopExit1);
Bind(&next1);
{
digit = Int32Mod(*n, radix);
n = Int32Div(*n, radix);
GateRef digitChar = ToCharCode(*digit);
Store(VariableType::INT8(), glue_, *dstTmp, IntPtr(0), TruncInt32ToInt8(digitChar));
cursor = Int32Sub(*cursor, Int32(1));
Jump(&loopEnd1);
}
}
Bind(&loopEnd1);
dstTmp = PtrSub(*dstTmp, IntPtr(sizeof(uint8_t)));
LoopEnd(&loopHead1);
Bind(&loopExit1);
{
Label strInsertSign(env);
Label strNotInsertSign(env);
BRANCH(isNegative, &strInsertSign, &exit);
Bind(&strInsertSign);
{
dstTmp = PtrSub(*dstTmp, IntPtr(sizeof(uint8_t)));
Store(VariableType::INT8(), glue_, dst, IntPtr(0), Int8(45));
Jump(&exit);
}
}
}
}
}
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
}