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 
25 namespace panda::ecmascript::kungfu {
ParseFloat(Variable *result, Label *exit, Label *slowPath)26 void 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 
ParseInt(Variable *result, Label *exit, Label *slowPath)53 void 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 
IsFinite(Variable *result, Label *exit, Label *slowPath)86 void 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 
IsNaN(Variable *result, Label *exit, Label *slowPath)125 void 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 
IsInteger(Variable *result, Label *exit, Label *slowPath)156 void 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 
IsSafeInteger(Variable *result, Label *exit, Label *slowPath)192 void 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 
GenNumberConstructor(GateRef nativeCode, GateRef func, GateRef newTarget)240 void 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 
ToString(Variable *result, Label *exit, Label *slowPath)315 void 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 
NumberToString(GateRef number, GateRef radix)356 GateRef 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