• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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/object_operator_stub_builder.h"
17 
18 #include "ecmascript/compiler/builtins/builtins_typedarray_stub_builder.h"
19 #include "ecmascript/compiler/circuit_builder_helper.h"
20 #include "ecmascript/compiler/rt_call_signature.h"
21 #include "ecmascript/compiler/stub_builder-inl.h"
22 #include "ecmascript/ecma_string.h"
23 #include "ecmascript/js_primitive_ref.h"
24 
25 namespace panda::ecmascript::kungfu {
26 
TryFastHandleStringKey(GateRef key,Variable * propKey,Variable * elemKey,Label * isProperty,Label * isElement,Label * tryFailed)27 void ObjectOperatorStubBuilder::TryFastHandleStringKey(GateRef key, Variable *propKey, Variable *elemKey,
28                                                        Label *isProperty, Label *isElement, Label *tryFailed)
29 {
30     auto env = GetEnvironment();
31     Label isInternString(env);
32 
33     BRANCH(IsInternalString(key), &isInternString, tryFailed);
34 
35     Bind(&isInternString);
36     {
37         Label isIntegerString(env);
38         Label notIntegerString(env);
39 
40         BRANCH(IsIntegerString(key), &isIntegerString, &notIntegerString);
41 
42         Bind(&isIntegerString);
43         {
44             *elemKey = GetRawHashFromString(key);
45             Jump(isElement);
46         }
47 
48         Bind(&notIntegerString);
49         {
50             Label keyIsProperty(env);
51             GateRef len = GetLengthFromString(key);
52             BRANCH(Int32LessThanOrEqual(len, Int32(EcmaString::MAX_CACHED_INTEGER_SIZE)), &keyIsProperty, tryFailed);
53 
54             Bind(&keyIsProperty);
55             *propKey = key;
56             Jump(isProperty);
57         }
58     }
59 }
60 
61 // ObjectOperator::HandleKey
HandleKey(GateRef glue,GateRef key,Variable * propKey,Variable * elemKey,Label * isProperty,Label * isElement,Label * hasException,GateRef hir)62 void ObjectOperatorStubBuilder::HandleKey(GateRef glue, GateRef key, Variable *propKey, Variable *elemKey,
63                                           Label *isProperty, Label *isElement, Label *hasException, GateRef hir)
64 {
65     auto env = GetEnvironment();
66     Label isInt(env);
67     Label notInt(env);
68     Label isString(env);
69     Label notString(env);
70     Label isDouble(env);
71     Label notDouble(env);
72     Label isSymbol(env);
73     Label notSymbol(env);
74 
75     BRANCH(TaggedIsInt(key), &isInt, &notInt);
76 
77     Bind(&isInt);
78     {
79         Label numberToString(env);
80         Label indexIsValid(env);
81         DEFVARIABLE(index, VariableType::INT32(), Int32(-1));
82         index = GetInt32OfTInt(key);
83         BRANCH(Int32GreaterThanOrEqual(*index, Int32(0)), &indexIsValid, &numberToString);
84         Bind(&indexIsValid);
85         {
86             *elemKey = *index;
87             Jump(isElement);
88         }
89         Bind(&numberToString);
90         {
91             *propKey = NumberToString(glue, key);
92             Jump(isProperty);
93         }
94     }
95 
96     Bind(&notInt);
97     BRANCH(TaggedIsString(key), &isString, &notString);
98 
99     Bind(&isString);
100     {
101         Label toInternString(env);
102         Label index64To32(env);
103         Label notInternString(env);
104         Label tryFailed(env);
105         DEFVARIABLE(index64, VariableType::INT64(), Int64(-1));
106         TryFastHandleStringKey(key, propKey, elemKey, isProperty, isElement, &tryFailed);
107 
108         Bind(&tryFailed);
109         index64 = StringToElementIndex(glue, key);
110         BRANCH(Int64Equal(*index64, Int64(-1)), &toInternString, &index64To32);
111         Bind(&toInternString);
112         {
113             *propKey = key;
114             BRANCH(IsInternalString(**propKey), isProperty, &notInternString);
115             Bind(&notInternString);
116             {
117                 // fixme(hewei): need the implementation of StringTable IR, callruntime now.
118                 *propKey = CallRuntime(glue, RTSTUB_ID(NewInternalString), {**propKey});
119                 Jump(isProperty);
120             }
121         }
122 
123         Bind(&index64To32);
124         {
125             *elemKey = TruncInt64ToInt32(*index64);
126             Jump(isElement);
127         }
128     }
129 
130     Bind(&notString);
131     BRANCH(TaggedIsDouble(key), &isDouble, &notDouble);
132 
133     Bind(&isDouble);
134     {
135         GateRef number = GetDoubleOfTDouble(key);
136         GateRef integer = ChangeFloat64ToInt32(number);
137         Label inRange(env);
138         Label isEqual(env);
139         Label tryToString(env);
140 
141         BRANCH(LogicAndBuilder(env)
142                    .And(DoubleGreaterThanOrEqual(number, Double(0)))
143                    .And(DoubleLessThan(number, Double(JSObject::MAX_ELEMENT_INDEX)))
144                    .Done(),
145                &inRange,
146                &tryToString);
147 
148         Bind(&inRange);
149         BRANCH(DoubleEqual(number, ChangeInt32ToFloat64(integer)), &isEqual, &tryToString);
150 
151         Bind(&isEqual);
152         {
153             *elemKey = integer;
154             Jump(isElement);
155         }
156 
157         Bind(&tryToString);
158         {
159             Label notInternString(env);
160             *propKey = NumberToString(glue, key);
161             BRANCH(IsInternalString(**propKey), isProperty, &notInternString);
162             Bind(&notInternString);
163             {
164                 // fixme(hewei): need the implementation of StringTable IR, callruntime now.
165                 *propKey = CallRuntime(glue, RTSTUB_ID(NewInternalString), {**propKey});
166                 Jump(isProperty);
167             }
168         }
169     }
170 
171     Bind(&notDouble);
172     BRANCH(IsSymbol(key), &isSymbol, &notSymbol);
173 
174     Bind(&isSymbol);
175     {
176         *propKey = key;
177         Jump(isProperty);
178     }
179 
180     Bind(&notSymbol);
181     {
182         Label noException(env);
183         *propKey = ToPrimitive(glue, key, PreferredPrimitiveType::PREFER_STRING, hir);
184         BRANCH(HasPendingException(glue), hasException, &noException);
185         Bind(&noException);
186         {
187             Label toString(env);
188             BRANCH(IsSymbol(**propKey), isProperty, &toString);
189             Bind(&toString);
190             {
191                 *propKey = JSTaggedValueToString(glue, **propKey, hir);
192                 // fixme(hewei): need the implementation of StringTable IR, callruntime now.
193                 *propKey = CallRuntime(glue, RTSTUB_ID(NewInternalString), {**propKey});
194                 Jump(isProperty);
195             }
196         }
197     }
198 }
199 
200 template <bool keyIsElement>
CheckValidIndexOrKeyIsLength(GateRef glue,GateRef obj,GateRef key,Label * checkSucc,Label * checkFail)201 void ObjectOperatorStubBuilder::CheckValidIndexOrKeyIsLength(GateRef glue, GateRef obj, GateRef key, Label *checkSucc,
202                                                              Label *checkFail)
203 {
204     auto env = GetEnvironment();
205     if constexpr (keyIsElement) {
206         /// For element, key is an int32 number.
207         BRANCH(Int32LessThan(key, GetLengthFromString(obj)), checkSucc, checkFail);
208     } else {
209         Label keyIsString(env);
210         BRANCH(TaggedIsString(key), &keyIsString, checkFail);
211         Bind(&keyIsString);
212         {
213             GateRef lengthString =
214                 GetGlobalConstantValue(VariableType::JS_POINTER(), glue, ConstantIndex::LENGTH_STRING_INDEX);
215             BRANCH(FastStringEqual(glue, key, lengthString), checkSucc, checkFail);
216         }
217     }
218 }
219 
220 template <bool keyIsElement>
UpdateHolder(GateRef glue,Variable * holder,GateRef key,Label * holderUpdated)221 void ObjectOperatorStubBuilder::UpdateHolder(GateRef glue, Variable *holder, GateRef key, Label *holderUpdated)
222 {
223     auto env = GetEnvironment();
224     Label notEcmaObject(env);
225     Label isString(env);
226     Label toProtoType(env);
227     Branch(IsEcmaObject(**holder), holderUpdated, &notEcmaObject);
228 
229     Bind(&notEcmaObject);
230     {
231         Branch(TaggedIsString(**holder), &isString, &toProtoType);
232     }
233     Bind(&isString);
234     {
235         Label checkSucess(env);
236         CheckValidIndexOrKeyIsLength<keyIsElement>(glue, **holder, key, &checkSucess, &toProtoType);
237 
238         Bind(&checkSucess);
239         {
240             // fixme(hewei): need the implementation of JSTaggedValue::DefinePropertyOrThrow IR, callruntime now.
241             *holder = CallRuntime(glue, RTSTUB_ID(PrimitiveStringCreate), {**holder});
242             Jump(holderUpdated);
243         }
244     }
245 
246     Bind(&toProtoType);
247     *holder = ToPrototypeOrObj(glue, **holder);
248     Jump(holderUpdated);
249 }
250 
251 template
252 void ObjectOperatorStubBuilder::UpdateHolder<false>(GateRef glue, Variable *holder, GateRef key, Label *holderUpdated);
253 template
254 void ObjectOperatorStubBuilder::UpdateHolder<true>(GateRef glue, Variable *holder, GateRef key, Label *holderUpdated);
255 
LookupPropertyInlinedProps(GateRef glue,GateRef obj,GateRef key,GateRef hir)256 GateRef ObjectOperatorStubBuilder::LookupPropertyInlinedProps(GateRef glue, GateRef obj, GateRef key, GateRef hir)
257 
258 {
259     auto env = GetEnvironment();
260     Label entry(env);
261     env->SubCfgEntry(&entry);
262     Label exit(env);
263     Label isJsObject(env);
264     Label notJsGlobalObject(env);
265     Label isDicMode(env);
266     Label hasEntry(env);
267     DEFVARIABLE(result, VariableType::JS_ANY(), TaggedFalse());
268 
269     BRANCH(IsJSObject(obj), &isJsObject, &exit);
270     Bind(&isJsObject);
271     {
272         BRANCH(IsJSGlobalObject(obj), &isDicMode, &notJsGlobalObject);
273     }
274 
275     Bind(&notJsGlobalObject);
276     {
277         Label notDicMod(env);
278         GateRef hclass = LoadHClass(obj);
279         BRANCH(IsDictionaryModeByHClass(hclass), &isDicMode, &notDicMod);
280         Bind(&notDicMod);
281         {
282             GateRef layOutInfo = GetLayoutFromHClass(hclass);
283             GateRef propsNum = GetNumberOfPropsFromHClass(hclass);
284             // int entry = layoutInfo->FindElementWithCache(thread, hclass, key, propsNumber)
285             GateRef entryA = FindElementWithCache(glue, layOutInfo, hclass, key, propsNum, hir);
286             // if branch condition : entry != -1
287             BRANCH(Int32NotEqual(entryA, Int32(-1)), &hasEntry, &exit);
288         }
289     }
290     Bind(&isDicMode);
291     {
292         Label findInDic(env);
293         GateRef array = GetPropertiesArray(obj);
294         GateRef len = GetLengthOfTaggedArray(array);
295         BRANCH(Int32Equal(len, Int32(0)), &exit, &findInDic);
296 
297         Bind(&findInDic);
298         // int entry = dict->FindEntry(key)
299         GateRef entryB = FindEntryFromNameDictionary(glue, array, key, hir);
300         // if branch condition : entry != -1
301         BRANCH(Int32NotEqual(entryB, Int32(-1)), &hasEntry, &exit);
302     }
303 
304     Bind(&hasEntry);
305     {
306         result = TaggedTrue();
307         Jump(&exit);
308     }
309 
310     Bind(&exit);
311     auto ret = *result;
312     env->SubCfgExit();
313     return ret;
314 }
315 
LookupElementInlinedProps(GateRef glue,GateRef obj,GateRef elementIdx,GateRef hir)316 GateRef ObjectOperatorStubBuilder::LookupElementInlinedProps(GateRef glue, GateRef obj, GateRef elementIdx,
317                                                              [[maybe_unused]] GateRef hir)
318 {
319     auto env = GetEnvironment();
320     Label entry(env);
321     env->SubCfgEntry(&entry);
322     Label exit(env);
323     Label isPrimitiveRef(env);
324     Label isStringObject(env);
325     Label notStringObject(env);
326     Label isTypedArray(env);
327     Label ordinaryObject(env);
328     Label elementFound(env);
329     DEFVARIABLE(result, VariableType::JS_ANY(), TaggedFalse());
330     // fastpath for string obj
331     BRANCH(IsJSPrimitiveRef(obj), &isPrimitiveRef, &notStringObject);
332     Bind(&isPrimitiveRef);
333     {
334         GateRef value = Load(VariableType::JS_ANY(), obj, IntPtr(JSPrimitiveRef::VALUE_OFFSET));
335         BRANCH(TaggedIsString(value), &isStringObject, &notStringObject);
336         Bind(&isStringObject);
337         {
338             GateRef strLength = GetLengthFromString(value);
339             BRANCH(Int32LessThan(elementIdx, strLength), &elementFound, &exit);
340         }
341     }
342 
343     Bind(&notStringObject);
344     BRANCH(IsTypedArray(obj), &isTypedArray, &ordinaryObject);
345 
346     Bind(&isTypedArray);
347     {
348         BuiltinsTypedArrayStubBuilder typedArrayStubBuilder(this);
349         GateRef element =
350             typedArrayStubBuilder.FastGetPropertyByIndex(glue, obj, elementIdx, GetObjectType(LoadHClass(obj)));
351         BRANCH(TaggedIsHole(element), &exit, &elementFound);
352     }
353 
354     Bind(&ordinaryObject);
355     {
356         Label findByIndex(env);
357         GateRef elements = GetElementsArray(obj);
358         GateRef len = GetLengthOfTaggedArray(elements);
359         BRANCH(Int32Equal(len, Int32(0)), &exit, &findByIndex);
360 
361         Bind(&findByIndex);
362         {
363             Label isDicMode(env);
364             Label notDicMode(env);
365             BRANCH(IsDictionaryMode(elements), &isDicMode, &notDicMode);
366             Bind(&notDicMode);
367             {
368                 Label lessThanLength(env);
369                 Label notLessThanLength(env);
370                 BRANCH(Int32UnsignedLessThanOrEqual(len, elementIdx), &exit, &lessThanLength);
371                 Bind(&lessThanLength);
372                 {
373                     Label notHole(env);
374                     GateRef value = GetTaggedValueWithElementsKind(glue, obj, elementIdx);
375                     BRANCH(TaggedIsNotHole(value), &elementFound, &exit);
376                 }
377             }
378             Bind(&isDicMode);
379             {
380                 GateRef entryA = FindElementFromNumberDictionary(glue, elements, elementIdx);
381                 BRANCH(Int32NotEqual(entryA, Int32(-1)), &elementFound, &exit);
382             }
383         }
384     }
385 
386     Bind(&elementFound);
387     {
388         result = TaggedTrue();
389         Jump(&exit);
390     }
391 
392     Bind(&exit);
393     auto ret = *result;
394     env->SubCfgExit();
395     return ret;
396 }
397 
398 template <bool keyIsElement>
LookupProperty(GateRef glue,Variable * holder,GateRef key,Label * isJSProxy,Label * ifFound,Label * notFound,GateRef hir)399 void ObjectOperatorStubBuilder::LookupProperty(GateRef glue, Variable *holder, GateRef key, Label *isJSProxy,
400                                                Label *ifFound, Label *notFound, GateRef hir)
401 {
402     auto env = GetEnvironment();
403     Label continuelyLookup(env);
404     Label lookupInProtoChain(env);
405     BRANCH(IsJsProxy(**holder), isJSProxy, &continuelyLookup);
406 
407     Bind(&continuelyLookup);
408     {
409         DEFVARIABLE(result, VariableType::JS_ANY(), TaggedFalse());
410         if constexpr (keyIsElement) {
411             result = LookupElementInlinedProps(glue, **holder, key, hir);
412         } else {
413             result = LookupPropertyInlinedProps(glue, **holder, key, hir);
414         }
415         BRANCH(TaggedIsTrue(*result), ifFound, &lookupInProtoChain);
416     }
417 
418     Bind(&lookupInProtoChain);
419     {
420         TryLookupInProtoChain<keyIsElement>(glue, holder, key, ifFound, notFound, isJSProxy, hir);
421     }
422 }
423 
424 template
425 void ObjectOperatorStubBuilder::LookupProperty<false>(GateRef glue, Variable *holder, GateRef key, Label *isJSProxy,
426                                                       Label *ifFound, Label *notFound, GateRef hir);
427 template
428 void ObjectOperatorStubBuilder::LookupProperty<true>(GateRef glue, Variable *holder, GateRef key, Label *isJSProxy,
429                                                      Label *ifFound, Label *notFound, GateRef hir);
430 
431 template <bool keyIsElement>
TryLookupInProtoChain(GateRef glue,Variable * holder,GateRef key,Label * ifFound,Label * notFound,Label * isJSProxy,GateRef hir)432 void ObjectOperatorStubBuilder::TryLookupInProtoChain(GateRef glue, Variable *holder, GateRef key, Label *ifFound,
433                                                       Label *notFound, Label *isJSProxy, GateRef hir)
434 {
435     auto env = GetEnvironment();
436     DEFVARIABLE(result, VariableType::JS_ANY(), TaggedFalse());
437 
438     Label loopHead(env);
439     Label loopEnd(env);
440     Label next(env);
441     Label loopExit(env);
442     Jump(&loopHead);
443     LoopBegin(&loopHead);
444     {
445         BRANCH(TaggedIsTrue(*result), ifFound, &next);
446         Bind(&next);
447         {
448             Label noPendingException(env);
449             Label notJSProxy(env);
450             GateRef proto = GetPrototype(glue, **holder);
451             BRANCH_UNLIKELY(HasPendingException(glue), &loopExit, &noPendingException);
452 
453             Bind(&noPendingException);
454             {
455                 Label updateHolder(env);
456                 BRANCH(TaggedIsHeapObject(proto), &updateHolder, &loopExit);
457 
458                 Bind(&updateHolder);
459                 {
460                     *holder = proto;
461                     BRANCH(IsJsProxy(**holder), isJSProxy, &notJSProxy);
462                 }
463             }
464 
465             Bind(&notJSProxy);
466             {
467                 if constexpr (keyIsElement) {
468                     result = LookupElementInlinedProps(glue, **holder, key, hir);
469                 } else {
470                     result = LookupPropertyInlinedProps(glue, **holder, key, hir);
471                 }
472                 Jump(&loopEnd);
473             }
474         }
475     }
476     Bind(&loopEnd);
477     LoopEnd(&loopHead);
478     Bind(&loopExit);
479     BRANCH(TaggedIsTrue(*result), ifFound, notFound);
480 }
481 }  // namespace panda::ecmascript::kungfu
482