• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /**
3  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "plugins/ets/runtime/ets_class_linker_extension.h"
18 #include "plugins/ets/runtime/ets_stubs-inl.h"
19 #include "plugins/ets/runtime/types/ets_box_primitive.h"
20 #include "plugins/ets/runtime/types/ets_base_enum.h"
21 #include "plugins/ets/runtime/types/ets_string.h"
22 
23 namespace ark::ets {
24 
25 template <typename T>
GetBoxedNumericValue(EtsPlatformTypes const * ptypes,EtsObject * obj)26 static std::optional<T> GetBoxedNumericValue(EtsPlatformTypes const *ptypes, EtsObject *obj)
27 {
28     auto *cls = obj->GetClass();
29 
30     auto const getValue = [obj](auto typeId) {
31         using Type = typename decltype(typeId)::type;
32         return static_cast<T>(EtsBoxPrimitive<Type>::FromCoreType(obj)->GetValue());
33     };
34 
35     if (cls == ptypes->coreDouble) {
36         return getValue(helpers::TypeIdentity<EtsDouble>());
37     }
38     if (cls == ptypes->coreInt) {
39         return getValue(helpers::TypeIdentity<EtsInt>());
40     }
41 
42     if (cls == ptypes->coreByte) {
43         return getValue(helpers::TypeIdentity<EtsByte>());
44     }
45     if (cls == ptypes->coreShort) {
46         return getValue(helpers::TypeIdentity<EtsShort>());
47     }
48     if (cls == ptypes->coreLong) {
49         return getValue(helpers::TypeIdentity<EtsLong>());
50     }
51     if (cls == ptypes->coreFloat) {
52         return getValue(helpers::TypeIdentity<EtsFloat>());
53     }
54     if (cls == ptypes->coreChar) {
55         return getValue(helpers::TypeIdentity<EtsChar>());
56     }
57     return std::nullopt;
58 }
59 
EtsBigIntEquality(EtsBigInt * obj1,EtsBigInt * obj2)60 bool EtsBigIntEquality(EtsBigInt *obj1, EtsBigInt *obj2)
61 {
62     auto bytes1 = obj1->GetBytes();
63     auto bytes2 = obj2->GetBytes();
64     ASSERT(bytes1 != nullptr && bytes2 != nullptr);
65 
66     auto size1 = bytes1->GetLength();
67     auto size2 = bytes2->GetLength();
68     if (size1 != size2) {
69         return false;
70     }
71 
72     if (obj1->GetSign() != obj2->GetSign()) {
73         return false;
74     }
75 
76     return (std::memcmp(bytes1->GetCoreType()->GetData(), bytes2->GetCoreType()->GetData(), size1 * sizeof(EtsInt)) ==
77             0);
78 }
79 
80 template <typename BoxedType>
CompareBoxedPrimitive(EtsObject * obj1,EtsObject * obj2)81 static bool CompareBoxedPrimitive(EtsObject *obj1, EtsObject *obj2)
82 {
83     return EtsBoxPrimitive<BoxedType>::FromCoreType(obj1)->GetValue() ==
84            EtsBoxPrimitive<BoxedType>::FromCoreType(obj2)->GetValue();
85 }
86 
EqualityResursionAllowed(EtsObject * obj)87 static bool EqualityResursionAllowed(EtsObject *obj)
88 {
89     return !(obj->GetClass()->IsEtsEnum() || obj->GetClass()->IsFunctionReference());
90 }
91 
92 // CC-OFFNXT(huge_cca_cyclomatic_complexity[C++], huge_cyclomatic_complexity[C++], huge_method[C++]) big case
EtsValueTypedEquals(EtsCoroutine * coro,EtsObject * obj1,EtsObject * obj2)93 bool EtsValueTypedEquals(EtsCoroutine *coro, EtsObject *obj1, EtsObject *obj2)
94 {
95     auto cls1 = obj1->GetClass();
96     auto cls2 = obj2->GetClass();
97     ASSERT(cls1->IsValueTyped() && cls1->IsValueTyped());
98 
99     auto ptypes = PlatformTypes(coro);
100     ASSERT(ptypes != nullptr);
101 
102     if (cls1->IsStringClass()) {
103         if (UNLIKELY(cls2->IsEtsEnum())) {
104             obj2 = EtsBaseEnum::FromEtsObject(obj2)->GetValue();
105             cls2 = obj2->GetClass();
106         }
107         return cls2->IsStringClass() &&
108                coretypes::String::Cast(obj1->GetCoreType())->Compare(coretypes::String::Cast(obj2->GetCoreType())) == 0;
109     }
110     if (cls1 == ptypes->coreBoolean) {
111         return cls2 == ptypes->coreBoolean && CompareBoxedPrimitive<EtsBoolean>(obj1, obj2);
112     }
113     if (UNLIKELY(cls1->IsBigInt())) {
114         return cls2->IsBigInt() && EtsBigIntEquality(EtsBigInt::FromEtsObject(obj1), EtsBigInt::FromEtsObject(obj2));
115     }
116     if (cls1 == ptypes->coreLong && cls2 == ptypes->coreLong) {
117         return CompareBoxedPrimitive<EtsLong>(obj1, obj2);
118     }
119     if (auto num1 = GetBoxedNumericValue<EtsDouble>(ptypes, obj1); num1.has_value()) {
120         if (UNLIKELY(cls2->IsEtsEnum())) {
121             obj2 = EtsBaseEnum::FromEtsObject(obj2)->GetValue();
122         }
123         auto num2 = GetBoxedNumericValue<EtsDouble>(ptypes, obj2);
124         return num2.has_value() && num2.value() == num1.value();
125     }
126     if (cls1->IsEtsEnum()) {
127         auto *value1 = EtsBaseEnum::FromEtsObject(obj1)->GetValue();
128         auto *value2 = obj2;
129         if (LIKELY(cls2->IsEtsEnum())) {
130             value2 = EtsBaseEnum::FromEtsObject(obj2)->GetValue();
131         }
132         if (!EqualityResursionAllowed(value1) || !EqualityResursionAllowed(value2)) {
133             return false;
134         }
135         return EtsReferenceEquals(coro, value1, value2);
136     }
137 
138     if (cls1->IsFunctionReference()) {
139         if (UNLIKELY(!cls2->IsFunctionReference())) {
140             return false;
141         }
142         if (cls1->GetTypeMetaData() != cls2->GetTypeMetaData()) {
143             return false;
144         }
145         // function or static method
146         if (obj1->GetClass()->GetFieldsNumber() == 0) {
147             return true;
148         }
149         // For instance method, always only have one field as guaranteed by class initialization
150         ASSERT((obj1->GetClass()->GetFieldsNumber() == 1) && (obj2->GetClass()->GetFieldsNumber() == 1));
151         auto instance1 = obj1->GetFieldObject(obj1->GetClass()->GetFieldByIndex(0));
152         auto instance2 = obj2->GetFieldObject(obj2->GetClass()->GetFieldByIndex(0));
153         if (!EqualityResursionAllowed(instance1) || !EqualityResursionAllowed(instance2)) {
154             return false;
155         }
156         return EtsReferenceEquals(coro, instance1, instance2);
157     }
158     UNREACHABLE();
159 }
160 
DbgIsBoxedNumericClass(EtsCoroutine * coro,EtsClass * cls)161 [[maybe_unused]] static bool DbgIsBoxedNumericClass(EtsCoroutine *coro, EtsClass *cls)
162 {
163     auto ptypes = PlatformTypes(coro);
164     return cls == ptypes->coreByte || cls == ptypes->coreChar || cls == ptypes->coreShort || cls == ptypes->coreInt ||
165            cls == ptypes->coreLong || cls == ptypes->coreFloat || cls == ptypes->coreDouble;
166 }
167 
EtsGetTypeof(EtsCoroutine * coro,EtsObject * obj)168 EtsString *EtsGetTypeof(EtsCoroutine *coro, EtsObject *obj)
169 {
170     // NOTE(vpukhov): #19799 use string constants
171     if (obj == nullptr) {
172         return EtsString::CreateFromMUtf8("undefined");
173     }
174     EtsClass *cls = obj->GetClass();
175 
176     // NOTE(vpukhov): re-encode subtyping flags if necessary
177     if (!cls->IsValueTyped()) {
178         if (cls->IsFunction()) {
179             return EtsString::CreateFromMUtf8("function");
180         }
181         return EtsString::CreateFromMUtf8("object");
182     }
183 
184     if (cls->IsFunctionReference()) {
185         return EtsString::CreateFromMUtf8("function");
186     }
187 
188     if (cls->IsNullValue()) {
189         return EtsString::CreateFromMUtf8("object");
190     }
191     if (obj->IsStringClass()) {
192         return EtsString::CreateFromMUtf8("string");
193     }
194     if (cls->IsBigInt()) {
195         return EtsString::CreateFromMUtf8("bigint");
196     }
197     if (cls->IsEtsEnum()) {
198         auto *value = EtsBaseEnum::FromEtsObject(obj)->GetValue();
199         if (UNLIKELY(value->GetClass()->IsEtsEnum())) {
200             // This situation is unexpected from language point of view. If BaseEnum object is contained
201             // as value of enum, then it's treated as object
202             return EtsString::CreateFromMUtf8("object");
203         }
204         return EtsGetTypeof(coro, EtsBaseEnum::FromEtsObject(obj)->GetValue());
205     }
206 
207     ASSERT(cls->IsBoxed());
208 
209     if (cls == PlatformTypes(coro)->coreBoolean) {
210         return EtsString::CreateFromMUtf8("boolean");
211     }
212 
213     ASSERT(DbgIsBoxedNumericClass(coro, cls));
214     return EtsString::CreateFromMUtf8("number");
215 }
216 
EtsGetIstrue(EtsCoroutine * coro,EtsObject * obj)217 bool EtsGetIstrue(EtsCoroutine *coro, EtsObject *obj)
218 {
219     if (IsReferenceNullish(coro, obj)) {
220         return false;
221     }
222     EtsClass *cls = obj->GetClass();
223 
224     if (!cls->IsValueTyped()) {
225         return true;
226     }
227     if (cls->IsFunctionReference()) {
228         return true;
229     }
230     if (obj->IsStringClass()) {
231         return !EtsString::FromEtsObject(obj)->IsEmpty();
232     }
233     if (cls->IsBigInt()) {
234         return EtsBigInt::FromEtsObject(obj)->GetSign() != 0;
235     }
236     if (cls->IsEtsEnum()) {
237         auto *value = EtsBaseEnum::FromEtsObject(obj)->GetValue();
238         if (UNLIKELY(value->GetClass()->IsEtsEnum())) {
239             // This situation is unexpected from language point of view. If BaseEnum object is contained
240             // as value of enum, then it's treated as object
241             return true;
242         }
243         return EtsGetIstrue(coro, value);
244     }
245 
246     ASSERT(cls->IsBoxed());
247 
248     auto ptypes = PlatformTypes(coro);
249     if (cls == ptypes->coreBoolean) {
250         return EtsBoxPrimitive<EtsBoolean>::FromCoreType(obj)->GetValue() != 0;
251     }
252 
253     ASSERT(DbgIsBoxedNumericClass(coro, cls));
254     if (auto num = GetBoxedNumericValue<EtsDouble>(ptypes, obj); num.has_value()) {
255         return num.value() != 0 && !std::isnan(num.value());
256     }
257     UNREACHABLE();
258 }
259 
260 }  // namespace ark::ets
261