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, &notThrowError);
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(&notThrowError);
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