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, ¬IntegerString);
41
42 Bind(&isIntegerString);
43 {
44 *elemKey = GetRawHashFromString(key);
45 Jump(isElement);
46 }
47
48 Bind(¬IntegerString);
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, ¬Int);
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(¬Int);
97 BRANCH(TaggedIsString(key), &isString, ¬String);
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, ¬InternString);
115 Bind(¬InternString);
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(¬String);
131 BRANCH(TaggedIsDouble(key), &isDouble, ¬Double);
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, ¬InternString);
162 Bind(¬InternString);
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(¬Double);
172 BRANCH(IsSymbol(key), &isSymbol, ¬Symbol);
173
174 Bind(&isSymbol);
175 {
176 *propKey = key;
177 Jump(isProperty);
178 }
179
180 Bind(¬Symbol);
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, ¬EcmaObject);
228
229 Bind(¬EcmaObject);
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, ¬JsGlobalObject);
273 }
274
275 Bind(¬JsGlobalObject);
276 {
277 Label notDicMod(env);
278 GateRef hclass = LoadHClass(obj);
279 BRANCH(IsDictionaryModeByHClass(hclass), &isDicMode, ¬DicMod);
280 Bind(¬DicMod);
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, ¬StringObject);
332 Bind(&isPrimitiveRef);
333 {
334 GateRef value = Load(VariableType::JS_ANY(), obj, IntPtr(JSPrimitiveRef::VALUE_OFFSET));
335 BRANCH(TaggedIsString(value), &isStringObject, ¬StringObject);
336 Bind(&isStringObject);
337 {
338 GateRef strLength = GetLengthFromString(value);
339 BRANCH(Int32LessThan(elementIdx, strLength), &elementFound, &exit);
340 }
341 }
342
343 Bind(¬StringObject);
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, ¬DicMode);
366 Bind(¬DicMode);
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, ¬JSProxy);
462 }
463 }
464
465 Bind(¬JSProxy);
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