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