1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "ecmascript/compiler/builtins/builtins_number_stub_builder.h" 17 18#include "ecmascript/builtins/builtins_number.h" 19#include "ecmascript/compiler/new_object_stub_builder.h" 20#include "ecmascript/compiler/stub_builder-inl.h" 21#include "ecmascript/js_arguments.h" 22#include "ecmascript/js_primitive_ref.h" 23#include "ecmascript/tagged_dictionary.h" 24 25namespace panda::ecmascript::kungfu { 26void BuiltinsNumberStubBuilder::ParseFloat(Variable *result, Label *exit, Label *slowPath) 27{ 28 auto env = GetEnvironment(); 29 Label definedMsg(env); 30 Label undefinedMsg(env); 31 GateRef msg = GetCallArg0(numArgs_); 32 BRANCH(TaggedIsUndefined(msg), &undefinedMsg, &definedMsg); 33 Bind(&undefinedMsg); 34 { 35 *result = DoubleToTaggedDoublePtr(Double(base::NAN_VALUE)); 36 Jump(exit); 37 } 38 Bind(&definedMsg); 39 { 40 Label heapObj(env); 41 Label stringObj(env); 42 BRANCH(TaggedIsHeapObject(msg), &heapObj, slowPath); 43 Bind(&heapObj); 44 BRANCH(IsString(msg), &stringObj, slowPath); 45 Bind(&stringObj); 46 { 47 *result = CallNGCRuntime(glue_, RTSTUB_ID(NumberHelperStringToDouble), { msg }); 48 Jump(exit); 49 } 50 } 51} 52 53void BuiltinsNumberStubBuilder::ParseInt(Variable *result, Label *exit, Label *slowPath) 54{ 55 auto env = GetEnvironment(); 56 Label msgIsString(env); 57 Label radixIsSpecial(env); 58 Label radixIsSpecialInt(env); 59 60 DEFVARIABLE(radix, VariableType::INT32(), Int32(0)); 61 GateRef msg = GetCallArg0(numArgs_); 62 GateRef arg2 = GetCallArg1(numArgs_); 63 // ToString maybe throw exception. 64 Branch(TaggedIsString(msg), &msgIsString, slowPath); 65 Bind(&msgIsString); 66 Branch(TaggedIsUndefined(arg2), &radixIsSpecialInt, &radixIsSpecial); 67 68 Bind(&radixIsSpecial); 69 { 70 Label radixIsInt(env); 71 // ToInt maybe throw exception. 72 Branch(TaggedIsInt(arg2), &radixIsInt, slowPath); 73 Bind(&radixIsInt); 74 { 75 radix = GetInt32OfTInt(arg2); 76 Jump(&radixIsSpecialInt); 77 } 78 } 79 Bind(&radixIsSpecialInt); 80 { 81 *result = CallNGCRuntime(glue_, RTSTUB_ID(StringToNumber), { msg, *radix }); 82 Jump(exit); 83 } 84} 85 86void BuiltinsNumberStubBuilder::IsFinite(Variable *result, Label *exit, Label *slowPath) 87{ 88 auto env = GetEnvironment(); 89 GateRef number = GetCallArg0(numArgs_); 90 91 // In this method, we actually don't need slow path. 92 // The following code is for passing the verification phase. 93 Label noSlowPath(env); 94 BRANCH(False(), slowPath, &noSlowPath); 95 Bind(&noSlowPath); 96 97 Label retTrue(env); 98 Label retFalse(env); 99 100 Label isNotInt(env); 101 BRANCH(TaggedIsInt(number), &retTrue, &isNotInt); 102 Bind(&isNotInt); 103 { 104 Label isDouble(env); 105 BRANCH(TaggedIsDouble(number), &isDouble, &retFalse); 106 Bind(&isDouble); 107 { 108 GateRef f = GetDoubleOfTDouble(number); 109 BRANCH(DoubleIsNanOrInf(f), &retFalse, &retTrue); 110 } 111 } 112 113 Bind(&retTrue); 114 { 115 *result = TaggedTrue(); 116 Jump(exit); 117 } 118 Bind(&retFalse); 119 { 120 *result = TaggedFalse(); 121 Jump(exit); 122 } 123} 124 125void BuiltinsNumberStubBuilder::IsNaN(Variable *result, Label *exit, Label *slowPath) 126{ 127 auto env = GetEnvironment(); 128 GateRef number = GetCallArg0(numArgs_); 129 130 // In this method, we actually don't need slow path. 131 // The following code is for passing the verification phase. 132 Label noSlowPath(env); 133 BRANCH(False(), slowPath, &noSlowPath); 134 Bind(&noSlowPath); 135 136 Label retTrue(env); 137 Label retFalse(env); 138 139 Label isDouble(env); 140 BRANCH(TaggedIsDouble(number), &isDouble, &retFalse); 141 Bind(&isDouble); 142 BRANCH(DoubleIsNAN(GetDoubleOfTDouble(number)), &retTrue, &retFalse); 143 144 Bind(&retTrue); 145 { 146 *result = TaggedTrue(); 147 Jump(exit); 148 } 149 Bind(&retFalse); 150 { 151 *result = TaggedFalse(); 152 Jump(exit); 153 } 154} 155 156void BuiltinsNumberStubBuilder::IsInteger(Variable *result, Label *exit, Label *slowPath) 157{ 158 auto env = GetEnvironment(); 159 GateRef number = GetCallArg0(numArgs_); 160 161 // In this method, we actually don't need slow path. 162 // The following code is for passing the verification phase. 163 Label noSlowPath(env); 164 BRANCH(False(), slowPath, &noSlowPath); 165 Bind(&noSlowPath); 166 167 Label retTrue(env); 168 Label retFalse(env); 169 170 Label isNotInt(env); 171 BRANCH(TaggedIsInt(number), &retTrue, &isNotInt); 172 Bind(&isNotInt); 173 { 174 Label isDouble(env); 175 BRANCH(TaggedIsDouble(number), &isDouble, &retFalse); 176 Bind(&isDouble); 177 BRANCH(DoubleIsInteger(GetDoubleOfTDouble(number)), &retTrue, &retFalse); 178 } 179 180 Bind(&retTrue); 181 { 182 *result = TaggedTrue(); 183 Jump(exit); 184 } 185 Bind(&retFalse); 186 { 187 *result = TaggedFalse(); 188 Jump(exit); 189 } 190} 191 192void BuiltinsNumberStubBuilder::IsSafeInteger(Variable *result, Label *exit, Label *slowPath) 193{ 194 auto env = GetEnvironment(); 195 GateRef number = GetCallArg0(numArgs_); 196 197 // In this method, we actually don't need slow path. 198 // The following code is for passing the verification phase. 199 Label noSlowPath(env); 200 BRANCH(False(), slowPath, &noSlowPath); 201 Bind(&noSlowPath); 202 203 Label retTrue(env); 204 Label retFalse(env); 205 206 Label isNotInt(env); 207 BRANCH(TaggedIsInt(number), &retTrue, &isNotInt); 208 Bind(&isNotInt); 209 { 210 Label isDouble(env); 211 BRANCH(TaggedIsDouble(number), &isDouble, &retFalse); 212 Bind(&isDouble); 213 { 214 Label isNotNanOrInf(env); 215 GateRef f = GetDoubleOfTDouble(number); 216 BRANCH(DoubleIsNanOrInf(f), &retFalse, &isNotNanOrInf); 217 Bind(&isNotNanOrInf); 218 { 219 Label checkSafe(env); 220 GateRef truncated = ChangeInt32ToFloat64(TruncFloatToInt64(f)); 221 BRANCH(DoubleEqual(f, truncated), &checkSafe, &retFalse); 222 Bind(&checkSafe); 223 BRANCH(DoubleLessThanOrEqual(DoubleAbs(f), Double(base::MAX_SAFE_INTEGER)), &retTrue, &retFalse); 224 } 225 } 226 } 227 228 Bind(&retTrue); 229 { 230 *result = TaggedTrue(); 231 Jump(exit); 232 } 233 Bind(&retFalse); 234 { 235 *result = TaggedFalse(); 236 Jump(exit); 237 } 238} 239 240void BuiltinsNumberStubBuilder::GenNumberConstructor(GateRef nativeCode, GateRef func, GateRef newTarget) 241{ 242 auto env = GetEnvironment(); 243 DEFVARIABLE(res, VariableType::JS_ANY(), Undefined()); 244 DEFVARIABLE(numberValue, VariableType::JS_ANY(), IntToTaggedPtr(IntPtr(0))); 245 Label thisCollectionObj(env); 246 Label slowPath(env); 247 Label slowPath1(env); 248 Label exit(env); 249 250 Label hasArg(env); 251 Label numberCreate(env); 252 Label newTargetIsHeapObject(env); 253 BRANCH(TaggedIsHeapObject(newTarget), &newTargetIsHeapObject, &slowPath); 254 Bind(&newTargetIsHeapObject); 255 BRANCH(Int64GreaterThan(numArgs_, IntPtr(0)), &hasArg, &numberCreate); 256 Bind(&hasArg); 257 { 258 GateRef value = GetArgFromArgv(Int32(0)); 259 Label number(env); 260 BRANCH(TaggedIsNumber(value), &number, &slowPath); 261 Bind(&number); 262 { 263 numberValue = value; 264 res = value; 265 Jump(&numberCreate); 266 } 267 } 268 269 Bind(&numberCreate); 270 Label newObj(env); 271 Label newTargetIsJSFunction(env); 272 BRANCH(TaggedIsUndefined(newTarget), &exit, &newObj); 273 Bind(&newObj); 274 { 275 BRANCH(IsJSFunction(newTarget), &newTargetIsJSFunction, &slowPath); 276 Bind(&newTargetIsJSFunction); 277 { 278 Label intialHClassIsHClass(env); 279 GateRef intialHClass = Load(VariableType::JS_ANY(), newTarget, 280 IntPtr(JSFunction::PROTO_OR_DYNCLASS_OFFSET)); 281 BRANCH(IsJSHClass(intialHClass), &intialHClassIsHClass, &slowPath1); 282 Bind(&intialHClassIsHClass); 283 { 284 NewObjectStubBuilder newBuilder(this); 285 newBuilder.SetParameters(glue_, 0); 286 Label afterNew(env); 287 newBuilder.NewJSObject(&res, &afterNew, intialHClass); 288 Bind(&afterNew); 289 { 290 GateRef valueOffset = IntPtr(JSPrimitiveRef::VALUE_OFFSET); 291 Store(VariableType::INT64(), glue_, *res, valueOffset, *numberValue); 292 Jump(&exit); 293 } 294 } 295 Bind(&slowPath1); 296 { 297 GateRef argv = GetArgv(); 298 res = CallBuiltinRuntimeWithNewTarget(glue_, 299 { glue_, nativeCode, func, thisValue_, numArgs_, argv, newTarget }); 300 Jump(&exit); 301 } 302 } 303 } 304 305 Bind(&slowPath); 306 { 307 GateRef argv = GetArgv(); 308 res = CallBuiltinRuntime(glue_, { glue_, nativeCode, func, thisValue_, numArgs_, argv }, true); 309 Jump(&exit); 310 } 311 Bind(&exit); 312 Return(*res); 313} 314 315void BuiltinsNumberStubBuilder::ToString(Variable *result, Label *exit, Label *slowPath) 316{ 317 auto env = GetEnvironment(); 318 Label definedMsg(env); 319 Label undefinedMsg(env); 320 Label thisIsInt(env); 321 Label msgIsInt(env); 322 BRANCH(TaggedIsInt(thisValue_), &thisIsInt, slowPath); 323 Bind(&thisIsInt); 324 GateRef thisValueInt = GetInt32OfTInt(thisValue_); 325 GateRef msg = GetCallArg0(numArgs_); 326 BRANCH(TaggedIsUndefined(msg), &undefinedMsg, &definedMsg); 327 Bind(&undefinedMsg); 328 { 329 *result = NumberToString(thisValueInt, Int32(10)); // 10: means radix 330 Jump(exit); 331 } 332 Bind(&definedMsg); 333 BRANCH(TaggedIsInt(msg), &msgIsInt, slowPath); 334 Bind(&msgIsInt); 335 { 336 Label throwError(env); 337 Label notThrowError(env); 338 GateRef msgValue = GetInt32OfTInt(msg); 339 GateRef outOfRange = BitOr(Int32LessThan(msgValue, Int32(base::MIN_RADIX)), 340 Int32GreaterThan(msgValue, Int32(base::MAX_RADIX))); 341 BRANCH(outOfRange, &throwError, ¬ThrowError); 342 Bind(&throwError); 343 { 344 GateRef taggedId = Int32(GET_MESSAGE_STRING_ID(InvalidRadixLength)); 345 CallRuntime(glue_, RTSTUB_ID(ThrowRangeError), { IntToTaggedInt(taggedId) }); 346 Jump(exit); 347 } 348 Bind(¬ThrowError); 349 { 350 *result = NumberToString(thisValueInt, msgValue); 351 Jump(exit); 352 } 353 } 354} 355 356GateRef BuiltinsNumberStubBuilder::NumberToString(GateRef number, GateRef radix) 357{ 358 auto env = GetEnvironment(); 359 Label subentry(env); 360 env->SubCfgEntry(&subentry); 361 DEFVARIABLE(result, VariableType::JS_POINTER(), Hole()); 362 DEFVARIABLE(n, VariableType::INT32(), number); 363 364 Label exit(env); 365 Label numIsNegative(env); 366 Label numNotNegative(env); 367 Label afterFast(env); 368 Label afterNew(env); 369 GateRef isNegative = Int32LessThan(number, Int32(0)); 370 BRANCH(isNegative, &numIsNegative, &numNotNegative); 371 Bind(&numIsNegative); 372 { 373 n = Int32Sub(Int32(0), *n); 374 Jump(&afterFast); 375 } 376 Bind(&numNotNegative); 377 { 378 Label thisIsZero(env); 379 Label thisNotZero(env); 380 Label thisIsSingle(env); 381 Label thisNotSingle(env); 382 BRANCH(Int32Equal(number, Int32(0)), &thisIsZero, &thisNotZero); 383 Bind(&thisIsZero); 384 { 385 result = GetGlobalConstantValue(VariableType::JS_POINTER(), glue_, ConstantIndex::ZERO_INDEX); 386 Jump(&exit); 387 } 388 Bind(&thisNotZero); 389 { 390 BRANCH(Int32LessThan(number, radix), &thisIsSingle, &afterFast); 391 Bind(&thisIsSingle); 392 GateRef singleCharTable = GetSingleCharTable(glue_); 393 GateRef index = ToCharCode(number); 394 result = GetValueFromTaggedArray(singleCharTable, index); 395 Jump(&exit); 396 } 397 } 398 Bind(&afterFast); 399 { 400 DEFVARIABLE(temp, VariableType::INT32(), *n); 401 DEFVARIABLE(length, VariableType::INT32(), Int32(0)); 402 Label lenAddOne(env); 403 Label lenNotAddOne(env); 404 BRANCH(isNegative, &lenAddOne, &lenNotAddOne); 405 Bind(&lenAddOne); 406 { 407 length = Int32Add(*length, Int32(1)); 408 Jump(&lenNotAddOne); 409 } 410 Bind(&lenNotAddOne); 411 { 412 Label loopHead(env); 413 Label loopEnd(env); 414 Label next(env); 415 Label loopExit(env); 416 Jump(&loopHead); 417 LoopBegin(&loopHead); 418 { 419 BRANCH(Int32GreaterThan(*temp, Int32(0)), &next, &loopExit); 420 Bind(&next); 421 { 422 temp = Int32Div(*temp, radix); 423 length = Int32Add(*length, Int32(1)); 424 Jump(&loopEnd); 425 } 426 } 427 Bind(&loopEnd); 428 LoopEnd(&loopHead, env, glue_); 429 Bind(&loopExit); 430 { 431 NewObjectStubBuilder newBuilder(this); 432 newBuilder.SetParameters(glue_, 0); 433 newBuilder.AllocLineStringObject(&result, &afterNew, *length, true); 434 Bind(&afterNew); 435 { 436 GateRef dst = ChangeTaggedPointerToInt64(PtrAdd(*result, IntPtr(LineEcmaString::DATA_OFFSET))); 437 DEFVARIABLE(cursor, VariableType::INT32(), Int32Sub(*length, Int32(1))); 438 DEFVARIABLE(digit, VariableType::INT32(), Int32(0)); 439 DEFVARIABLE(dstTmp, VariableType::NATIVE_POINTER(), dst); 440 dstTmp = PtrAdd(*dstTmp, PtrMul(ZExtInt32ToPtr(*cursor), IntPtr(sizeof(uint8_t)))); 441 Label loopHead1(env); 442 Label loopEnd1(env); 443 Label next1(env); 444 Label loopExit1(env); 445 Jump(&loopHead1); 446 LoopBegin(&loopHead1); 447 { 448 BRANCH(Int32GreaterThan(*n, Int32(0)), &next1, &loopExit1); 449 Bind(&next1); 450 { 451 digit = Int32Mod(*n, radix); 452 n = Int32Div(*n, radix); 453 GateRef digitChar = ToCharCode(*digit); 454 Store(VariableType::INT8(), glue_, *dstTmp, IntPtr(0), TruncInt32ToInt8(digitChar)); 455 cursor = Int32Sub(*cursor, Int32(1)); 456 Jump(&loopEnd1); 457 } 458 } 459 Bind(&loopEnd1); 460 dstTmp = PtrSub(*dstTmp, IntPtr(sizeof(uint8_t))); 461 // Work with low level buffers, we can't call GC. Loop is simple, no more 32 iteration. 462 // Ability using GC at the end of loop require add additional calculate pointer to data of string 463 // on each iteration. 464 LoopEnd(&loopHead1); 465 Bind(&loopExit1); 466 { 467 Label strInsertSign(env); 468 Label strNotInsertSign(env); 469 BRANCH(isNegative, &strInsertSign, &exit); 470 Bind(&strInsertSign); 471 { 472 dstTmp = PtrSub(*dstTmp, IntPtr(sizeof(uint8_t))); 473 Store(VariableType::INT8(), glue_, dst, IntPtr(0), Int8(45)); // 45: means '-' 474 Jump(&exit); 475 } 476 } 477 } 478 } 479 } 480 } 481 Bind(&exit); 482 auto ret = *result; 483 env->SubCfgExit(); 484 return ret; 485} 486} // namespace panda::ecmascript::kungfu 487