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