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, ¬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
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