1 /*
2 * Copyright (c) 2021-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 "ETSGen.h"
17 #include "ETSGen-inl.h"
18
19 #include "generated/signatures.h"
20 #include "ir/base/scriptFunction.h"
21 #include "ir/base/classDefinition.h"
22 #include "ir/statement.h"
23 #include "ir/expressions/assignmentExpression.h"
24 #include "ir/expressions/identifier.h"
25 #include "ir/expressions/binaryExpression.h"
26 #include "ir/expressions/callExpression.h"
27 #include "ir/expressions/memberExpression.h"
28 #include "ir/expressions/templateLiteral.h"
29 #include "ir/statements/breakStatement.h"
30 #include "ir/statements/continueStatement.h"
31 #include "ir/statements/tryStatement.h"
32 #include "ir/ts/tsInterfaceDeclaration.h"
33 #include "varbinder/variableFlags.h"
34 #include "compiler/base/lreference.h"
35 #include "compiler/base/catchTable.h"
36 #include "compiler/core/dynamicContext.h"
37 #include "varbinder/ETSBinder.h"
38 #include "varbinder/variable.h"
39 #include "checker/types/type.h"
40 #include "checker/types/typeFlag.h"
41 #include "checker/checker.h"
42 #include "checker/ETSchecker.h"
43 #include "checker/types/ets/etsObjectType.h"
44 #include "checker/types/ets/etsTupleType.h"
45 #include "checker/types/ets/etsAsyncFuncReturnType.h"
46 #include "parser/program/program.h"
47 #include "checker/types/globalTypesHolder.h"
48 #include "public/public.h"
49
50 namespace ark::es2panda::compiler {
51
IsWidePrimitiveType(checker::Type const * type)52 static inline bool IsWidePrimitiveType(checker::Type const *type)
53 {
54 return type->IsLongType() || type->IsDoubleType();
55 }
56
ETSGen(ArenaAllocator * allocator,RegSpiller * spiller,public_lib::Context * context,std::tuple<varbinder::FunctionScope *,ProgramElement *,AstCompiler * > toCompile)57 ETSGen::ETSGen(ArenaAllocator *allocator, RegSpiller *spiller, public_lib::Context *context,
58 std::tuple<varbinder::FunctionScope *, ProgramElement *, AstCompiler *> toCompile) noexcept
59 : CodeGen(allocator, spiller, context, toCompile),
60 containingObjectType_(util::Helpers::GetContainingObjectType(RootNode()))
61 {
62 ETSFunction::Compile(this);
63 }
64
SetAccumulatorType(const checker::Type * type)65 void ETSGen::SetAccumulatorType(const checker::Type *type)
66 {
67 SetVRegType(acc_, type);
68 }
69
GetAccumulatorType() const70 const checker::Type *ETSGen::GetAccumulatorType() const
71 {
72 return GetVRegType(acc_);
73 }
74
CompileAndCheck(const ir::Expression * expr)75 void ETSGen::CompileAndCheck(const ir::Expression *expr)
76 {
77 // NOTE: vpukhov. bad accumulator type leads to terrible bugs in codegen
78 // make exact types match mandatory
79 expr->Compile(this);
80
81 auto const *const accType = GetAccumulatorType();
82 auto const *const exprType = expr->TsType();
83
84 if (Checker()->Relation()->IsIdenticalTo(accType, exprType) || exprType->IsETSTypeParameter() ||
85 exprType->IsETSPartialTypeParameter() || exprType->IsETSNonNullishType()) {
86 return;
87 }
88
89 ES2PANDA_ASSERT(accType != nullptr);
90 if (accType->IsETSPrimitiveType() &&
91 ((accType->TypeFlags() ^ exprType->TypeFlags()) & ~checker::TypeFlag::CONSTANT) == 0) {
92 return;
93 }
94
95 ASSERT_PRINT(false, std::string("Type mismatch after Expression::Compile: ") + accType->ToString() +
96 " instead of " + exprType->ToString());
97 }
98
Checker() const99 const checker::ETSChecker *ETSGen::Checker() const noexcept
100 {
101 return Context()->checker->AsETSChecker();
102 }
103
VarBinder() const104 const varbinder::ETSBinder *ETSGen::VarBinder() const noexcept
105 {
106 return Context()->parserProgram->VarBinder()->AsETSBinder();
107 }
108
ReturnType() const109 const checker::Type *ETSGen::ReturnType() const noexcept
110 {
111 return RootNode()->AsScriptFunction()->Signature()->ReturnType();
112 }
113
ContainingObjectType() const114 const checker::ETSObjectType *ETSGen::ContainingObjectType() const noexcept
115 {
116 return containingObjectType_;
117 }
118
Acc()119 VReg &ETSGen::Acc() noexcept
120 {
121 return acc_;
122 }
123
Acc() const124 VReg ETSGen::Acc() const noexcept
125 {
126 return acc_;
127 }
128
ApplyConversionAndStoreAccumulator(const ir::AstNode * const node,const VReg vreg,const checker::Type * const targetType)129 void ETSGen::ApplyConversionAndStoreAccumulator(const ir::AstNode *const node, const VReg vreg,
130 const checker::Type *const targetType)
131 {
132 ApplyConversion(node, targetType);
133 StoreAccumulator(node, vreg);
134 }
135
StoreException(const ir::AstNode * node)136 VReg ETSGen::StoreException(const ir::AstNode *node)
137 {
138 VReg exception = AllocReg();
139 Ra().Emit<StaObj>(node, exception);
140
141 SetAccumulatorType(Checker()->GlobalBuiltinExceptionType());
142 SetVRegType(exception, GetAccumulatorType());
143 return exception;
144 }
145
StoreAccumulator(const ir::AstNode * const node,const VReg vreg)146 void ETSGen::StoreAccumulator(const ir::AstNode *const node, const VReg vreg)
147 {
148 const auto *const accType = GetAccumulatorType();
149
150 ES2PANDA_ASSERT(accType != nullptr);
151 if (accType->IsETSReferenceType()) {
152 Ra().Emit<StaObj>(node, vreg);
153 } else if (IsWidePrimitiveType(accType)) {
154 Ra().Emit<StaWide>(node, vreg);
155 } else {
156 Ra().Emit<Sta>(node, vreg);
157 }
158
159 SetVRegType(vreg, accType);
160 }
161
LoadAccumulator(const ir::AstNode * node,VReg vreg)162 void ETSGen::LoadAccumulator(const ir::AstNode *node, VReg vreg)
163 {
164 const auto *const vregType = GetVRegType(vreg);
165
166 ES2PANDA_ASSERT(vregType != nullptr);
167 if (vregType->IsETSReferenceType()) {
168 Ra().Emit<LdaObj>(node, vreg);
169 } else if (IsWidePrimitiveType(vregType)) {
170 Ra().Emit<LdaWide>(node, vreg);
171 } else {
172 Ra().Emit<Lda>(node, vreg);
173 }
174
175 SetAccumulatorType(vregType);
176 }
177
AllocMov(const ir::AstNode * const node,const VReg vd,const VReg vs)178 IRNode *ETSGen::AllocMov(const ir::AstNode *const node, const VReg vd, const VReg vs)
179 {
180 const auto *const sourceType = GetVRegType(vs);
181 // CC-OFFNXT(G.FMT.14-CPP) project code style
182 auto *const mov = [this, sourceType, node, vd, vs]() -> IRNode * {
183 if (sourceType->IsETSReferenceType()) {
184 return Allocator()->New<MovObj>(node, vd, vs);
185 }
186 if (IsWidePrimitiveType(sourceType)) {
187 return Allocator()->New<MovWide>(node, vd, vs);
188 }
189 return Allocator()->New<Mov>(node, vd, vs);
190 }();
191
192 SetVRegType(vd, sourceType);
193 return mov;
194 }
195
AllocMov(const ir::AstNode * const node,OutVReg vd,const VReg vs)196 IRNode *ETSGen::AllocMov(const ir::AstNode *const node, OutVReg vd, const VReg vs)
197 {
198 ES2PANDA_ASSERT(vd.type != OperandType::ANY && vd.type != OperandType::NONE);
199
200 switch (vd.type) {
201 case OperandType::REF:
202 return Allocator()->New<MovObj>(node, *vd.reg, vs);
203 case OperandType::B64:
204 return Allocator()->New<MovWide>(node, *vd.reg, vs);
205 default:
206 break;
207 }
208
209 return Allocator()->New<Mov>(node, *vd.reg, vs);
210 }
211
TypeForVar(varbinder::Variable const * var) const212 checker::Type const *ETSGen::TypeForVar(varbinder::Variable const *var) const noexcept
213 {
214 return var->TsType();
215 }
216
MoveVreg(const ir::AstNode * const node,const VReg vd,const VReg vs)217 void ETSGen::MoveVreg(const ir::AstNode *const node, const VReg vd, const VReg vs)
218 {
219 const auto *const sourceType = GetVRegType(vs);
220 ES2PANDA_ASSERT(sourceType != nullptr);
221
222 if (sourceType->IsETSReferenceType()) {
223 Ra().Emit<MovObj>(node, vd, vs);
224 } else if (IsWidePrimitiveType(sourceType)) {
225 Ra().Emit<MovWide>(node, vd, vs);
226 } else {
227 Ra().Emit<Mov>(node, vd, vs);
228 }
229
230 SetVRegType(vd, sourceType);
231 }
232
233 // indicates that initializer is meaningless and may lead to NPEs
LoadAccumulatorPoison(const ir::AstNode * node,const checker::Type * type)234 void ETSGen::LoadAccumulatorPoison(const ir::AstNode *node, const checker::Type *type)
235 {
236 ES2PANDA_ASSERT(type->IsETSReferenceType());
237 LoadAccumulatorUndefined(node);
238 SetAccumulatorType(type);
239 }
240
FormDynamicModulePropReference(const varbinder::Variable * var)241 util::StringView ETSGen::FormDynamicModulePropReference(const varbinder::Variable *var)
242 {
243 ES2PANDA_ASSERT(VarBinder()->IsDynamicModuleVariable(var) || VarBinder()->IsDynamicNamespaceVariable(var));
244
245 auto *data = VarBinder()->DynamicImportDataForVar(var);
246 ES2PANDA_ASSERT(data != nullptr);
247
248 auto *import = data->import;
249
250 return FormDynamicModulePropReference(import);
251 }
252
LoadAccumulatorDynamicModule(const ir::AstNode * node,const ir::ETSImportDeclaration * import)253 void ETSGen::LoadAccumulatorDynamicModule(const ir::AstNode *node, const ir::ETSImportDeclaration *import)
254 {
255 ES2PANDA_ASSERT(import->Language().IsDynamic());
256 LoadStaticProperty(node, Checker()->GlobalBuiltinDynamicType(import->Language()),
257 FormDynamicModulePropReference(import));
258 }
259
FormDynamicModulePropReference(const ir::ETSImportDeclaration * import)260 util::StringView ETSGen::FormDynamicModulePropReference(const ir::ETSImportDeclaration *import)
261 {
262 std::stringstream ss;
263 ss << VarBinder()->Program()->ModulePrefix();
264 ss << compiler::Signatures::DYNAMIC_MODULE_CLASS;
265 ss << '.';
266 ss << import->AssemblerName();
267 return util::UString(ss.str(), Allocator()).View();
268 }
269
LoadDynamicModuleVariable(const ir::AstNode * node,varbinder::Variable const * const var)270 void ETSGen::LoadDynamicModuleVariable(const ir::AstNode *node, varbinder::Variable const *const var)
271 {
272 RegScope rs(this);
273
274 auto *data = VarBinder()->DynamicImportDataForVar(var);
275 ES2PANDA_ASSERT(data != nullptr);
276
277 auto *import = data->import;
278
279 LoadStaticProperty(node, var->TsType(), FormDynamicModulePropReference(var));
280
281 auto objReg = AllocReg();
282 StoreAccumulator(node, objReg);
283
284 auto *id = data->specifier->AsImportSpecifier()->Imported();
285 auto lang = import->Language();
286 LoadPropertyDynamic(node, Checker()->GlobalBuiltinDynamicType(lang), objReg, id->Name());
287
288 ApplyConversion(node);
289 }
290
LoadDynamicNamespaceVariable(const ir::AstNode * node,varbinder::Variable const * const var)291 void ETSGen::LoadDynamicNamespaceVariable(const ir::AstNode *node, varbinder::Variable const *const var)
292 {
293 LoadStaticProperty(node, var->TsType(), FormDynamicModulePropReference(var));
294 }
295
LoadVar(const ir::Identifier * node,varbinder::Variable const * const var)296 void ETSGen::LoadVar(const ir::Identifier *node, varbinder::Variable const *const var)
297 {
298 if (VarBinder()->IsDynamicModuleVariable(var)) {
299 LoadDynamicModuleVariable(node, var);
300 return;
301 }
302
303 if (VarBinder()->IsDynamicNamespaceVariable(var)) {
304 LoadDynamicNamespaceVariable(node, var);
305 return;
306 }
307
308 auto *local = var->AsLocalVariable();
309
310 switch (ETSLReference::ResolveReferenceKind(var)) {
311 case ReferenceKind::STATIC_FIELD: {
312 auto fullName = FormClassPropReference(var);
313 LoadStaticProperty(node, var->TsType(), fullName);
314 break;
315 }
316 case ReferenceKind::FIELD: {
317 ES2PANDA_ASSERT(GetVRegType(GetThisReg()) != nullptr);
318 const auto fullName = FormClassPropReference(GetVRegType(GetThisReg())->AsETSObjectType(), var->Name());
319 LoadProperty(node, var->TsType(), GetThisReg(), fullName);
320 break;
321 }
322 case ReferenceKind::METHOD:
323 case ReferenceKind::STATIC_METHOD:
324 case ReferenceKind::CLASS:
325 case ReferenceKind::STATIC_CLASS: {
326 SetAccumulatorType(var->TsType());
327 break;
328 }
329 case ReferenceKind::LOCAL: {
330 LoadAccumulator(node, local->Vreg());
331 SetAccumulatorType(GetVRegType(local->Vreg()));
332 break;
333 }
334 default: {
335 ES2PANDA_UNREACHABLE();
336 }
337 }
338 }
339
StoreVar(const ir::Identifier * node,const varbinder::ConstScopeFindResult & result)340 void ETSGen::StoreVar(const ir::Identifier *node, const varbinder::ConstScopeFindResult &result)
341 {
342 auto *local = result.variable->AsLocalVariable();
343 ApplyConversion(node, local->TsType());
344
345 switch (ETSLReference::ResolveReferenceKind(result.variable)) {
346 case ReferenceKind::STATIC_FIELD: {
347 auto fullName = FormClassPropReference(result.variable);
348 StoreStaticProperty(node, result.variable->TsType(), fullName);
349 break;
350 }
351 case ReferenceKind::FIELD: {
352 StoreProperty(node, result.variable->TsType(), GetThisReg(), result.name);
353 break;
354 }
355 case ReferenceKind::LOCAL: {
356 StoreAccumulator(node, local->Vreg());
357 SetVRegType(local->Vreg(), GetAccumulatorType());
358 break;
359 }
360 default: {
361 ES2PANDA_UNREACHABLE();
362 }
363 }
364 }
365
FormClassPropReference(const checker::ETSObjectType * classType,const util::StringView & name)366 util::StringView ETSGen::FormClassPropReference(const checker::ETSObjectType *classType, const util::StringView &name)
367 {
368 std::stringstream ss;
369 ES2PANDA_ASSERT(classType != nullptr);
370 ss << classType->AssemblerName().Mutf8() << Signatures::METHOD_SEPARATOR << name;
371 return util::StringView(*ProgElement()->Strings().emplace(ss.str()).first);
372 }
373
FormClassPropReference(varbinder::Variable const * const var)374 util::StringView ETSGen::FormClassPropReference(varbinder::Variable const *const var)
375 {
376 auto containingObjectType = util::Helpers::GetContainingObjectType(var->Declaration()->Node());
377 return FormClassPropReference(containingObjectType, var->Name());
378 }
379
StoreStaticOwnProperty(const ir::AstNode * node,const checker::Type * propType,const util::StringView & name)380 void ETSGen::StoreStaticOwnProperty(const ir::AstNode *node, const checker::Type *propType,
381 const util::StringView &name)
382 {
383 util::StringView fullName = FormClassPropReference(containingObjectType_, name);
384 StoreStaticProperty(node, propType, fullName);
385 }
386
StoreStaticProperty(const ir::AstNode * const node,const checker::Type * propType,const util::StringView & fullName)387 void ETSGen::StoreStaticProperty(const ir::AstNode *const node, const checker::Type *propType,
388 const util::StringView &fullName)
389 {
390 if (propType->IsETSReferenceType()) {
391 Sa().Emit<StstaticObj>(node, fullName);
392 } else if (IsWidePrimitiveType(propType)) {
393 Sa().Emit<StstaticWide>(node, fullName);
394 } else {
395 Sa().Emit<Ststatic>(node, fullName);
396 }
397 }
398
LoadStaticProperty(const ir::AstNode * const node,const checker::Type * propType,const util::StringView & fullName)399 void ETSGen::LoadStaticProperty(const ir::AstNode *const node, const checker::Type *propType,
400 const util::StringView &fullName)
401 {
402 ES2PANDA_ASSERT(propType != nullptr);
403 if (propType->IsETSReferenceType()) {
404 Sa().Emit<LdstaticObj>(node, fullName);
405 } else if (IsWidePrimitiveType(propType)) {
406 Sa().Emit<LdstaticWide>(node, fullName);
407 } else {
408 Sa().Emit<Ldstatic>(node, fullName);
409 }
410
411 SetAccumulatorType(propType);
412 }
413
StoreProperty(const ir::AstNode * const node,const checker::Type * propType,const VReg objReg,const util::StringView & name)414 void ETSGen::StoreProperty(const ir::AstNode *const node, const checker::Type *propType, const VReg objReg,
415 const util::StringView &name)
416 {
417 ES2PANDA_ASSERT(Checker()->GetApparentType(GetVRegType(objReg)) != nullptr);
418 auto *objType = Checker()->GetApparentType(GetVRegType(objReg))->AsETSObjectType();
419 const auto fullName = FormClassPropReference(objType, name);
420
421 if (propType->IsETSReferenceType()) {
422 Ra().Emit<StobjObj>(node, objReg, fullName);
423 } else if (IsWidePrimitiveType(propType)) {
424 Ra().Emit<StobjWide>(node, objReg, fullName);
425 } else {
426 Ra().Emit<Stobj>(node, objReg, fullName);
427 }
428 }
429
LoadProperty(const ir::AstNode * const node,const checker::Type * propType,const VReg objReg,const util::StringView & fullName)430 void ETSGen::LoadProperty(const ir::AstNode *const node, const checker::Type *propType, const VReg objReg,
431 const util::StringView &fullName)
432 {
433 if (propType->IsETSReferenceType()) {
434 Ra().Emit<LdobjObj>(node, objReg, fullName);
435 } else if (IsWidePrimitiveType(propType)) {
436 Ra().Emit<LdobjWide>(node, objReg, fullName);
437 } else {
438 Ra().Emit<Ldobj>(node, objReg, fullName);
439 }
440
441 SetAccumulatorType(propType);
442 }
443
StorePropertyByName(const ir::AstNode * node,VReg objReg,checker::ETSChecker::NamedAccessMeta const & fieldMeta)444 void ETSGen::StorePropertyByName([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] VReg objReg,
445 [[maybe_unused]] checker::ETSChecker::NamedAccessMeta const &fieldMeta)
446 {
447 #ifdef PANDA_WITH_ETS
448 auto [metaObj, propType, propName] = fieldMeta;
449 const auto fullName = FormClassPropReference(metaObj, propName);
450
451 if (propType->IsETSReferenceType()) {
452 Ra().Emit<EtsStobjNameObj>(node, objReg, fullName);
453 } else if (IsWidePrimitiveType(propType)) {
454 Ra().Emit<EtsStobjNameWide>(node, objReg, fullName);
455 } else {
456 Ra().Emit<EtsStobjName>(node, objReg, fullName);
457 }
458 #else
459 ES2PANDA_UNREACHABLE();
460 #endif // PANDA_WITH_ETS
461 }
462
LoadPropertyByName(const ir::AstNode * const node,VReg objReg,checker::ETSChecker::NamedAccessMeta const & fieldMeta)463 void ETSGen::LoadPropertyByName([[maybe_unused]] const ir::AstNode *const node, [[maybe_unused]] VReg objReg,
464 [[maybe_unused]] checker::ETSChecker::NamedAccessMeta const &fieldMeta)
465 {
466 #ifdef PANDA_WITH_ETS
467 auto [metaObj, propType, propName] = fieldMeta;
468 const auto fullName = FormClassPropReference(metaObj, propName);
469
470 if (propType->IsETSReferenceType()) {
471 Ra().Emit<EtsLdobjNameObj>(node, objReg, fullName);
472 } else if (IsWidePrimitiveType(propType)) {
473 Ra().Emit<EtsLdobjNameWide>(node, objReg, fullName);
474 } else {
475 Ra().Emit<EtsLdobjName>(node, objReg, fullName);
476 }
477 SetAccumulatorType(propType);
478 #else
479 ES2PANDA_UNREACHABLE();
480 #endif // PANDA_WITH_ETS
481 }
482
StorePropertyDynamic(const ir::AstNode * node,const checker::Type * propType,VReg objReg,const util::StringView & propName)483 void ETSGen::StorePropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
484 const util::StringView &propName)
485 {
486 ES2PANDA_ASSERT(GetVRegType(objReg) != nullptr);
487 auto const lang = GetVRegType(objReg)->AsETSDynamicType()->Language();
488 std::string_view methodName {};
489 if (propType->IsETSBooleanType()) {
490 methodName = Signatures::Dynamic::SetPropertyBooleanBuiltin(lang);
491 } else if (propType->IsByteType()) {
492 methodName = Signatures::Dynamic::SetPropertyByteBuiltin(lang);
493 } else if (propType->IsCharType()) {
494 methodName = Signatures::Dynamic::SetPropertyCharBuiltin(lang);
495 } else if (propType->IsShortType()) {
496 methodName = Signatures::Dynamic::SetPropertyShortBuiltin(lang);
497 } else if (propType->IsIntType()) {
498 methodName = Signatures::Dynamic::SetPropertyIntBuiltin(lang);
499 } else if (propType->IsLongType()) {
500 methodName = Signatures::Dynamic::SetPropertyLongBuiltin(lang);
501 } else if (propType->IsFloatType()) {
502 methodName = Signatures::Dynamic::SetPropertyFloatBuiltin(lang);
503 } else if (propType->IsDoubleType()) {
504 methodName = Signatures::Dynamic::SetPropertyDoubleBuiltin(lang);
505 } else if (propType->IsETSStringType()) {
506 methodName = Signatures::Dynamic::SetPropertyStringBuiltin(lang);
507 } else if (propType->IsETSObjectType() || propType->IsETSTypeParameter()) {
508 methodName = Signatures::Dynamic::SetPropertyDynamicBuiltin(lang);
509 // NOTE: vpukhov. add non-dynamic builtin
510 if (!propType->IsETSDynamicType()) {
511 CastToDynamic(node, Checker()->GlobalBuiltinDynamicType(lang)->AsETSDynamicType());
512 }
513 } else {
514 ASSERT_PRINT(false, "Unsupported property type");
515 }
516
517 RegScope rs(this);
518 VReg propValueReg = AllocReg();
519 VReg propNameReg = AllocReg();
520
521 StoreAccumulator(node, propValueReg);
522
523 // Load property name
524 LoadAccumulatorString(node, propName);
525 StoreAccumulator(node, propNameReg);
526
527 // Set property by name
528 Ra().Emit<Call, 3U>(node, methodName, objReg, propNameReg, propValueReg, dummyReg_);
529 SetAccumulatorType(Checker()->GlobalBuiltinJSValueType());
530 }
531
LoadPropertyDynamic(const ir::AstNode * node,const checker::Type * propType,VReg objReg,std::variant<util::StringView,const ark::es2panda::ir::Expression * > property)532 void ETSGen::LoadPropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
533 std::variant<util::StringView, const ark::es2panda::ir::Expression *> property)
534 {
535 ES2PANDA_ASSERT(propType != nullptr && GetVRegType(objReg) != nullptr);
536 auto const lang = GetVRegType(objReg)->AsETSDynamicType()->Language();
537 auto *type = propType;
538 std::string_view methodName {};
539 if (propType->IsETSBooleanType()) {
540 methodName = Signatures::Dynamic::GetPropertyBooleanBuiltin(lang);
541 } else if (propType->IsByteType()) {
542 methodName = Signatures::Dynamic::GetPropertyByteBuiltin(lang);
543 } else if (propType->IsCharType()) {
544 methodName = Signatures::Dynamic::GetPropertyCharBuiltin(lang);
545 } else if (propType->IsShortType()) {
546 methodName = Signatures::Dynamic::GetPropertyShortBuiltin(lang);
547 } else if (propType->IsIntType()) {
548 methodName = Signatures::Dynamic::GetPropertyIntBuiltin(lang);
549 } else if (propType->IsLongType()) {
550 methodName = Signatures::Dynamic::GetPropertyLongBuiltin(lang);
551 } else if (propType->IsFloatType()) {
552 methodName = Signatures::Dynamic::GetPropertyFloatBuiltin(lang);
553 } else if (propType->IsDoubleType()) {
554 methodName = Signatures::Dynamic::GetPropertyDoubleBuiltin(lang);
555 } else if (propType->IsETSStringType()) {
556 methodName = Signatures::Dynamic::GetPropertyStringBuiltin(lang);
557 } else if (propType->IsETSObjectType() || propType->IsETSTypeParameter()) {
558 methodName = Signatures::Dynamic::GetPropertyDynamicBuiltin(lang);
559 type = Checker()->GlobalBuiltinDynamicType(lang);
560 } else {
561 ASSERT_PRINT(false, "Unsupported property type");
562 }
563
564 RegScope rs(this);
565
566 VReg propNameObject;
567
568 if (node->IsMemberExpression() && node->AsMemberExpression()->IsComputed()) {
569 (std::get<const ark::es2panda::ir::Expression *>(property))->Compile(this);
570 } else {
571 // Load property name
572 LoadAccumulatorString(node, std::get<util::StringView>(property));
573 }
574
575 propNameObject = AllocReg();
576 StoreAccumulator(node, propNameObject);
577
578 // Get property
579 Ra().Emit<CallShort, 2U>(node, methodName, objReg, propNameObject);
580
581 SetAccumulatorType(type);
582
583 if (propType != type && !propType->IsETSDynamicType()) {
584 CastDynamicToObject(node, propType);
585 }
586 }
587
StoreElementDynamic(const ir::AstNode * node,VReg objectReg,VReg index)588 void ETSGen::StoreElementDynamic(const ir::AstNode *node, VReg objectReg, VReg index)
589 {
590 ES2PANDA_ASSERT(GetVRegType(objectReg) != nullptr);
591 auto const lang = GetVRegType(objectReg)->AsETSDynamicType()->Language();
592 std::string_view methodName = Signatures::Dynamic::SetElementDynamicBuiltin(lang);
593
594 RegScope rs(this);
595
596 VReg valueReg = AllocReg();
597 StoreAccumulator(node, valueReg);
598
599 // Set property by index
600 Ra().Emit<Call, 3U>(node, methodName, objectReg, index, valueReg, dummyReg_);
601 SetAccumulatorType(Checker()->GlobalVoidType());
602 }
603
LoadElementDynamic(const ir::AstNode * node,VReg objectReg)604 void ETSGen::LoadElementDynamic(const ir::AstNode *node, VReg objectReg)
605 {
606 ES2PANDA_ASSERT(GetVRegType(objectReg) != nullptr);
607 auto const lang = GetVRegType(objectReg)->AsETSDynamicType()->Language();
608 std::string_view methodName = Signatures::Dynamic::GetElementDynamicBuiltin(lang);
609
610 RegScope rs(this);
611
612 VReg indexReg = AllocReg();
613 StoreAccumulator(node, indexReg);
614
615 // Get property by index
616 Ra().Emit<CallShort, 2U>(node, methodName, objectReg, indexReg);
617 SetAccumulatorType(Checker()->GlobalBuiltinDynamicType(lang));
618 }
619
CallRangeFillUndefined(const ir::AstNode * const node,checker::Signature * const signature,const VReg thisReg)620 void ETSGen::CallRangeFillUndefined(const ir::AstNode *const node, checker::Signature *const signature,
621 const VReg thisReg)
622 {
623 RegScope rs(this);
624 ES2PANDA_ASSERT(signature->MinArgCount() == 0);
625
626 auto undef = AllocReg();
627 LoadAccumulatorUndefined(node);
628 StoreAccumulator(node, undef);
629
630 VReg const argStart = NextReg();
631 Ra().Emit<MovObj>(node, AllocReg(), thisReg);
632
633 for (size_t idx = 0; idx < signature->ArgCount(); idx++) {
634 Ra().Emit<MovObj>(node, AllocReg(), undef);
635 }
636 Rra().Emit<CallRange>(node, argStart, signature->ArgCount() + 1, signature->InternalName(), argStart);
637 }
638
LoadUndefinedDynamic(const ir::AstNode * node,Language lang)639 void ETSGen::LoadUndefinedDynamic(const ir::AstNode *node, Language lang)
640 {
641 RegScope rs(this);
642 Ra().Emit<CallShort, 0>(node, Signatures::Dynamic::GetUndefinedBuiltin(lang), dummyReg_, dummyReg_);
643 SetAccumulatorType(Checker()->GlobalBuiltinDynamicType(lang));
644 }
645
LoadThis(const ir::AstNode * node)646 void ETSGen::LoadThis(const ir::AstNode *node)
647 {
648 LoadAccumulator(node, GetThisReg());
649 }
650
CreateBigIntObject(const ir::AstNode * node,VReg arg0,std::string_view signature)651 void ETSGen::CreateBigIntObject(const ir::AstNode *node, VReg arg0, std::string_view signature)
652 {
653 Ra().Emit<InitobjShort>(node, signature, arg0, dummyReg_);
654 }
655
GetThisReg() const656 VReg ETSGen::GetThisReg() const
657 {
658 const auto res = Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS);
659 return res.variable->AsLocalVariable()->Vreg();
660 }
661
LoadDefaultValue(const ir::AstNode * node,const checker::Type * type)662 const checker::Type *ETSGen::LoadDefaultValue(const ir::AstNode *node, const checker::Type *type)
663 {
664 if (type->IsETSAsyncFuncReturnType()) {
665 LoadDefaultValue(node, type->AsETSAsyncFuncReturnType()->GetPromiseTypeArg());
666 return type;
667 }
668
669 auto const checker = const_cast<checker::ETSChecker *>(Checker());
670
671 if (type->IsETSReferenceType()) {
672 if (checker->Relation()->IsSupertypeOf(type, checker->GlobalETSUndefinedType())) {
673 LoadAccumulatorUndefined(node);
674 } else {
675 LoadAccumulatorPoison(node, type);
676 }
677 return type;
678 }
679
680 if (type->IsETSBooleanType()) {
681 LoadAccumulatorBoolean(node, type->AsETSBooleanType()->GetValue());
682 } else {
683 const auto ttctx = TargetTypeContext(this, type);
684 LoadAccumulatorInt(node, 0);
685 }
686 return type;
687 }
688
EmitReturnVoid(const ir::AstNode * node)689 void ETSGen::EmitReturnVoid(const ir::AstNode *node)
690 {
691 Sa().Emit<ReturnVoid>(node);
692 }
693
ReturnAcc(const ir::AstNode * node)694 void ETSGen::ReturnAcc(const ir::AstNode *node)
695 {
696 const auto *const accType = GetAccumulatorType();
697 ES2PANDA_ASSERT(accType != nullptr);
698
699 if (accType->IsETSReferenceType()) {
700 Sa().Emit<ReturnObj>(node);
701 } else if (IsWidePrimitiveType(accType)) {
702 Sa().Emit<ReturnWide>(node);
703 } else {
704 Sa().Emit<Return>(node);
705 }
706 }
707
IsNullUnsafeObjectType(checker::Type const * type)708 static bool IsNullUnsafeObjectType(checker::Type const *type)
709 {
710 ES2PANDA_ASSERT(type != nullptr);
711 return type->IsETSObjectType() && type->AsETSObjectType()->IsGlobalETSObjectType();
712 }
713
IsInstanceDynamic(const ir::BinaryExpression * const node,const VReg srcReg,const VReg tgtReg)714 void ETSGen::IsInstanceDynamic(const ir::BinaryExpression *const node, const VReg srcReg,
715 [[maybe_unused]] const VReg tgtReg)
716 {
717 ES2PANDA_ASSERT(node->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF);
718 const checker::Type *lhsType = node->Left()->TsType();
719 const checker::Type *rhsType = node->Right()->TsType();
720 ES2PANDA_ASSERT(rhsType->IsETSDynamicType() || lhsType->IsETSDynamicType());
721
722 const RegScope rs(this);
723 if (rhsType->IsETSDynamicType()) {
724 ES2PANDA_ASSERT(node->Right()->TsType()->AsETSDynamicType()->HasDecl());
725 if (lhsType->IsETSDynamicType()) {
726 VReg dynTypeReg = MoveAccToReg(node);
727 // Semantics:
728 // let dyn_val: JSValue = ...
729 // dyn_value instanceof DynamicDecl
730 // Bytecode:
731 // call runtime intrinsic_dynamic
732 CallExact(node, Signatures::BUILTIN_JSRUNTIME_INSTANCE_OF_DYNAMIC, srcReg, dynTypeReg);
733 } else if (lhsType == Checker()->GlobalETSObjectType()) {
734 // Semantics:
735 // let obj: Object = ...
736 // obj instanceof DynamicDecl
737 // Bytecode:
738 // if isinstance <dynamic type name>:
739 // checkcast <dynamic type name>
740 // return call runtime intrinsic_dynamic
741 // return false
742 Label *ifFalse = AllocLabel();
743 Language lang = rhsType->AsETSDynamicType()->Language();
744 ES2PANDA_ASSERT(Checker()->GlobalBuiltinDynamicType(lang) != nullptr);
745 VReg dynTypeReg = MoveAccToReg(node);
746 LoadAccumulator(node, srcReg);
747 EmitIsInstance(node, Checker()->GlobalBuiltinDynamicType(lang)->AssemblerName());
748 BranchIfFalse(node, ifFalse);
749 LoadAccumulator(node, srcReg);
750 EmitCheckCast(node, Checker()->GlobalBuiltinDynamicType(lang)->AssemblerName());
751 CallExact(node, Signatures::BUILTIN_JSRUNTIME_INSTANCE_OF_DYNAMIC, srcReg, dynTypeReg);
752 SetLabel(node, ifFalse);
753 } else {
754 // Semantics:
755 // let obj: EtsType = ...
756 // obj instanceof DynamicDecl
757 // Bytecode:
758 // False
759 Sa().Emit<Ldai>(node, 0);
760 }
761 } else {
762 if (lhsType->IsETSDynamicType()) {
763 if (rhsType == Checker()->GlobalETSObjectType()) {
764 // Semantics:
765 // let dyn_val: JSValue = ...
766 // dyn_val instanceof Object
767 // Bytecode:
768 // True
769 Sa().Emit<Ldai>(node, 1);
770 } else {
771 // Semantics:
772 // let dyn_val: JSValue = ...
773 // dyn_val instanceof EtsType
774 // Bytecode:
775 // lda.type + call runtime instrinsic_static
776 Sa().Emit<LdaType>(node, rhsType->AsETSObjectType()->AssemblerName());
777 VReg typeReg = MoveAccToReg(node);
778 CallExact(node, Signatures::BUILTIN_JSRUNTIME_INSTANCE_OF_STATIC, srcReg, typeReg);
779 }
780 } else {
781 ES2PANDA_UNREACHABLE();
782 }
783 }
784 SetAccumulatorType(Checker()->GlobalETSBooleanType());
785 }
786
787 // Implemented on top of the runtime type system, do not relax checks, do not introduce new types
TestIsInstanceConstituent(const ir::AstNode * const node,std::tuple<Label *,Label * > label,checker::Type const * target,bool acceptNull)788 void ETSGen::TestIsInstanceConstituent(const ir::AstNode *const node, std::tuple<Label *, Label *> label,
789 checker::Type const *target, bool acceptNull)
790 {
791 ES2PANDA_ASSERT(target != nullptr);
792 ES2PANDA_ASSERT(!target->IsETSDynamicType());
793 auto [ifTrue, ifFalse] = label;
794
795 switch (checker::ETSChecker::ETSType(target)) {
796 case checker::TypeFlag::ETS_UNDEFINED:
797 case checker::TypeFlag::ETS_VOID:
798 BranchIfUndefined(node, ifTrue);
799 break;
800 case checker::TypeFlag::ETS_NULL:
801 BranchIfNull(node, ifTrue);
802 break;
803 case checker::TypeFlag::ETS_OBJECT:
804 if (!IsNullUnsafeObjectType(target)) {
805 EmitIsInstance(node, ToAssemblerType(target));
806 BranchIfTrue(node, ifTrue);
807 break;
808 }
809 if (!acceptNull) {
810 BranchIfNull(node, ifFalse);
811 }
812 JumpTo(node, ifTrue);
813 break;
814 case checker::TypeFlag::ETS_ARRAY:
815 case checker::TypeFlag::ETS_TUPLE:
816 case checker::TypeFlag::FUNCTION: {
817 EmitIsInstance(node, ToAssemblerType(target));
818 BranchIfTrue(node, ifTrue);
819 break;
820 }
821 default:
822 ES2PANDA_UNREACHABLE(); // other types must not appear here
823 }
824 SetAccumulatorType(nullptr);
825 }
826
827 // Implemented on top of the runtime type system, do not relax checks, do not introduce new types
BranchIfIsInstance(const ir::AstNode * const node,const VReg srcReg,const checker::Type * target,Label * ifTrue)828 void ETSGen::BranchIfIsInstance(const ir::AstNode *const node, const VReg srcReg, const checker::Type *target,
829 Label *ifTrue)
830 {
831 ES2PANDA_ASSERT(target == Checker()->GetApparentType(target));
832 auto ifFalse = AllocLabel();
833
834 if (!target->PossiblyETSUndefined()) {
835 LoadAccumulator(node, srcReg);
836 BranchIfUndefined(node, ifFalse);
837 }
838
839 auto const checkType = [this, srcReg, ifTrue, ifFalse, acceptNull = target->PossiblyETSNull()](
840 const ir::AstNode *const n, checker::Type const *t) {
841 LoadAccumulator(n, srcReg);
842 // #21835: type-alias in ApparentType
843 t = t->IsETSTypeAliasType() ? t->AsETSTypeAliasType()->GetTargetType() : t;
844 TestIsInstanceConstituent(n, std::tie(ifTrue, ifFalse), t, acceptNull);
845 };
846
847 if (target->IsETSUnionType()) {
848 for (auto *ct : target->AsETSUnionType()->ConstituentTypes()) {
849 checkType(node, ct);
850 }
851 } else if (!target->IsETSNeverType()) {
852 checkType(node, target);
853 }
854
855 SetLabel(node, ifFalse);
856 SetAccumulatorType(nullptr);
857 }
858
859 // Implemented on top of the runtime type system, do not relax checks, do not introduce new types
IsInstance(const ir::AstNode * const node,const VReg srcReg,const checker::Type * target)860 void ETSGen::IsInstance(const ir::AstNode *const node, const VReg srcReg, const checker::Type *target)
861 {
862 target = Checker()->GetApparentType(target);
863 ES2PANDA_ASSERT(target != nullptr);
864 ES2PANDA_ASSERT(target->IsETSReferenceType() && GetAccumulatorType() != nullptr);
865
866 if (target->IsETSAnyType()) { // should be IsSupertypeOf(target, source)
867 LoadAccumulatorBoolean(node, true);
868 return;
869 }
870 if (target->IsETSArrayType() ||
871 (target->IsETSObjectType() && !(IsNullUnsafeObjectType(target) && GetAccumulatorType()->PossiblyETSNull()))) {
872 InternalIsInstance(node, target);
873 return;
874 }
875
876 auto ifTrue = AllocLabel();
877 auto end = AllocLabel();
878
879 BranchIfIsInstance(node, srcReg, target, ifTrue);
880 LoadAccumulatorBoolean(node, false);
881 JumpTo(node, end);
882
883 SetLabel(node, ifTrue);
884 LoadAccumulatorBoolean(node, true);
885 SetLabel(node, end);
886 }
887
888 // isinstance can only be used for Object and [] types, ensure source is not null!
InternalIsInstance(const ir::AstNode * node,const es2panda::checker::Type * target)889 void ETSGen::InternalIsInstance(const ir::AstNode *node, const es2panda::checker::Type *target)
890 {
891 ES2PANDA_ASSERT(target != nullptr);
892 ES2PANDA_ASSERT(target->IsETSObjectType() || target->IsETSArrayType());
893 if (!IsNullUnsafeObjectType(target)) {
894 EmitIsInstance(node, ToAssemblerType(target));
895 SetAccumulatorType(Checker()->GlobalETSBooleanType());
896 } else {
897 LoadAccumulatorBoolean(node, true);
898 }
899 }
900
901 // checkcast can only be used for Object and [] types, ensure source is not nullish!
InternalCheckCast(const ir::AstNode * node,const es2panda::checker::Type * target)902 void ETSGen::InternalCheckCast(const ir::AstNode *node, const es2panda::checker::Type *target)
903 {
904 ES2PANDA_ASSERT(target != nullptr);
905 ES2PANDA_ASSERT(target->IsETSObjectType() || target->IsETSArrayType() || target->IsETSTupleType());
906 if (!IsNullUnsafeObjectType(target)) {
907 EmitCheckCast(node, ToAssemblerType(target));
908 }
909 SetAccumulatorType(target);
910 }
911
912 // optimized specialization for object and [] targets
CheckedReferenceNarrowingObject(const ir::AstNode * node,const checker::Type * target)913 void ETSGen::CheckedReferenceNarrowingObject(const ir::AstNode *node, const checker::Type *target)
914 {
915 ES2PANDA_ASSERT(target != nullptr);
916 ES2PANDA_ASSERT(target->IsETSObjectType() || target->IsETSArrayType() || target->IsETSTupleType());
917 const RegScope rs(this);
918 const auto srcReg = AllocReg();
919 StoreAccumulator(node, srcReg);
920
921 auto isNullish = AllocLabel();
922 auto end = AllocLabel();
923 bool nullishCheck = false;
924
925 auto *source = GetAccumulatorType();
926 ES2PANDA_ASSERT(source != nullptr);
927 if (source->PossiblyETSUndefined()) {
928 nullishCheck = true;
929 BranchIfUndefined(node, isNullish);
930 }
931 if (source->PossiblyETSNull() && IsNullUnsafeObjectType(target)) {
932 nullishCheck = true;
933 BranchIfNull(node, isNullish);
934 }
935
936 if (!nullishCheck) {
937 InternalCheckCast(node, target);
938 } else {
939 LoadAccumulator(node, srcReg);
940 InternalCheckCast(node, target);
941 JumpTo(node, end);
942
943 SetLabel(node, isNullish);
944 EmitFailedTypeCastException(node, srcReg, target);
945
946 SetLabel(node, end);
947 SetAccumulatorType(target);
948 }
949 }
950
951 // Implemented on top of the runtime type system, do not relax checks, do not introduce new types
CheckedReferenceNarrowing(const ir::AstNode * node,const checker::Type * target)952 void ETSGen::CheckedReferenceNarrowing(const ir::AstNode *node, const checker::Type *target)
953 {
954 ES2PANDA_ASSERT(target != nullptr);
955 // NOTE(vpukhov): #19701 void refactoring
956 if (target->IsETSVoidType()) {
957 SetAccumulatorType(target);
958 return;
959 }
960
961 target = Checker()->GetApparentType(target);
962 ES2PANDA_ASSERT(target != nullptr);
963 ES2PANDA_ASSERT(target->IsETSReferenceType());
964 if (target->IsETSAnyType()) { // should be IsSupertypeOf(target, source)
965 SetAccumulatorType(target);
966 return;
967 }
968 if (target->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT | checker::TypeFlag::ETS_TUPLE) &&
969 !target->IsConstantType()) {
970 CheckedReferenceNarrowingObject(node, target);
971 return;
972 }
973
974 const RegScope rs(this);
975 const auto srcReg = AllocReg();
976 auto ifTrue = AllocLabel();
977
978 StoreAccumulator(node, srcReg);
979 BranchIfIsInstance(node, srcReg, target, ifTrue);
980
981 EmitFailedTypeCastException(node, srcReg, target);
982
983 SetLabel(node, ifTrue);
984 LoadAccumulator(node, srcReg);
985 // Verifier can't infer type if isinstance met, help him
986 EmitCheckCast(node, ToAssemblerType(target));
987 SetAccumulatorType(target);
988 }
989
GuardUncheckedType(const ir::AstNode * node,const checker::Type * unchecked,const checker::Type * target)990 void ETSGen::GuardUncheckedType(const ir::AstNode *node, const checker::Type *unchecked, const checker::Type *target)
991 {
992 if (unchecked != nullptr) {
993 SetAccumulatorType(unchecked);
994 // this check guards possible type violations, **do not relax it**
995 CheckedReferenceNarrowing(node, Checker()->MaybeBoxType(target));
996 }
997 SetAccumulatorType(target);
998 }
999
EmitFailedTypeCastException(const ir::AstNode * node,const VReg src,checker::Type const * target)1000 void ETSGen::EmitFailedTypeCastException(const ir::AstNode *node, const VReg src, checker::Type const *target)
1001 {
1002 const RegScope rs(this);
1003 const auto errorReg = AllocReg();
1004
1005 LoadAccumulatorString(node, util::UString(target->ToString(), Allocator()).View());
1006 Ra().Emit<CallAccShort, 1>(node, Signatures::BUILTIN_RUNTIME_FAILED_TYPE_CAST_EXCEPTION, src, 1);
1007 StoreAccumulator(node, errorReg);
1008 EmitThrow(node, errorReg);
1009 SetAccumulatorType(nullptr);
1010 }
1011
LoadConstantObject(const ir::Expression * node,const checker::Type * type)1012 void ETSGen::LoadConstantObject(const ir::Expression *node, const checker::Type *type)
1013 {
1014 if (type->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL)) {
1015 LoadAccumulatorBigInt(node, type->AsETSObjectType()->AsETSBigIntType()->GetValue());
1016 const VReg value = AllocReg();
1017 StoreAccumulator(node, value);
1018 CreateBigIntObject(node, value);
1019 } else {
1020 LoadAccumulatorString(node, type->AsETSObjectType()->AsETSStringType()->GetValue());
1021 SetAccumulatorType(node->TsType());
1022 }
1023 }
1024
ApplyConversionCast(const ir::AstNode * node,const checker::Type * targetType)1025 void ETSGen::ApplyConversionCast(const ir::AstNode *node, const checker::Type *targetType)
1026 {
1027 switch (checker::ETSChecker::TypeKind(targetType)) {
1028 case checker::TypeFlag::DOUBLE: {
1029 CastToDouble(node);
1030 break;
1031 }
1032 case checker::TypeFlag::FLOAT: {
1033 CastToFloat(node);
1034 break;
1035 }
1036 case checker::TypeFlag::LONG: {
1037 CastToLong(node);
1038 break;
1039 }
1040 case checker::TypeFlag::INT: {
1041 CastToInt(node);
1042 break;
1043 }
1044 case checker::TypeFlag::CHAR: {
1045 CastToChar(node);
1046 break;
1047 }
1048 case checker::TypeFlag::SHORT: {
1049 CastToShort(node);
1050 break;
1051 }
1052 case checker::TypeFlag::BYTE: {
1053 CastToByte(node);
1054 break;
1055 }
1056 case checker::TypeFlag::ETS_ARRAY:
1057 case checker::TypeFlag::ETS_OBJECT:
1058 case checker::TypeFlag::ETS_TYPE_PARAMETER: {
1059 ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
1060 if (GetAccumulatorType() != nullptr && GetAccumulatorType()->IsETSDynamicType()) {
1061 CastDynamicToObject(node, targetType);
1062 }
1063 break;
1064 }
1065 case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1066 CastToDynamic(node, targetType->AsETSDynamicType());
1067 break;
1068 }
1069 default: {
1070 break;
1071 }
1072 }
1073 }
1074
ApplyBoxingConversion(const ir::AstNode * node)1075 void ETSGen::ApplyBoxingConversion(const ir::AstNode *node)
1076 {
1077 EmitBoxingConversion(node);
1078 node->SetBoxingUnboxingFlags(
1079 static_cast<ir::BoxingUnboxingFlags>(node->GetBoxingUnboxingFlags() & ~(ir::BoxingUnboxingFlags::BOXING_FLAG)));
1080 }
1081
ApplyUnboxingConversion(const ir::AstNode * node)1082 void ETSGen::ApplyUnboxingConversion(const ir::AstNode *node)
1083 {
1084 auto const callUnbox = [this, node](std::string_view sig, checker::Type const *unboxedType) {
1085 auto boxedType = Checker()->MaybeBoxType(unboxedType)->AsETSObjectType();
1086 EmitUnboxedCall(node, sig, unboxedType, boxedType);
1087 };
1088
1089 auto const unboxFlags =
1090 ir::BoxingUnboxingFlags(node->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG);
1091 node->RemoveBoxingUnboxingFlags(ir::BoxingUnboxingFlags::UNBOXING_FLAG);
1092
1093 switch (unboxFlags) {
1094 case ir::BoxingUnboxingFlags::UNBOX_TO_BOOLEAN:
1095 callUnbox(Signatures::BUILTIN_BOOLEAN_UNBOXED, Checker()->GlobalETSBooleanType());
1096 return;
1097 case ir::BoxingUnboxingFlags::UNBOX_TO_BYTE:
1098 callUnbox(Signatures::BUILTIN_BYTE_UNBOXED, Checker()->GlobalByteType());
1099 return;
1100 case ir::BoxingUnboxingFlags::UNBOX_TO_CHAR:
1101 callUnbox(Signatures::BUILTIN_CHAR_UNBOXED, Checker()->GlobalCharType());
1102 return;
1103 case ir::BoxingUnboxingFlags::UNBOX_TO_SHORT:
1104 callUnbox(Signatures::BUILTIN_SHORT_UNBOXED, Checker()->GlobalShortType());
1105 return;
1106 case ir::BoxingUnboxingFlags::UNBOX_TO_INT:
1107 callUnbox(Signatures::BUILTIN_INT_UNBOXED, Checker()->GlobalIntType());
1108 return;
1109 case ir::BoxingUnboxingFlags::UNBOX_TO_LONG:
1110 callUnbox(Signatures::BUILTIN_LONG_UNBOXED, Checker()->GlobalLongType());
1111 return;
1112 case ir::BoxingUnboxingFlags::UNBOX_TO_FLOAT:
1113 callUnbox(Signatures::BUILTIN_FLOAT_UNBOXED, Checker()->GlobalFloatType());
1114 return;
1115 case ir::BoxingUnboxingFlags::UNBOX_TO_DOUBLE:
1116 callUnbox(Signatures::BUILTIN_DOUBLE_UNBOXED, Checker()->GlobalDoubleType());
1117 return;
1118 default:
1119 ES2PANDA_UNREACHABLE();
1120 }
1121 }
1122
ApplyConversion(const ir::AstNode * node,const checker::Type * targetType)1123 void ETSGen::ApplyConversion(const ir::AstNode *node, const checker::Type *targetType)
1124 {
1125 auto ttctx = TargetTypeContext(this, targetType);
1126
1127 const bool hasBoxingflags = (node->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::BOXING_FLAG) != 0U;
1128 const bool hasUnboxingflags = (node->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0U;
1129 if (hasBoxingflags && !hasUnboxingflags) {
1130 ApplyBoxingConversion(node);
1131 return;
1132 }
1133
1134 if (hasUnboxingflags) {
1135 ApplyUnboxingConversion(node);
1136 }
1137
1138 if (targetType == nullptr) {
1139 return;
1140 }
1141
1142 ApplyConversionCast(node, targetType);
1143
1144 if (hasBoxingflags) {
1145 ApplyBoxingConversion(node);
1146 }
1147 }
1148
ApplyCast(const ir::AstNode * node,const checker::Type * targetType)1149 void ETSGen::ApplyCast(const ir::AstNode *node, const checker::Type *targetType)
1150 {
1151 auto typeKind = checker::ETSChecker::TypeKind(targetType);
1152
1153 switch (typeKind) {
1154 case checker::TypeFlag::DOUBLE: {
1155 CastToDouble(node);
1156 break;
1157 }
1158 case checker::TypeFlag::FLOAT: {
1159 CastToFloat(node);
1160 break;
1161 }
1162 case checker::TypeFlag::LONG: {
1163 CastToLong(node);
1164 break;
1165 }
1166 case checker::TypeFlag::INT: {
1167 CastToInt(node);
1168 break;
1169 }
1170 case checker::TypeFlag::SHORT: {
1171 CastToShort(node);
1172 break;
1173 }
1174 case checker::TypeFlag::BYTE: {
1175 CastToByte(node);
1176 break;
1177 }
1178 case checker::TypeFlag::CHAR: {
1179 CastToChar(node);
1180 break;
1181 }
1182 case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1183 CastToDynamic(node, targetType->AsETSDynamicType());
1184 break;
1185 }
1186 default: {
1187 break;
1188 }
1189 }
1190 }
1191
ApplyCastToBoxingFlags(const ir::AstNode * node,const ir::BoxingUnboxingFlags targetType)1192 void ETSGen::ApplyCastToBoxingFlags(const ir::AstNode *node, const ir::BoxingUnboxingFlags targetType)
1193 {
1194 switch (targetType) {
1195 case ir::BoxingUnboxingFlags::BOX_TO_DOUBLE: {
1196 CastToDouble(node);
1197 break;
1198 }
1199 case ir::BoxingUnboxingFlags::BOX_TO_FLOAT: {
1200 CastToFloat(node);
1201 break;
1202 }
1203 case ir::BoxingUnboxingFlags::BOX_TO_LONG: {
1204 CastToLong(node);
1205 break;
1206 }
1207 case ir::BoxingUnboxingFlags::BOX_TO_INT: {
1208 CastToInt(node);
1209 break;
1210 }
1211 case ir::BoxingUnboxingFlags::BOX_TO_BYTE: {
1212 CastToByte(node);
1213 break;
1214 }
1215 default: {
1216 break;
1217 }
1218 }
1219 }
1220
EmitUnboxedCall(const ir::AstNode * node,std::string_view signatureFlag,const checker::Type * const targetType,const checker::Type * const boxedType)1221 void ETSGen::EmitUnboxedCall(const ir::AstNode *node, std::string_view signatureFlag,
1222 const checker::Type *const targetType, const checker::Type *const boxedType)
1223 {
1224 RegScope rs(this);
1225 // NOTE(vpukhov): #20510 lowering
1226 if (node->HasAstNodeFlags(ir::AstNodeFlags::CHECKCAST)) {
1227 CheckedReferenceNarrowing(node, boxedType);
1228 }
1229
1230 // to cast to primitive types we probably have to cast to corresponding boxed built-in types first.
1231 auto *const checker = Checker()->AsETSChecker();
1232 auto const *accumulatorType = GetAccumulatorType();
1233 ES2PANDA_ASSERT(accumulatorType != nullptr);
1234 if (accumulatorType->IsETSObjectType() && //! accumulatorType->DefinitelyNotETSNullish() &&
1235 !checker->Relation()->IsIdenticalTo(const_cast<checker::Type *>(accumulatorType),
1236 const_cast<checker::Type *>(boxedType))) {
1237 CastToReftype(node, boxedType, false);
1238 }
1239
1240 Ra().Emit<CallAccShort, 0>(node, signatureFlag, dummyReg_, 0);
1241 SetAccumulatorType(targetType);
1242 if (node->IsExpression()) {
1243 const_cast<ir::Expression *>(node->AsExpression())->SetTsType(const_cast<checker::Type *>(targetType));
1244 }
1245 }
1246
1247 // NOTE(vpukhov): #20510 should be available only as a part of ApplyBoxingConversion
EmitBoxingConversion(ir::BoxingUnboxingFlags boxingFlag,const ir::AstNode * node)1248 void ETSGen::EmitBoxingConversion(ir::BoxingUnboxingFlags boxingFlag, const ir::AstNode *node)
1249 {
1250 auto const callBox = [this, node](std::string_view sig, checker::Type const *unboxedType) {
1251 Ra().Emit<CallAccShort, 0>(node, sig, dummyReg_, 0);
1252 SetAccumulatorType(Checker()->MaybeBoxType(unboxedType)->AsETSObjectType());
1253 };
1254
1255 switch (boxingFlag) {
1256 case ir::BoxingUnboxingFlags::BOX_TO_BOOLEAN:
1257 callBox(Signatures::BUILTIN_BOOLEAN_VALUE_OF, Checker()->GlobalETSBooleanType());
1258 return;
1259 case ir::BoxingUnboxingFlags::BOX_TO_BYTE:
1260 callBox(Signatures::BUILTIN_BYTE_VALUE_OF, Checker()->GlobalByteType());
1261 return;
1262 case ir::BoxingUnboxingFlags::BOX_TO_CHAR:
1263 callBox(Signatures::BUILTIN_CHAR_VALUE_OF, Checker()->GlobalCharType());
1264 return;
1265 case ir::BoxingUnboxingFlags::BOX_TO_SHORT:
1266 callBox(Signatures::BUILTIN_SHORT_VALUE_OF, Checker()->GlobalShortType());
1267 return;
1268 case ir::BoxingUnboxingFlags::BOX_TO_INT:
1269 callBox(Signatures::BUILTIN_INT_VALUE_OF, Checker()->GlobalIntType());
1270 return;
1271 case ir::BoxingUnboxingFlags::BOX_TO_LONG:
1272 callBox(Signatures::BUILTIN_LONG_VALUE_OF, Checker()->GlobalLongType());
1273 return;
1274 case ir::BoxingUnboxingFlags::BOX_TO_FLOAT:
1275 callBox(Signatures::BUILTIN_FLOAT_VALUE_OF, Checker()->GlobalFloatType());
1276 return;
1277 case ir::BoxingUnboxingFlags::BOX_TO_DOUBLE:
1278 callBox(Signatures::BUILTIN_DOUBLE_VALUE_OF, Checker()->GlobalDoubleType());
1279 return;
1280 default:
1281 ES2PANDA_UNREACHABLE();
1282 }
1283 }
1284
1285 // NOTE(vpukhov): #20510 should be available only as a part of ApplyBoxingConversion
EmitBoxingConversion(const ir::AstNode * node)1286 void ETSGen::EmitBoxingConversion(const ir::AstNode *node)
1287 {
1288 auto boxingFlag =
1289 static_cast<ir::BoxingUnboxingFlags>(ir::BoxingUnboxingFlags::BOXING_FLAG & node->GetBoxingUnboxingFlags());
1290
1291 RegScope rs(this);
1292
1293 ApplyCastToBoxingFlags(node, boxingFlag);
1294
1295 EmitBoxingConversion(boxingFlag, node);
1296
1297 if (node->IsExpression()) {
1298 auto boxedType = const_cast<checker::Type *>(GetAccumulatorType());
1299 const_cast<ir::Expression *>(node->AsExpression())->SetTsType(boxedType);
1300 }
1301 }
1302
SwapBinaryOpArgs(const ir::AstNode * const node,const VReg lhs)1303 void ETSGen::SwapBinaryOpArgs(const ir::AstNode *const node, const VReg lhs)
1304 {
1305 const RegScope rs(this);
1306 const auto tmp = AllocReg();
1307
1308 StoreAccumulator(node, tmp);
1309 LoadAccumulator(node, lhs);
1310 MoveVreg(node, lhs, tmp);
1311 }
1312
MoveAccToReg(const ir::AstNode * const node)1313 VReg ETSGen::MoveAccToReg(const ir::AstNode *const node)
1314 {
1315 const auto newReg = AllocReg();
1316 StoreAccumulator(node, newReg);
1317 return newReg;
1318 }
1319
CastToBoolean(const ir::AstNode * node)1320 void ETSGen::CastToBoolean([[maybe_unused]] const ir::AstNode *node)
1321 {
1322 auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1323 switch (typeKind) {
1324 case checker::TypeFlag::ETS_BOOLEAN: {
1325 return;
1326 }
1327 case checker::TypeFlag::CHAR: {
1328 Sa().Emit<U32tou1>(node);
1329 break;
1330 }
1331 case checker::TypeFlag::BYTE:
1332 case checker::TypeFlag::SHORT:
1333 case checker::TypeFlag::INT: {
1334 Sa().Emit<I32tou1>(node);
1335 return;
1336 }
1337 case checker::TypeFlag::LONG: {
1338 Sa().Emit<I64tou1>(node);
1339 break;
1340 }
1341 case checker::TypeFlag::FLOAT: {
1342 Sa().Emit<F32toi32>(node);
1343 Sa().Emit<I32tou1>(node);
1344 break;
1345 }
1346 case checker::TypeFlag::DOUBLE: {
1347 Sa().Emit<F64toi32>(node);
1348 Sa().Emit<I32tou1>(node);
1349 break;
1350 }
1351 case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1352 CastDynamicTo(node, checker::TypeFlag::ETS_BOOLEAN);
1353 ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalETSBooleanType());
1354 break;
1355 }
1356 default: {
1357 ES2PANDA_UNREACHABLE();
1358 }
1359 }
1360
1361 SetAccumulatorType(Checker()->GlobalETSBooleanType());
1362 }
1363
CastToByte(const ir::AstNode * node)1364 void ETSGen::CastToByte([[maybe_unused]] const ir::AstNode *node)
1365 {
1366 auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1367 switch (typeKind) {
1368 case checker::TypeFlag::BYTE: {
1369 return;
1370 }
1371 case checker::TypeFlag::ETS_BOOLEAN:
1372 case checker::TypeFlag::CHAR: {
1373 Sa().Emit<U32toi8>(node);
1374 break;
1375 }
1376 case checker::TypeFlag::SHORT:
1377 case checker::TypeFlag::INT: {
1378 Sa().Emit<I32toi8>(node);
1379 break;
1380 }
1381 case checker::TypeFlag::LONG: {
1382 Sa().Emit<I64toi32>(node);
1383 Sa().Emit<I32toi8>(node);
1384 break;
1385 }
1386 case checker::TypeFlag::FLOAT: {
1387 Sa().Emit<F32toi32>(node);
1388 Sa().Emit<I32toi8>(node);
1389 break;
1390 }
1391 case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1392 CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1393 ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1394 [[fallthrough]];
1395 }
1396 case checker::TypeFlag::DOUBLE: {
1397 Sa().Emit<F64toi32>(node);
1398 Sa().Emit<I32toi8>(node);
1399 break;
1400 }
1401 case checker::TypeFlag::ETS_OBJECT: {
1402 break;
1403 }
1404 default: {
1405 ES2PANDA_UNREACHABLE();
1406 }
1407 }
1408
1409 SetAccumulatorType(Checker()->GlobalByteType());
1410 }
1411
CastToChar(const ir::AstNode * node)1412 void ETSGen::CastToChar([[maybe_unused]] const ir::AstNode *node)
1413 {
1414 auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1415 switch (typeKind) {
1416 case checker::TypeFlag::CHAR: {
1417 if (node->IsCharLiteral()) {
1418 auto type = node->AsCharLiteral()->TsType();
1419 if (type->TypeFlags() == (checker::TypeFlag::CONSTANT | checker::TypeFlag::BYTE)) {
1420 SetAccumulatorType(type);
1421 }
1422 }
1423 return;
1424 }
1425 case checker::TypeFlag::ETS_BOOLEAN: {
1426 break;
1427 }
1428 case checker::TypeFlag::BYTE:
1429 case checker::TypeFlag::SHORT:
1430 case checker::TypeFlag::INT: {
1431 Sa().Emit<I32tou16>(node);
1432 break;
1433 }
1434 case checker::TypeFlag::LONG: {
1435 Sa().Emit<I64toi32>(node);
1436 Sa().Emit<I32tou16>(node);
1437 break;
1438 }
1439 case checker::TypeFlag::FLOAT: {
1440 Sa().Emit<F32toi32>(node);
1441 Sa().Emit<I32tou16>(node);
1442 break;
1443 }
1444 case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1445 CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1446 ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1447 [[fallthrough]];
1448 }
1449 case checker::TypeFlag::DOUBLE: {
1450 Sa().Emit<F64toi32>(node);
1451 Sa().Emit<I32tou16>(node);
1452 break;
1453 }
1454 case checker::TypeFlag::ETS_OBJECT: {
1455 break;
1456 }
1457 default: {
1458 ES2PANDA_UNREACHABLE();
1459 }
1460 }
1461
1462 SetAccumulatorType(Checker()->GlobalCharType());
1463 }
1464
CastToShort(const ir::AstNode * node)1465 void ETSGen::CastToShort([[maybe_unused]] const ir::AstNode *node)
1466 {
1467 auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1468 switch (typeKind) {
1469 case checker::TypeFlag::SHORT: {
1470 return;
1471 }
1472 case checker::TypeFlag::ETS_BOOLEAN:
1473 case checker::TypeFlag::CHAR: {
1474 Sa().Emit<U32toi16>(node);
1475 break;
1476 }
1477 case checker::TypeFlag::BYTE: {
1478 break;
1479 }
1480 case checker::TypeFlag::INT: {
1481 Sa().Emit<I32toi16>(node);
1482 break;
1483 }
1484 case checker::TypeFlag::LONG: {
1485 Sa().Emit<I64toi32>(node);
1486 Sa().Emit<I32toi16>(node);
1487 break;
1488 }
1489 case checker::TypeFlag::FLOAT: {
1490 Sa().Emit<F32toi32>(node);
1491 Sa().Emit<I32toi16>(node);
1492 break;
1493 }
1494 case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1495 CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1496 ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1497 [[fallthrough]];
1498 }
1499 case checker::TypeFlag::DOUBLE: {
1500 Sa().Emit<F64toi32>(node);
1501 Sa().Emit<I32toi16>(node);
1502 break;
1503 }
1504 case checker::TypeFlag::ETS_OBJECT: {
1505 break;
1506 }
1507 default: {
1508 ES2PANDA_UNREACHABLE();
1509 }
1510 }
1511
1512 SetAccumulatorType(Checker()->GlobalShortType());
1513 }
1514
CastToDouble(const ir::AstNode * node)1515 void ETSGen::CastToDouble(const ir::AstNode *node)
1516 {
1517 auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1518 switch (typeKind) {
1519 case checker::TypeFlag::DOUBLE: {
1520 return;
1521 }
1522 case checker::TypeFlag::ETS_BOOLEAN:
1523 case checker::TypeFlag::CHAR: {
1524 Sa().Emit<U32tof64>(node);
1525 break;
1526 }
1527 case checker::TypeFlag::BYTE:
1528 case checker::TypeFlag::SHORT:
1529 case checker::TypeFlag::INT: {
1530 Sa().Emit<I32tof64>(node);
1531 break;
1532 }
1533 case checker::TypeFlag::LONG: {
1534 Sa().Emit<I64tof64>(node);
1535 break;
1536 }
1537 case checker::TypeFlag::FLOAT: {
1538 Sa().Emit<F32tof64>(node);
1539 break;
1540 }
1541 case checker::TypeFlag::ETS_NEVER:
1542 case checker::TypeFlag::ETS_OBJECT: {
1543 break;
1544 }
1545 case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1546 CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1547 ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1548 break;
1549 }
1550 default: {
1551 ES2PANDA_UNREACHABLE();
1552 }
1553 }
1554
1555 SetAccumulatorType(Checker()->GlobalDoubleType());
1556 }
1557
CastToFloat(const ir::AstNode * node)1558 void ETSGen::CastToFloat(const ir::AstNode *node)
1559 {
1560 auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1561 switch (typeKind) {
1562 case checker::TypeFlag::FLOAT: {
1563 return;
1564 }
1565 case checker::TypeFlag::ETS_BOOLEAN:
1566 case checker::TypeFlag::CHAR: {
1567 Sa().Emit<U32tof32>(node);
1568 break;
1569 }
1570 case checker::TypeFlag::BYTE:
1571 case checker::TypeFlag::SHORT:
1572 case checker::TypeFlag::INT: {
1573 Sa().Emit<I32tof32>(node);
1574 break;
1575 }
1576 case checker::TypeFlag::LONG: {
1577 Sa().Emit<I64tof32>(node);
1578 break;
1579 }
1580 case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1581 CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1582 ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1583 [[fallthrough]];
1584 }
1585 case checker::TypeFlag::DOUBLE: {
1586 Sa().Emit<F64tof32>(node);
1587 break;
1588 }
1589 case checker::TypeFlag::ETS_OBJECT: {
1590 break;
1591 }
1592 default: {
1593 ES2PANDA_UNREACHABLE();
1594 }
1595 }
1596
1597 SetAccumulatorType(Checker()->GlobalFloatType());
1598 }
1599
CastToLong(const ir::AstNode * node)1600 void ETSGen::CastToLong(const ir::AstNode *node)
1601 {
1602 auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1603 switch (typeKind) {
1604 case checker::TypeFlag::LONG: {
1605 return;
1606 }
1607 case checker::TypeFlag::ETS_BOOLEAN:
1608 case checker::TypeFlag::CHAR: {
1609 Sa().Emit<U32toi64>(node);
1610 break;
1611 }
1612 case checker::TypeFlag::BYTE:
1613 case checker::TypeFlag::SHORT:
1614 case checker::TypeFlag::INT: {
1615 Sa().Emit<I32toi64>(node);
1616 break;
1617 }
1618 case checker::TypeFlag::FLOAT: {
1619 Sa().Emit<F32toi64>(node);
1620 break;
1621 }
1622 case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1623 CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1624 ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1625 [[fallthrough]];
1626 }
1627 case checker::TypeFlag::DOUBLE: {
1628 Sa().Emit<F64toi64>(node);
1629 break;
1630 }
1631 case checker::TypeFlag::ETS_ARRAY:
1632 case checker::TypeFlag::ETS_OBJECT: {
1633 break;
1634 }
1635 default: {
1636 ES2PANDA_UNREACHABLE();
1637 }
1638 }
1639
1640 SetAccumulatorType(Checker()->GlobalLongType());
1641 }
1642
CastToInt(const ir::AstNode * node)1643 void ETSGen::CastToInt(const ir::AstNode *node)
1644 {
1645 auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1646 switch (typeKind) {
1647 case checker::TypeFlag::INT: {
1648 return;
1649 }
1650 case checker::TypeFlag::ETS_BOOLEAN:
1651 case checker::TypeFlag::CHAR:
1652 case checker::TypeFlag::BYTE:
1653 case checker::TypeFlag::SHORT: {
1654 break;
1655 }
1656 case checker::TypeFlag::LONG: {
1657 Sa().Emit<I64toi32>(node);
1658 break;
1659 }
1660 case checker::TypeFlag::FLOAT: {
1661 Sa().Emit<F32toi32>(node);
1662 break;
1663 }
1664 case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1665 CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1666 ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1667 [[fallthrough]];
1668 }
1669 case checker::TypeFlag::DOUBLE: {
1670 Sa().Emit<F64toi32>(node);
1671 break;
1672 }
1673 case checker::TypeFlag::ETS_OBJECT: {
1674 break;
1675 }
1676 default: {
1677 ES2PANDA_UNREACHABLE();
1678 }
1679 }
1680
1681 SetAccumulatorType(Checker()->GlobalIntType());
1682 }
1683
CastToReftype(const ir::AstNode * const node,const checker::Type * const targetType,const bool unchecked)1684 void ETSGen::CastToReftype(const ir::AstNode *const node, const checker::Type *const targetType, const bool unchecked)
1685 {
1686 ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
1687 ES2PANDA_ASSERT(GetAccumulatorType()->IsETSReferenceType());
1688
1689 const auto *const sourceType = GetAccumulatorType();
1690 ES2PANDA_ASSERT(sourceType != nullptr);
1691
1692 if (sourceType->IsETSDynamicType()) {
1693 CastDynamicToObject(node, targetType);
1694 return;
1695 }
1696 if (targetType->IsETSDynamicType()) {
1697 CastToDynamic(node, targetType->AsETSDynamicType());
1698 return;
1699 }
1700
1701 if (targetType->IsETSStringType() && !sourceType->IsETSStringType()) {
1702 CastToString(node);
1703 }
1704
1705 if (!unchecked) {
1706 CheckedReferenceNarrowing(node, targetType);
1707 return;
1708 }
1709
1710 ES2PANDA_ASSERT(!targetType->IsETSTypeParameter() && !targetType->IsETSNonNullishType() &&
1711 !targetType->IsETSPartialTypeParameter());
1712 CheckedReferenceNarrowing(node, targetType);
1713 SetAccumulatorType(targetType);
1714 }
1715
CastDynamicToObject(const ir::AstNode * node,const checker::Type * targetType)1716 void ETSGen::CastDynamicToObject(const ir::AstNode *node, const checker::Type *targetType)
1717 {
1718 if (targetType->IsETSStringType()) {
1719 CastDynamicTo(node, checker::TypeFlag::STRING);
1720 CheckedReferenceNarrowing(node, targetType);
1721 SetAccumulatorType(targetType);
1722 return;
1723 }
1724
1725 // NOTE(vpukhov): #14626 remove, replace targetType with interface
1726 if (targetType->IsLambdaObject()) {
1727 RegScope rs(this);
1728 VReg dynObjReg = AllocReg();
1729 StoreAccumulator(node, dynObjReg);
1730 Ra().Emit<InitobjShort>(node, targetType->AsETSObjectType()->ConstructSignatures()[0]->InternalName(),
1731 dynObjReg, dummyReg_);
1732 SetAccumulatorType(targetType);
1733 return;
1734 }
1735
1736 if (targetType == Checker()->GlobalETSObjectType()) {
1737 SetAccumulatorType(targetType);
1738 return;
1739 }
1740
1741 if (targetType->IsETSDynamicType()) {
1742 SetAccumulatorType(targetType);
1743 return;
1744 }
1745
1746 // should be valid only for Object and [] types, other are workarounds
1747 // the DefinitelyETSNullish function has been used to add handling for null and undefined cases,
1748 // and this function will need to be refactored in the future.
1749 if (targetType->IsETSArrayType() || targetType->IsETSObjectType() || targetType->IsETSTypeParameter() ||
1750 targetType->IsETSUnionType() || targetType->IsETSFunctionType() || targetType->DefinitelyETSNullish() ||
1751 targetType->IsETSTupleType() || targetType->IsETSAnyType()) {
1752 ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
1753 auto lang = GetAccumulatorType()->AsETSDynamicType()->Language();
1754 auto methodName = compiler::Signatures::Dynamic::GetObjectBuiltin(lang);
1755
1756 RegScope rs(this);
1757 VReg dynObjReg = AllocReg();
1758 StoreAccumulator(node, dynObjReg);
1759
1760 // try internal checkcast
1761 VReg typeReg = AllocReg();
1762 auto assemblerType = ToAssemblerType(targetType);
1763 Sa().Emit<LdaType>(node, assemblerType);
1764 StoreAccumulator(node, typeReg);
1765
1766 Ra().Emit<CallShort, 2U>(node, methodName, dynObjReg, typeReg);
1767 EmitCheckCast(node, assemblerType); // trick verifier
1768 SetAccumulatorType(targetType);
1769 return;
1770 }
1771
1772 ES2PANDA_UNREACHABLE();
1773 }
1774
CastToString(const ir::AstNode * const node)1775 void ETSGen::CastToString(const ir::AstNode *const node)
1776 {
1777 const auto *const sourceType = GetAccumulatorType();
1778 ES2PANDA_ASSERT(sourceType != nullptr);
1779 if (sourceType->IsETSStringType()) {
1780 return;
1781 }
1782
1783 ES2PANDA_ASSERT(sourceType->IsETSReferenceType());
1784
1785 // caller must ensure parameter is not null
1786 Ra().Emit<CallVirtAccShort, 0>(node, Signatures::BUILTIN_OBJECT_TO_STRING, dummyReg_, 0);
1787 SetAccumulatorType(Checker()->GetGlobalTypesHolder()->GlobalETSStringBuiltinType());
1788 }
1789
CastToDynamic(const ir::AstNode * node,const checker::ETSDynamicType * type)1790 void ETSGen::CastToDynamic(const ir::AstNode *node, const checker::ETSDynamicType *type)
1791 {
1792 std::string_view methodName {};
1793 auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1794 switch (typeKind) {
1795 case checker::TypeFlag::ETS_BOOLEAN:
1796 methodName = compiler::Signatures::Dynamic::NewBooleanBuiltin(type->Language());
1797 break;
1798 case checker::TypeFlag::CHAR:
1799 case checker::TypeFlag::BYTE:
1800 case checker::TypeFlag::SHORT:
1801 case checker::TypeFlag::INT:
1802 case checker::TypeFlag::LONG:
1803 case checker::TypeFlag::FLOAT:
1804 case checker::TypeFlag::DOUBLE:
1805 CastToDouble(node);
1806 methodName = compiler::Signatures::Dynamic::NewDoubleBuiltin(type->Language());
1807 break;
1808 case checker::TypeFlag::ETS_OBJECT:
1809 case checker::TypeFlag::ETS_TYPE_PARAMETER:
1810 case checker::TypeFlag::ETS_NONNULLISH:
1811 case checker::TypeFlag::ETS_PARTIAL_TYPE_PARAMETER:
1812 case checker::TypeFlag::ETS_UNION: // NOTE(vpukhov): refine dynamic type cast rules
1813 case checker::TypeFlag::ETS_ANY:
1814 ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
1815 if (GetAccumulatorType()->IsETSStringType()) {
1816 methodName = compiler::Signatures::Dynamic::NewStringBuiltin(type->Language());
1817 break;
1818 }
1819 [[fallthrough]];
1820 case checker::TypeFlag::FUNCTION:
1821 ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
1822 ES2PANDA_ASSERT(!GetAccumulatorType()->IsETSMethodType());
1823 [[fallthrough]];
1824 case checker::TypeFlag::ETS_ARRAY:
1825 case checker::TypeFlag::ETS_TUPLE:
1826 methodName = compiler::Signatures::Dynamic::NewObjectBuiltin(type->Language());
1827 break;
1828 case checker::TypeFlag::ETS_DYNAMIC_TYPE:
1829 SetAccumulatorType(type);
1830 return;
1831 default:
1832 ES2PANDA_UNREACHABLE();
1833 }
1834
1835 ES2PANDA_ASSERT(!methodName.empty());
1836
1837 RegScope rs(this);
1838 // Load value
1839 VReg valReg = AllocReg();
1840 StoreAccumulator(node, valReg);
1841
1842 // Create new JSValue and initialize it
1843 Ra().Emit<CallShort, 1>(node, methodName, valReg, dummyReg_);
1844 SetAccumulatorType(Checker()->GlobalBuiltinDynamicType(type->Language()));
1845 }
1846
CastDynamicTo(const ir::AstNode * node,enum checker::TypeFlag typeFlag)1847 void ETSGen::CastDynamicTo(const ir::AstNode *node, enum checker::TypeFlag typeFlag)
1848 {
1849 std::string_view methodName {};
1850 checker::Type *objectType {};
1851 ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
1852 auto type = GetAccumulatorType()->AsETSDynamicType();
1853 switch (typeFlag) {
1854 case checker::TypeFlag::ETS_BOOLEAN: {
1855 methodName = compiler::Signatures::Dynamic::GetBooleanBuiltin(type->Language());
1856 objectType = Checker()->GlobalETSBooleanType();
1857 break;
1858 }
1859 case checker::TypeFlag::DOUBLE: {
1860 methodName = compiler::Signatures::Dynamic::GetDoubleBuiltin(type->Language());
1861 objectType = Checker()->GlobalDoubleType();
1862 break;
1863 }
1864 case checker::TypeFlag::STRING: {
1865 methodName = compiler::Signatures::Dynamic::GetStringBuiltin(type->Language());
1866 objectType = Checker()->GlobalBuiltinETSStringType();
1867 break;
1868 }
1869 default: {
1870 ES2PANDA_UNREACHABLE();
1871 }
1872 }
1873
1874 RegScope rs(this);
1875 // Load dynamic object
1876 VReg dynObjReg = AllocReg();
1877 StoreAccumulator(node, dynObjReg);
1878
1879 // Get value from dynamic object
1880 Ra().Emit<CallShort, 1>(node, methodName, dynObjReg, dummyReg_);
1881 SetAccumulatorType(objectType);
1882 }
1883
ToBinaryResult(const ir::AstNode * node,Label * ifFalse)1884 void ETSGen::ToBinaryResult(const ir::AstNode *node, Label *ifFalse)
1885 {
1886 Label *end = AllocLabel();
1887 Sa().Emit<Ldai>(node, 1);
1888 Sa().Emit<Jmp>(node, end);
1889 SetLabel(node, ifFalse);
1890 Sa().Emit<Ldai>(node, 0);
1891 SetLabel(node, end);
1892 SetAccumulatorType(Checker()->GlobalETSBooleanType());
1893 }
1894
BinaryLogic(const ir::AstNode * node,lexer::TokenType op,VReg lhs)1895 void ETSGen::BinaryLogic(const ir::AstNode *node, lexer::TokenType op, VReg lhs)
1896 {
1897 switch (op) {
1898 case lexer::TokenType::PUNCTUATOR_MOD:
1899 case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: {
1900 SwapBinaryOpArgs(node, lhs);
1901 BinaryArithmetic<Mod2, Mod2Wide, Fmod2, Fmod2Wide>(node, lhs);
1902 break;
1903 }
1904 case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT:
1905 case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: {
1906 SwapBinaryOpArgs(node, lhs);
1907 BinaryBitwiseArithmetic<Shl2, Shl2Wide>(node, lhs);
1908 break;
1909 }
1910 case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT:
1911 case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: {
1912 SwapBinaryOpArgs(node, lhs);
1913 BinaryBitwiseArithmetic<Ashr2, Ashr2Wide>(node, lhs);
1914 break;
1915 }
1916 case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT:
1917 case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: {
1918 SwapBinaryOpArgs(node, lhs);
1919 BinaryBitwiseArithmetic<Shr2, Shr2Wide>(node, lhs);
1920 break;
1921 }
1922 case lexer::TokenType::PUNCTUATOR_BITWISE_AND:
1923 case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: {
1924 BinaryBitwiseArithmetic<And2, And2Wide>(node, lhs);
1925 break;
1926 }
1927 case lexer::TokenType::PUNCTUATOR_BITWISE_OR:
1928 case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: {
1929 BinaryBitwiseArithmetic<Or2, Or2Wide>(node, lhs);
1930 break;
1931 }
1932 case lexer::TokenType::PUNCTUATOR_BITWISE_XOR:
1933 case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: {
1934 BinaryBitwiseArithmetic<Xor2, Xor2Wide>(node, lhs);
1935 break;
1936 }
1937 default: {
1938 ES2PANDA_UNREACHABLE();
1939 }
1940 }
1941
1942 ES2PANDA_ASSERT(node->IsAssignmentExpression() || node->IsBinaryExpression());
1943 ES2PANDA_ASSERT(Checker()->Relation()->IsIdenticalTo(GetAccumulatorType(), node->AsExpression()->TsType()));
1944 }
1945
BinaryArithmLogic(const ir::AstNode * node,lexer::TokenType op,VReg lhs)1946 void ETSGen::BinaryArithmLogic(const ir::AstNode *node, lexer::TokenType op, VReg lhs)
1947 {
1948 switch (op) {
1949 case lexer::TokenType::PUNCTUATOR_PLUS:
1950 case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: {
1951 SwapBinaryOpArgs(node, lhs);
1952 BinaryArithmetic<Add2, Add2Wide, Fadd2, Fadd2Wide>(node, lhs);
1953 break;
1954 }
1955 case lexer::TokenType::PUNCTUATOR_MINUS:
1956 case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: {
1957 SwapBinaryOpArgs(node, lhs);
1958 BinaryArithmetic<Sub2, Sub2Wide, Fsub2, Fsub2Wide>(node, lhs);
1959 break;
1960 }
1961 case lexer::TokenType::PUNCTUATOR_MULTIPLY:
1962 case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: {
1963 SwapBinaryOpArgs(node, lhs);
1964 BinaryArithmetic<Mul2, Mul2Wide, Fmul2, Fmul2Wide>(node, lhs);
1965 break;
1966 }
1967 case lexer::TokenType::PUNCTUATOR_DIVIDE:
1968 case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: {
1969 SwapBinaryOpArgs(node, lhs);
1970 BinaryArithmetic<Div2, Div2Wide, Fdiv2, Fdiv2Wide>(node, lhs);
1971 break;
1972 }
1973 default: {
1974 BinaryLogic(node, op, lhs);
1975 break;
1976 }
1977 }
1978
1979 ES2PANDA_ASSERT(node->IsAssignmentExpression() || node->IsBinaryExpression());
1980 ES2PANDA_ASSERT(Checker()->Relation()->IsIdenticalTo(GetAccumulatorType(), node->AsExpression()->TsType()));
1981 }
1982
Binary(const ir::AstNode * node,lexer::TokenType op,VReg lhs)1983 void ETSGen::Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs)
1984 {
1985 Label *ifFalse = AllocLabel();
1986 switch (op) {
1987 case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: {
1988 BinaryEquality<Jne, Jnez, Jeqz, true>(node, lhs, ifFalse);
1989 break;
1990 }
1991 case lexer::TokenType::PUNCTUATOR_EQUAL: {
1992 BinaryEquality<Jne, Jnez, Jeqz>(node, lhs, ifFalse);
1993 break;
1994 }
1995 case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: {
1996 BinaryEquality<Jeq, Jeqz, Jnez, true>(node, lhs, ifFalse);
1997 break;
1998 }
1999 case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
2000 BinaryEquality<Jeq, Jeqz, Jnez>(node, lhs, ifFalse);
2001 break;
2002 }
2003 case lexer::TokenType::PUNCTUATOR_LESS_THAN: {
2004 BinaryRelation<Jle, Jlez>(node, lhs, ifFalse);
2005 break;
2006 }
2007 case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: {
2008 BinaryRelation<Jlt, Jltz>(node, lhs, ifFalse);
2009 break;
2010 }
2011 case lexer::TokenType::PUNCTUATOR_GREATER_THAN: {
2012 BinaryRelation<Jge, Jgez>(node, lhs, ifFalse);
2013 break;
2014 }
2015 case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
2016 BinaryRelation<Jgt, Jgtz>(node, lhs, ifFalse);
2017 break;
2018 }
2019 default: {
2020 BinaryArithmLogic(node, op, lhs);
2021 break;
2022 }
2023 }
2024
2025 ES2PANDA_ASSERT(node->IsAssignmentExpression() || node->IsBinaryExpression());
2026 ES2PANDA_ASSERT(Checker()->Relation()->IsIdenticalTo(GetAccumulatorType(), node->AsExpression()->TsType()));
2027 }
2028
Condition(const ir::AstNode * node,lexer::TokenType op,VReg lhs,Label * ifFalse)2029 void ETSGen::Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *ifFalse)
2030 {
2031 switch (op) {
2032 case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: {
2033 BinaryEqualityCondition<Jne, Jnez, true>(node, lhs, ifFalse);
2034 break;
2035 }
2036 case lexer::TokenType::PUNCTUATOR_EQUAL: {
2037 BinaryEqualityCondition<Jne, Jnez>(node, lhs, ifFalse);
2038 break;
2039 }
2040 case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: {
2041 BinaryEqualityCondition<Jeq, Jeqz, true>(node, lhs, ifFalse);
2042 break;
2043 }
2044 case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
2045 BinaryEqualityCondition<Jeq, Jeqz>(node, lhs, ifFalse);
2046 break;
2047 }
2048 case lexer::TokenType::PUNCTUATOR_LESS_THAN: {
2049 BinaryRelationCondition<Jle, Jlez>(node, lhs, ifFalse);
2050 break;
2051 }
2052 case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: {
2053 BinaryRelationCondition<Jlt, Jltz>(node, lhs, ifFalse);
2054 break;
2055 }
2056 case lexer::TokenType::PUNCTUATOR_GREATER_THAN: {
2057 BinaryRelationCondition<Jge, Jgez>(node, lhs, ifFalse);
2058 break;
2059 }
2060 case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
2061 BinaryRelationCondition<Jgt, Jgtz>(node, lhs, ifFalse);
2062 break;
2063 }
2064 default: {
2065 ES2PANDA_UNREACHABLE();
2066 }
2067 }
2068 }
2069
2070 template <typename CondCompare, bool BEFORE_LOGICAL_NOT>
ResolveConditionalResultFloat(const ir::AstNode * node,Label * realEndLabel)2071 void ETSGen::ResolveConditionalResultFloat(const ir::AstNode *node, Label *realEndLabel)
2072 {
2073 auto type = GetAccumulatorType();
2074 ES2PANDA_ASSERT(type != nullptr);
2075 VReg tmpReg = AllocReg();
2076 StoreAccumulator(node, tmpReg);
2077 if (type->IsFloatType()) {
2078 FloatIsNaN(node);
2079 } else {
2080 DoubleIsNaN(node);
2081 }
2082 Sa().Emit<Xori>(node, 1);
2083
2084 BranchIfFalse(node, realEndLabel);
2085 LoadAccumulator(node, tmpReg);
2086 VReg zeroReg = AllocReg();
2087
2088 if (type->IsFloatType()) {
2089 MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::FLOAT, 0);
2090 BinaryNumberComparison<Fcmpl, Jeqz>(node, zeroReg, realEndLabel);
2091 } else {
2092 MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::DOUBLE, 0);
2093 BinaryNumberComparison<FcmplWide, Jeqz>(node, zeroReg, realEndLabel);
2094 }
2095 }
2096
2097 template <typename CondCompare, bool BEFORE_LOGICAL_NOT, bool USE_FALSE_LABEL>
ResolveConditionalResultNumeric(const ir::AstNode * node,Label * ifFalse,Label ** end)2098 void ETSGen::ResolveConditionalResultNumeric(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse, Label **end)
2099 {
2100 auto type = GetAccumulatorType();
2101 ES2PANDA_ASSERT(type != nullptr);
2102 auto realEndLabel = [end, ifFalse, this](bool useFalseLabel) {
2103 if (useFalseLabel) {
2104 return ifFalse;
2105 }
2106 if ((*end) == nullptr) {
2107 (*end) = AllocLabel();
2108 }
2109 return (*end);
2110 }(USE_FALSE_LABEL);
2111 if (type->IsDoubleType() || type->IsFloatType()) {
2112 ResolveConditionalResultFloat<CondCompare, BEFORE_LOGICAL_NOT>(node, realEndLabel);
2113 }
2114 if (type->IsLongType()) {
2115 VReg zeroReg = AllocReg();
2116 MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::LONG, 0);
2117 BinaryNumberComparison<CmpWide, CondCompare>(node, zeroReg, realEndLabel);
2118 }
2119 if constexpr (BEFORE_LOGICAL_NOT) {
2120 Label *zeroPrimitive = AllocLabel();
2121 BranchIfFalse(node, zeroPrimitive);
2122 ToBinaryResult(node, zeroPrimitive);
2123 }
2124 }
2125
2126 template <typename CondCompare, bool BEFORE_LOGICAL_NOT>
ResolveConditionalResultReference(const ir::AstNode * node)2127 void ETSGen::ResolveConditionalResultReference(const ir::AstNode *node)
2128 {
2129 auto const testString = [this, node]() {
2130 LoadStringLength(node);
2131 if constexpr (BEFORE_LOGICAL_NOT) {
2132 Label *zeroLenth = AllocLabel();
2133 BranchIfFalse(node, zeroLenth);
2134 ToBinaryResult(node, zeroLenth);
2135 }
2136 };
2137
2138 auto type = GetAccumulatorType();
2139 ES2PANDA_ASSERT(type != nullptr);
2140 if (!type->PossiblyETSString()) {
2141 Sa().Emit<Ldai>(node, 1);
2142 return;
2143 }
2144 if (type->IsETSStringType()) { // should also be valid for string|null|undefined
2145 testString();
2146 return;
2147 }
2148
2149 Label *isString = AllocLabel();
2150 Label *end = AllocLabel();
2151 compiler::VReg objReg = AllocReg();
2152 StoreAccumulator(node, objReg);
2153
2154 ES2PANDA_ASSERT(Checker()->GlobalBuiltinETSStringType() != nullptr);
2155 EmitIsInstance(node, Checker()->GlobalBuiltinETSStringType()->AssemblerName());
2156 BranchIfTrue(node, isString);
2157 Sa().Emit<Ldai>(node, 1);
2158 Branch(node, end);
2159 SetLabel(node, isString);
2160 LoadAccumulator(node, objReg);
2161 InternalCheckCast(node, Checker()->GlobalBuiltinETSStringType()); // help verifier
2162 testString();
2163 SetLabel(node, end);
2164 }
2165
2166 template <typename CondCompare, bool BEFORE_LOGICAL_NOT, bool USE_FALSE_LABEL>
ResolveConditionalResult(const ir::AstNode * node,Label * ifFalse)2167 void ETSGen::ResolveConditionalResult(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse)
2168 {
2169 auto type = GetAccumulatorType();
2170 ES2PANDA_ASSERT(type != nullptr);
2171 #ifdef PANDA_WITH_ETS
2172 if (type->IsETSReferenceType()) {
2173 VReg valReg = AllocReg();
2174 StoreAccumulator(node, valReg);
2175 EmitEtsIstrue(node, valReg);
2176 return;
2177 }
2178 #endif // PANDA_WITH_ETS
2179 if (type->IsETSBooleanType()) {
2180 return;
2181 }
2182 Label *ifNullish {nullptr};
2183 Label *end {nullptr};
2184 if (type->PossiblyETSNullish()) {
2185 if constexpr (USE_FALSE_LABEL) {
2186 BranchIfNullish(node, ifFalse);
2187 } else {
2188 ifNullish = AllocLabel();
2189 end = AllocLabel();
2190 BranchIfNullish(node, ifNullish);
2191 }
2192 }
2193 if (type->DefinitelyETSNullish()) {
2194 // skip
2195 } else if (type->IsETSReferenceType()) {
2196 ResolveConditionalResultReference<CondCompare, BEFORE_LOGICAL_NOT>(node);
2197 } else {
2198 ResolveConditionalResultNumeric<CondCompare, BEFORE_LOGICAL_NOT, USE_FALSE_LABEL>(node, ifFalse, &end);
2199 }
2200 if (ifNullish != nullptr) {
2201 Branch(node, end);
2202 SetLabel(node, ifNullish);
2203 Sa().Emit<Ldai>(node, 0);
2204 }
2205 if (end != nullptr) {
2206 SetLabel(node, end);
2207 }
2208 }
2209
2210 template <bool BEFORE_LOGICAL_NOT, bool FALSE_LABEL_EXISTED>
ResolveConditionalResultIfFalse(const ir::AstNode * node,Label * ifFalse)2211 void ETSGen::ResolveConditionalResultIfFalse(const ir::AstNode *node, Label *ifFalse)
2212 {
2213 ResolveConditionalResult<Jeqz, BEFORE_LOGICAL_NOT, FALSE_LABEL_EXISTED>(node, ifFalse);
2214 }
2215
2216 template void ETSGen::ResolveConditionalResultIfFalse<false, true>(const ir::AstNode *node, Label *ifFalse);
2217 template void ETSGen::ResolveConditionalResultIfFalse<true, false>(const ir::AstNode *node, Label *ifFalse);
2218 template void ETSGen::ResolveConditionalResultIfFalse<false, false>(const ir::AstNode *node, Label *ifFalse);
2219
2220 template <bool BEFORE_LOGICAL_NOT, bool FALSE_LABEL_EXISTED>
ResolveConditionalResultIfTrue(const ir::AstNode * node,Label * ifFalse)2221 void ETSGen::ResolveConditionalResultIfTrue(const ir::AstNode *node, Label *ifFalse)
2222 {
2223 ResolveConditionalResult<Jnez, BEFORE_LOGICAL_NOT, FALSE_LABEL_EXISTED>(node, ifFalse);
2224 }
2225
2226 template void ETSGen::ResolveConditionalResultIfTrue<false, true>(const ir::AstNode *node, Label *ifFalse);
2227 template void ETSGen::ResolveConditionalResultIfTrue<false, false>(const ir::AstNode *node, Label *ifFalse);
2228
2229 template <typename CondCompare, typename NegCondCompare>
BranchConditional(const ir::AstNode * node,Label * endLabel)2230 void ETSGen::BranchConditional(const ir::AstNode *node, Label *endLabel)
2231 {
2232 auto type = GetAccumulatorType();
2233 ES2PANDA_ASSERT(type != nullptr);
2234 if (type->IsETSReferenceType()) {
2235 VReg valReg = AllocReg();
2236 StoreAccumulator(node, valReg);
2237 EmitEtsIstrue(node, valReg);
2238 } else if (type->IsDoubleType() || type->IsFloatType()) {
2239 ConditionalFloat(node);
2240 } else if (type->IsLongType()) {
2241 VReg zeroReg = AllocReg();
2242 MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::LONG, 0);
2243 Ra().Emit<CmpWide>(node, zeroReg);
2244 }
2245
2246 Sa().Emit<CondCompare>(node, endLabel);
2247 }
2248
ConditionalFloat(const ir::AstNode * node)2249 void ETSGen::ConditionalFloat(const ir::AstNode *node)
2250 {
2251 auto type = GetAccumulatorType();
2252 ES2PANDA_ASSERT(type != nullptr);
2253 VReg tmpReg = AllocReg();
2254 VReg isNaNReg = AllocReg();
2255
2256 StoreAccumulator(node, tmpReg);
2257 if (type->IsFloatType()) {
2258 FloatIsNaN(node);
2259 } else {
2260 DoubleIsNaN(node);
2261 }
2262 Sa().Emit<Xori>(node, 1);
2263 StoreAccumulator(node, isNaNReg);
2264 LoadAccumulator(node, tmpReg);
2265
2266 VReg zeroReg = AllocReg();
2267
2268 if (type->IsFloatType()) {
2269 MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::FLOAT, 0);
2270 Ra().Emit<Fcmpl>(node, zeroReg);
2271 } else {
2272 MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::DOUBLE, 0);
2273 Ra().Emit<FcmplWide>(node, zeroReg);
2274 }
2275 Sa().Emit<Xori>(node, 0);
2276 Sa().Emit<And2>(node, isNaNReg);
2277 }
2278
BranchConditionalIfFalse(const ir::AstNode * node,Label * endLabel)2279 void ETSGen::BranchConditionalIfFalse(const ir::AstNode *node, Label *endLabel)
2280 {
2281 BranchConditional<Jeqz, Jnez>(node, endLabel);
2282 }
2283
BranchConditionalIfTrue(const ir::AstNode * node,Label * endLabel)2284 void ETSGen::BranchConditionalIfTrue(const ir::AstNode *node, Label *endLabel)
2285 {
2286 BranchConditional<Jnez, Jeqz>(node, endLabel);
2287 }
2288
BranchIfNullish(const ir::AstNode * node,Label * ifNullish)2289 void ETSGen::BranchIfNullish(const ir::AstNode *node, Label *ifNullish)
2290 {
2291 auto *const type = GetAccumulatorType();
2292 ES2PANDA_ASSERT(type != nullptr);
2293
2294 if (type->IsETSVoidType()) {
2295 // NOTE(): #19701 need void refactoring
2296 Sa().Emit<Jmp>(node, ifNullish);
2297 } else if (type->DefinitelyNotETSNullish()) {
2298 // no action
2299 } else if (type->DefinitelyETSNullish()) {
2300 Sa().Emit<Jmp>(node, ifNullish);
2301 } else if (!type->PossiblyETSNull()) {
2302 Sa().Emit<JeqzObj>(node, ifNullish);
2303 } else {
2304 RegScope rs(this);
2305 auto tmpObj = AllocReg();
2306 auto notTaken = AllocLabel();
2307
2308 if (type->PossiblyETSUndefined()) {
2309 Sa().Emit<JeqzObj>(node, ifNullish);
2310 }
2311
2312 Sa().Emit<StaObj>(node, tmpObj);
2313 EmitIsNull(node);
2314 Sa().Emit<Jeqz>(node, notTaken);
2315
2316 Sa().Emit<LdaObj>(node, tmpObj);
2317 Sa().Emit<Jmp>(node, ifNullish);
2318
2319 SetLabel(node, notTaken);
2320 Sa().Emit<LdaObj>(node, tmpObj);
2321 }
2322 }
2323
BranchIfNotNullish(const ir::AstNode * node,Label * ifNotNullish)2324 void ETSGen::BranchIfNotNullish(const ir::AstNode *node, Label *ifNotNullish)
2325 {
2326 auto notTaken = AllocLabel();
2327 BranchIfNullish(node, notTaken);
2328 JumpTo(node, ifNotNullish);
2329 SetLabel(node, notTaken);
2330 }
2331
AssumeNonNullish(const ir::AstNode * node,checker::Type const * targetType)2332 void ETSGen::AssumeNonNullish(const ir::AstNode *node, checker::Type const *targetType)
2333 {
2334 auto const *nullishType = GetAccumulatorType();
2335 ES2PANDA_ASSERT(nullishType != nullptr);
2336 if (nullishType->PossiblyETSNull()) {
2337 // clear 'null' dataflow
2338 EmitCheckCast(node, ToAssemblerType(targetType));
2339 }
2340 SetAccumulatorType(targetType);
2341 }
2342
EmitNullishException(const ir::AstNode * node)2343 void ETSGen::EmitNullishException(const ir::AstNode *node)
2344 {
2345 RegScope ra(this);
2346 VReg undef = AllocReg();
2347 LoadAccumulatorUndefined(node);
2348 StoreAccumulator(node, undef);
2349 VReg exception = AllocReg();
2350 NewObject(node, Signatures::BUILTIN_NULLPOINTER_ERROR, exception);
2351 CallExact(node, Signatures::BUILTIN_NULLPOINTER_ERROR_CTOR, exception, undef, undef);
2352 EmitThrow(node, exception);
2353 SetAccumulatorType(nullptr);
2354 }
2355
2356 template <bool IS_STRICT>
RefEqualityLooseDynamic(const ir::AstNode * node,VReg lhs,VReg rhs,Label * ifFalse)2357 void ETSGen::RefEqualityLooseDynamic(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse)
2358 {
2359 // NOTE(vpukhov): implement
2360 EmitEtsEquals<IS_STRICT>(node, lhs, rhs);
2361 BranchIfFalse(node, ifFalse);
2362 }
2363
2364 template <typename IntCompare, typename CondCompare, typename DynCompare, bool IS_STRICT>
BinaryEquality(const ir::AstNode * node,VReg lhs,Label * ifFalse)2365 void ETSGen::BinaryEquality(const ir::AstNode *node, VReg lhs, Label *ifFalse)
2366 {
2367 BinaryEqualityCondition<IntCompare, CondCompare, IS_STRICT>(node, lhs, ifFalse);
2368 ToBinaryResult(node, ifFalse);
2369 SetAccumulatorType(Checker()->GlobalETSBooleanType());
2370 }
2371
2372 template <typename IntCompare, typename CondCompare, bool IS_STRICT>
BinaryEqualityCondition(const ir::AstNode * node,VReg lhs,Label * ifFalse)2373 void ETSGen::BinaryEqualityCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
2374 {
2375 if (targetType_->IsETSReferenceType()) {
2376 RegScope rs(this);
2377 VReg arg0 = AllocReg();
2378 StoreAccumulator(node, arg0);
2379 InverseCondition(
2380 node, [this, node, lhs, arg0](Label *tgt) { RefEqualityLoose<IS_STRICT>(node, lhs, arg0, tgt); }, ifFalse,
2381 std::is_same_v<CondCompare, Jeqz>);
2382 SetAccumulatorType(Checker()->GlobalETSBooleanType());
2383 return;
2384 }
2385
2386 auto typeKind = checker::ETSChecker::TypeKind(targetType_);
2387
2388 switch (typeKind) {
2389 case checker::TypeFlag::DOUBLE: {
2390 BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
2391 break;
2392 }
2393 case checker::TypeFlag::FLOAT: {
2394 BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
2395 break;
2396 }
2397 case checker::TypeFlag::LONG: {
2398 BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
2399 break;
2400 }
2401 case checker::TypeFlag::ETS_BOOLEAN:
2402 case checker::TypeFlag::BYTE:
2403 case checker::TypeFlag::CHAR:
2404 case checker::TypeFlag::SHORT:
2405 case checker::TypeFlag::INT: {
2406 Ra().Emit<IntCompare>(node, lhs, ifFalse);
2407 break;
2408 }
2409 default: {
2410 ES2PANDA_UNREACHABLE();
2411 }
2412 }
2413
2414 SetAccumulatorType(Checker()->GlobalETSBooleanType());
2415 }
2416
2417 template <typename IntCompare, typename CondCompare>
BinaryRelation(const ir::AstNode * node,VReg lhs,Label * ifFalse)2418 void ETSGen::BinaryRelation(const ir::AstNode *node, VReg lhs, Label *ifFalse)
2419 {
2420 BinaryRelationCondition<IntCompare, CondCompare>(node, lhs, ifFalse);
2421 ToBinaryResult(node, ifFalse);
2422 }
2423
2424 template <typename IntCompare, typename CondCompare>
BinaryRelationCondition(const ir::AstNode * node,VReg lhs,Label * ifFalse)2425 void ETSGen::BinaryRelationCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
2426 {
2427 auto typeKind = checker::ETSChecker::TypeKind(targetType_);
2428
2429 switch (typeKind) {
2430 case checker::TypeFlag::DOUBLE: {
2431 BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
2432 break;
2433 }
2434 case checker::TypeFlag::FLOAT: {
2435 BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
2436 break;
2437 }
2438 case checker::TypeFlag::LONG: {
2439 BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
2440 break;
2441 }
2442 case checker::TypeFlag::ETS_BOOLEAN:
2443 case checker::TypeFlag::BYTE:
2444 case checker::TypeFlag::SHORT:
2445 case checker::TypeFlag::CHAR:
2446 case checker::TypeFlag::INT: {
2447 Ra().Emit<IntCompare>(node, lhs, ifFalse);
2448 break;
2449 }
2450 default: {
2451 ES2PANDA_UNREACHABLE();
2452 }
2453 }
2454
2455 SetAccumulatorType(Checker()->GlobalETSBooleanType());
2456 }
2457
2458 template <typename IntOp, typename LongOp, typename FloatOp, typename DoubleOp>
BinaryArithmetic(const ir::AstNode * node,VReg lhs)2459 void ETSGen::BinaryArithmetic(const ir::AstNode *node, VReg lhs)
2460 {
2461 auto typeKind = checker::ETSChecker::TypeKind(targetType_);
2462
2463 switch (typeKind) {
2464 case checker::TypeFlag::DOUBLE: {
2465 Ra().Emit<DoubleOp>(node, lhs);
2466 SetAccumulatorType(Checker()->GlobalDoubleType());
2467 break;
2468 }
2469 case checker::TypeFlag::FLOAT: {
2470 Ra().Emit<FloatOp>(node, lhs);
2471 SetAccumulatorType(Checker()->GlobalFloatType());
2472 break;
2473 }
2474 default: {
2475 BinaryBitwiseArithmetic<IntOp, LongOp>(node, lhs);
2476 break;
2477 }
2478 }
2479
2480 ApplyCast(node, node->AsExpression()->TsType());
2481 }
2482
2483 template <typename IntOp, typename LongOp>
BinaryBitwiseArithmetic(const ir::AstNode * node,VReg lhs)2484 void ETSGen::BinaryBitwiseArithmetic(const ir::AstNode *node, VReg lhs)
2485 {
2486 auto typeKind = checker::ETSChecker::TypeKind(targetType_);
2487
2488 switch (typeKind) {
2489 case checker::TypeFlag::LONG: {
2490 Ra().Emit<LongOp>(node, lhs);
2491 SetAccumulatorType(Checker()->GlobalLongType());
2492 break;
2493 }
2494 case checker::TypeFlag::BYTE:
2495 case checker::TypeFlag::SHORT:
2496 case checker::TypeFlag::CHAR:
2497 case checker::TypeFlag::INT: {
2498 Ra().Emit<IntOp>(node, lhs);
2499 SetAccumulatorType(Checker()->GlobalIntType());
2500 break;
2501 }
2502 case checker::TypeFlag::ETS_BOOLEAN: {
2503 Ra().Emit<IntOp>(node, lhs);
2504 SetAccumulatorType(Checker()->GlobalETSBooleanType());
2505 break;
2506 }
2507 default: {
2508 ES2PANDA_UNREACHABLE();
2509 }
2510 }
2511
2512 ApplyCast(node, node->AsExpression()->TsType());
2513 }
2514
2515 template <bool IS_STRICT>
HandleDefinitelyNullishEquality(const ir::AstNode * node,VReg lhs,VReg rhs,Label * ifFalse)2516 void ETSGen::HandleDefinitelyNullishEquality(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse)
2517 {
2518 if constexpr (IS_STRICT) {
2519 LoadAccumulator(node, lhs);
2520 Ra().Emit<JneObj>(node, rhs, ifFalse);
2521 } else {
2522 auto *checker = const_cast<checker::ETSChecker *>(Checker());
2523 auto ltype = checker->GetNonConstantType(const_cast<checker::Type *>(GetVRegType(lhs)));
2524 ES2PANDA_ASSERT(ltype != nullptr);
2525 LoadAccumulator(node, ltype->DefinitelyETSNullish() ? rhs : lhs);
2526 BranchIfNotNullish(node, ifFalse);
2527 }
2528 }
2529
2530 template <bool IS_STRICT>
HandlePossiblyNullishEquality(const ir::AstNode * node,VReg lhs,VReg rhs,Label * ifFalse,Label * ifTrue)2531 void ETSGen::HandlePossiblyNullishEquality(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse, Label *ifTrue)
2532 {
2533 Label *ifLhsNullish = AllocLabel();
2534 Label *out = AllocLabel();
2535
2536 LoadAccumulator(node, lhs);
2537 BranchIfNullish(node, ifLhsNullish);
2538
2539 LoadAccumulator(node, rhs);
2540 BranchIfNullish(node, ifFalse);
2541 JumpTo(node, out);
2542
2543 SetLabel(node, ifLhsNullish);
2544 if constexpr (IS_STRICT) {
2545 Ra().Emit<JneObj>(node, rhs, ifFalse);
2546 } else {
2547 LoadAccumulator(node, rhs);
2548 BranchIfNotNullish(node, ifFalse);
2549 }
2550 JumpTo(node, ifTrue);
2551
2552 SetLabel(node, out);
2553 SetAccumulatorType(nullptr);
2554 }
2555
SelectLooseObjComparator(checker::ETSChecker * checker,checker::Type * lhs,checker::Type * rhs)2556 static std::optional<std::pair<checker::Type const *, util::StringView>> SelectLooseObjComparator(
2557 checker::ETSChecker *checker, checker::Type *lhs, checker::Type *rhs)
2558 {
2559 auto alhs = checker->GetApparentType(checker->GetNonNullishType(lhs));
2560 auto arhs = checker->GetApparentType(checker->GetNonNullishType(rhs));
2561 ES2PANDA_ASSERT(alhs != nullptr && arhs != nullptr);
2562 alhs = alhs->IsETSStringType() ? checker->GlobalBuiltinETSStringType() : alhs;
2563 arhs = arhs->IsETSStringType() ? checker->GlobalBuiltinETSStringType() : arhs;
2564 if (!alhs->IsETSObjectType() || !arhs->IsETSObjectType()) {
2565 return std::nullopt;
2566 }
2567 if (!checker->Relation()->IsIdenticalTo(alhs, arhs)) {
2568 return std::nullopt;
2569 }
2570 auto obj = alhs->AsETSObjectType();
2571 if (!obj->HasObjectFlag(checker::ETSObjectFlags::VALUE_TYPED)) {
2572 return std::nullopt;
2573 }
2574 // NOTE(vpukhov): emit faster code
2575 auto methodSig =
2576 util::UString(std::string(obj->AssemblerName()) + ".equals:std.core.Object;u1;", checker->Allocator()).View();
2577 return std::make_pair(checker->GetNonConstantType(obj), methodSig);
2578 }
2579
2580 template <typename LongOp, typename IntOp, typename DoubleOp, typename FloatOp>
UpdateOperator(const ir::AstNode * node)2581 void ETSGen::UpdateOperator(const ir::AstNode *node)
2582 {
2583 switch (checker::ETSChecker::ETSType(GetAccumulatorType())) {
2584 case checker::TypeFlag::LONG: {
2585 RegScope scope(this);
2586 VReg reg = AllocReg();
2587 Ra().Emit<MoviWide>(node, reg, 1LL);
2588 Ra().Emit<LongOp>(node, reg);
2589 break;
2590 }
2591 case checker::TypeFlag::INT: {
2592 Sa().Emit<IntOp>(node, 1);
2593 break;
2594 }
2595 case checker::TypeFlag::CHAR: {
2596 Sa().Emit<IntOp>(node, 1);
2597 Sa().Emit<I32tou16>(node);
2598 break;
2599 }
2600 case checker::TypeFlag::SHORT: {
2601 Sa().Emit<IntOp>(node, 1);
2602 Sa().Emit<I32toi16>(node);
2603 break;
2604 }
2605 case checker::TypeFlag::BYTE: {
2606 Sa().Emit<IntOp>(node, 1);
2607 Sa().Emit<I32toi8>(node);
2608 break;
2609 }
2610 case checker::TypeFlag::DOUBLE: {
2611 RegScope scope(this);
2612 VReg reg = AllocReg();
2613 Ra().Emit<FmoviWide>(node, reg, 1.0);
2614 Ra().Emit<DoubleOp>(node, reg);
2615 break;
2616 }
2617 case checker::TypeFlag::FLOAT: {
2618 RegScope scope(this);
2619 VReg reg = AllocReg();
2620 Ra().Emit<Fmovi>(node, reg, 1.0F);
2621 Ra().Emit<FloatOp>(node, reg);
2622 break;
2623 }
2624 default: {
2625 ES2PANDA_UNREACHABLE();
2626 }
2627 }
2628 }
2629
2630 template <bool IS_STRICT>
RefEqualityLoose(const ir::AstNode * node,VReg lhs,VReg rhs,Label * ifFalse)2631 void ETSGen::RefEqualityLoose(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse)
2632 {
2633 auto *checker = const_cast<checker::ETSChecker *>(Checker());
2634 auto ltype = checker->GetNonConstantType(const_cast<checker::Type *>(GetVRegType(lhs)));
2635 auto rtype = checker->GetNonConstantType(const_cast<checker::Type *>(GetVRegType(rhs)));
2636 ES2PANDA_ASSERT(ltype != nullptr && rtype != nullptr);
2637 if (ltype->IsETSDynamicType() || rtype->IsETSDynamicType()) {
2638 RefEqualityLooseDynamic<IS_STRICT>(node, lhs, rhs, ifFalse);
2639 return;
2640 }
2641
2642 if (ltype->DefinitelyETSNullish() || rtype->DefinitelyETSNullish()) {
2643 HandleDefinitelyNullishEquality<IS_STRICT>(node, lhs, rhs, ifFalse);
2644 } else if (!ltype->PossiblyETSValueTypedExceptNullish() || !rtype->PossiblyETSValueTypedExceptNullish()) {
2645 auto ifTrue = AllocLabel();
2646 if ((ltype->PossiblyETSUndefined() && rtype->PossiblyETSNull()) ||
2647 (rtype->PossiblyETSUndefined() && ltype->PossiblyETSNull())) {
2648 HandlePossiblyNullishEquality(node, lhs, rhs, ifFalse, ifTrue);
2649 }
2650 LoadAccumulator(node, lhs);
2651 Ra().Emit<JneObj>(node, rhs, ifFalse);
2652 SetLabel(node, ifTrue);
2653 } else if (auto spec = SelectLooseObjComparator( // try to select specific type
2654 // CC-OFFNXT(G.FMT.06-CPP) project code style
2655 const_cast<checker::ETSChecker *>(Checker()), const_cast<checker::Type *>(ltype),
2656 const_cast<checker::Type *>(rtype)); // CC-OFF(G.FMT.02) project code style
2657 spec.has_value()) { // CC-OFF(G.FMT.02-CPP) project code style
2658 auto ifTrue = AllocLabel();
2659 if (ltype->PossiblyETSNullish() || rtype->PossiblyETSNullish()) {
2660 HandlePossiblyNullishEquality<IS_STRICT>(node, lhs, rhs, ifFalse, ifTrue);
2661 }
2662 LoadAccumulator(node, rhs);
2663 AssumeNonNullish(node, spec->first);
2664 StoreAccumulator(node, rhs);
2665 LoadAccumulator(node, lhs);
2666 AssumeNonNullish(node, spec->first);
2667 CallExact(node, spec->second, lhs, rhs);
2668 BranchIfFalse(node, ifFalse);
2669 SetLabel(node, ifTrue);
2670 } else {
2671 EmitEtsEquals<IS_STRICT>(node, lhs, rhs);
2672 BranchIfFalse(node, ifFalse);
2673 }
2674 SetAccumulatorType(nullptr);
2675 }
2676
CompileStatements(const ArenaVector<ir::Statement * > & statements)2677 void ETSGen::CompileStatements(const ArenaVector<ir::Statement *> &statements)
2678 {
2679 for (const auto *stmt : statements) {
2680 stmt->Compile(this);
2681 }
2682 }
2683
Negate(const ir::AstNode * node)2684 void ETSGen::Negate(const ir::AstNode *node)
2685 {
2686 auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
2687
2688 switch (typeKind) {
2689 case checker::TypeFlag::BYTE:
2690 case checker::TypeFlag::SHORT:
2691 case checker::TypeFlag::CHAR:
2692 case checker::TypeFlag::INT: {
2693 Sa().Emit<Neg>(node);
2694 return;
2695 }
2696 case checker::TypeFlag::LONG: {
2697 Sa().Emit<NegWide>(node);
2698 break;
2699 }
2700 case checker::TypeFlag::FLOAT: {
2701 Sa().Emit<Fneg>(node);
2702 break;
2703 }
2704 case checker::TypeFlag::DOUBLE: {
2705 Sa().Emit<FnegWide>(node);
2706 break;
2707 }
2708 default: {
2709 ES2PANDA_UNREACHABLE();
2710 }
2711 }
2712 }
2713
LogicalNot(const ir::AstNode * node)2714 void ETSGen::LogicalNot(const ir::AstNode *node)
2715 {
2716 ResolveConditionalResultIfFalse<true, false>(node);
2717 Sa().Emit<Xori>(node, 1);
2718 SetAccumulatorType(Checker()->GlobalETSBooleanType());
2719 }
2720
LoadAccumulatorByte(const ir::AstNode * node,int8_t number)2721 void ETSGen::LoadAccumulatorByte(const ir::AstNode *node, int8_t number)
2722 {
2723 LoadAccumulatorNumber<int8_t>(node, number, checker::TypeFlag::BYTE);
2724 }
2725
LoadAccumulatorShort(const ir::AstNode * node,int16_t number)2726 void ETSGen::LoadAccumulatorShort(const ir::AstNode *node, int16_t number)
2727 {
2728 LoadAccumulatorNumber<int16_t>(node, number, checker::TypeFlag::SHORT);
2729 }
2730
LoadAccumulatorInt(const ir::AstNode * node,int32_t number)2731 void ETSGen::LoadAccumulatorInt(const ir::AstNode *node, int32_t number)
2732 {
2733 LoadAccumulatorNumber<int32_t>(node, number, checker::TypeFlag::INT);
2734 }
2735
LoadAccumulatorWideInt(const ir::AstNode * node,int64_t number)2736 void ETSGen::LoadAccumulatorWideInt(const ir::AstNode *node, int64_t number)
2737 {
2738 LoadAccumulatorNumber<int64_t>(node, number, checker::TypeFlag::LONG);
2739 }
2740
LoadAccumulatorFloat(const ir::AstNode * node,float number)2741 void ETSGen::LoadAccumulatorFloat(const ir::AstNode *node, float number)
2742 {
2743 LoadAccumulatorNumber<float>(node, number, checker::TypeFlag::FLOAT);
2744 }
2745
LoadAccumulatorDouble(const ir::AstNode * node,double number)2746 void ETSGen::LoadAccumulatorDouble(const ir::AstNode *node, double number)
2747 {
2748 LoadAccumulatorNumber<double>(node, number, checker::TypeFlag::DOUBLE);
2749 }
2750
LoadAccumulatorBoolean(const ir::AstNode * node,bool value)2751 void ETSGen::LoadAccumulatorBoolean(const ir::AstNode *node, bool value)
2752 {
2753 Sa().Emit<Ldai>(node, value ? 1 : 0);
2754 SetAccumulatorType(Checker()->GlobalETSBooleanType());
2755 ApplyConversion(node, Checker()->GlobalETSBooleanType());
2756 }
2757
LoadAccumulatorString(const ir::AstNode * node,util::StringView str)2758 void ETSGen::LoadAccumulatorString(const ir::AstNode *node, util::StringView str)
2759 {
2760 Sa().Emit<LdaStr>(node, str);
2761 SetAccumulatorType(Checker()->GlobalETSStringLiteralType());
2762 }
2763
LoadAccumulatorBigInt(const ir::AstNode * node,util::StringView str)2764 void ETSGen::LoadAccumulatorBigInt(const ir::AstNode *node, util::StringView str)
2765 {
2766 Sa().Emit<LdaStr>(node, str);
2767 SetAccumulatorType(Checker()->GlobalETSBigIntType());
2768 }
2769
LoadAccumulatorUndefined(const ir::AstNode * node)2770 void ETSGen::LoadAccumulatorUndefined(const ir::AstNode *node)
2771 {
2772 Sa().Emit<LdaNull>(node);
2773 SetAccumulatorType(Checker()->GlobalETSUndefinedType());
2774 }
2775
LoadAccumulatorNull(const ir::AstNode * node)2776 void ETSGen::LoadAccumulatorNull([[maybe_unused]] const ir::AstNode *node)
2777 {
2778 #ifdef PANDA_WITH_ETS
2779 Sa().Emit<EtsLdnullvalue>(node);
2780 SetAccumulatorType(Checker()->GlobalETSNullType());
2781 #else
2782 ES2PANDA_UNREACHABLE();
2783 #endif // PANDA_WITH_ETS
2784 }
2785
LoadAccumulatorChar(const ir::AstNode * node,char16_t value)2786 void ETSGen::LoadAccumulatorChar(const ir::AstNode *node, char16_t value)
2787 {
2788 Sa().Emit<Ldai>(node, value);
2789 SetAccumulatorType(Checker()->GlobalCharType());
2790 ApplyConversion(node, Checker()->GlobalCharType());
2791 }
2792
Unary(const ir::AstNode * node,lexer::TokenType op)2793 void ETSGen::Unary(const ir::AstNode *node, lexer::TokenType op)
2794 {
2795 switch (op) {
2796 case lexer::TokenType::PUNCTUATOR_PLUS:
2797 break; // NOP -> Unary numeric promotion is performed
2798 case lexer::TokenType::PUNCTUATOR_MINUS:
2799 UnaryMinus(node);
2800 break;
2801 case lexer::TokenType::PUNCTUATOR_TILDE:
2802 UnaryTilde(node);
2803 break;
2804 case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK:
2805 LogicalNot(node);
2806 break;
2807 default:
2808 ES2PANDA_UNREACHABLE();
2809 }
2810 }
2811
UnaryMinus(const ir::AstNode * node)2812 void ETSGen::UnaryMinus(const ir::AstNode *node)
2813 {
2814 ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
2815 if (GetAccumulatorType()->IsETSBigIntType()) {
2816 const VReg value = AllocReg();
2817 StoreAccumulator(node, value);
2818 CallExact(node, Signatures::BUILTIN_BIGINT_NEGATE, value);
2819 return;
2820 }
2821
2822 switch (checker::ETSChecker::ETSType(GetAccumulatorType())) {
2823 case checker::TypeFlag::LONG: {
2824 Sa().Emit<NegWide>(node);
2825 break;
2826 }
2827 case checker::TypeFlag::INT:
2828 case checker::TypeFlag::SHORT:
2829 case checker::TypeFlag::CHAR:
2830 case checker::TypeFlag::BYTE: {
2831 Sa().Emit<Neg>(node);
2832 break;
2833 }
2834 case checker::TypeFlag::DOUBLE: {
2835 Sa().Emit<FnegWide>(node);
2836 break;
2837 }
2838 case checker::TypeFlag::FLOAT: {
2839 Sa().Emit<Fneg>(node);
2840 break;
2841 }
2842 default: {
2843 ES2PANDA_UNREACHABLE();
2844 }
2845 }
2846 }
2847
UnaryTilde(const ir::AstNode * node)2848 void ETSGen::UnaryTilde(const ir::AstNode *node)
2849 {
2850 ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
2851 if (GetAccumulatorType()->IsETSBigIntType()) {
2852 const VReg value = AllocReg();
2853 StoreAccumulator(node, value);
2854 CallExact(node, Signatures::BUILTIN_BIGINT_OPERATOR_BITWISE_NOT, value);
2855 SetAccumulatorType(Checker()->GlobalETSBigIntType());
2856 return;
2857 }
2858
2859 switch (checker::ETSChecker::ETSType(GetAccumulatorType())) {
2860 case checker::TypeFlag::LONG: {
2861 Sa().Emit<NotWide>(node);
2862 break;
2863 }
2864 case checker::TypeFlag::INT:
2865 case checker::TypeFlag::SHORT:
2866 case checker::TypeFlag::CHAR:
2867 case checker::TypeFlag::BYTE: {
2868 Sa().Emit<Not>(node);
2869 break;
2870 }
2871 default: {
2872 ES2PANDA_UNREACHABLE();
2873 }
2874 }
2875 }
2876
Update(const ir::AstNode * node,lexer::TokenType op)2877 void ETSGen::Update(const ir::AstNode *node, lexer::TokenType op)
2878 {
2879 switch (op) {
2880 case lexer::TokenType::PUNCTUATOR_PLUS_PLUS: {
2881 UpdateOperator<Add2Wide, Addi, Fadd2Wide, Fadd2>(node);
2882 break;
2883 }
2884 case lexer::TokenType::PUNCTUATOR_MINUS_MINUS: {
2885 UpdateOperator<Sub2Wide, Subi, Fsub2Wide, Fsub2>(node);
2886 break;
2887 }
2888 default: {
2889 ES2PANDA_UNREACHABLE();
2890 }
2891 }
2892 }
2893
UpdateBigInt(const ir::Expression * node,VReg arg,lexer::TokenType op)2894 void ETSGen::UpdateBigInt(const ir::Expression *node, VReg arg, lexer::TokenType op)
2895 {
2896 switch (op) {
2897 case lexer::TokenType::PUNCTUATOR_PLUS_PLUS: {
2898 CallBigIntUnaryOperator(node, arg, compiler::Signatures::BUILTIN_BIGINT_OPERATOR_INCREMENT);
2899 break;
2900 }
2901 case lexer::TokenType::PUNCTUATOR_MINUS_MINUS: {
2902 CallBigIntUnaryOperator(node, arg, compiler::Signatures::BUILTIN_BIGINT_OPERATOR_DECREMENT);
2903 break;
2904 }
2905 default: {
2906 ES2PANDA_UNREACHABLE();
2907 }
2908 }
2909 }
2910
StringBuilderAppend(const ir::AstNode * node,VReg builder)2911 void ETSGen::StringBuilderAppend(const ir::AstNode *node, VReg builder)
2912 {
2913 RegScope rs(this);
2914 util::StringView signature {};
2915
2916 node->Compile(this);
2917
2918 std::unordered_map<checker::TypeFlag, std::string_view> typeFlagToSignaturesMap {
2919 {checker::TypeFlag::ETS_BOOLEAN, Signatures::BUILTIN_STRING_BUILDER_APPEND_BOOLEAN},
2920 {checker::TypeFlag::CHAR, Signatures::BUILTIN_STRING_BUILDER_APPEND_CHAR},
2921 {checker::TypeFlag::SHORT, Signatures::BUILTIN_STRING_BUILDER_APPEND_INT},
2922 {checker::TypeFlag::BYTE, Signatures::BUILTIN_STRING_BUILDER_APPEND_INT},
2923 {checker::TypeFlag::INT, Signatures::BUILTIN_STRING_BUILDER_APPEND_INT},
2924 {checker::TypeFlag::LONG, Signatures::BUILTIN_STRING_BUILDER_APPEND_LONG},
2925 {checker::TypeFlag::FLOAT, Signatures::BUILTIN_STRING_BUILDER_APPEND_FLOAT},
2926 {checker::TypeFlag::DOUBLE, Signatures::BUILTIN_STRING_BUILDER_APPEND_DOUBLE},
2927 };
2928
2929 auto search = typeFlagToSignaturesMap.find(checker::ETSChecker::ETSType(GetAccumulatorType()));
2930 if (search != typeFlagToSignaturesMap.end()) {
2931 signature = search->second;
2932 } else {
2933 signature = Signatures::BUILTIN_STRING_BUILDER_APPEND_BUILTIN_STRING;
2934 }
2935
2936 ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
2937 if (GetAccumulatorType()->IsETSReferenceType() && !GetAccumulatorType()->IsETSStringType()) {
2938 if (GetAccumulatorType()->PossiblyETSUndefined()) {
2939 Label *ifUndefined = AllocLabel();
2940 Label *end = AllocLabel();
2941 BranchIfUndefined(node, ifUndefined);
2942 Ra().Emit<CallVirtAccShort, 0>(node, Signatures::BUILTIN_OBJECT_TO_STRING, dummyReg_, 0);
2943 JumpTo(node, end);
2944
2945 SetLabel(node, ifUndefined);
2946 LoadAccumulatorString(node, "undefined");
2947
2948 SetLabel(node, end);
2949 } else {
2950 Ra().Emit<CallVirtAccShort, 0>(node, Signatures::BUILTIN_OBJECT_TO_STRING, dummyReg_, 0);
2951 }
2952 }
2953
2954 VReg arg0 = AllocReg();
2955 StoreAccumulator(node, arg0);
2956
2957 CallExact(node, signature, builder, arg0);
2958 SetAccumulatorType(Checker()->GetGlobalTypesHolder()->GlobalStringBuilderBuiltinType());
2959 }
2960
AppendString(const ir::Expression * const expr,const VReg builder)2961 void ETSGen::AppendString(const ir::Expression *const expr, const VReg builder)
2962 {
2963 ES2PANDA_ASSERT((expr->IsBinaryExpression() &&
2964 expr->AsBinaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS) ||
2965 (expr->IsAssignmentExpression() &&
2966 expr->AsAssignmentExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS_EQUAL));
2967
2968 if (expr->IsBinaryExpression()) {
2969 StringBuilder(expr->AsBinaryExpression()->Left(), expr->AsBinaryExpression()->Right(), builder);
2970 } else {
2971 StringBuilder(expr->AsAssignmentExpression()->Left(), expr->AsAssignmentExpression()->Right(), builder);
2972 }
2973 }
2974
StringBuilder(const ir::Expression * const left,const ir::Expression * const right,const VReg builder)2975 void ETSGen::StringBuilder(const ir::Expression *const left, const ir::Expression *const right, const VReg builder)
2976 {
2977 if (left->IsBinaryExpression() && left->TsType()->IsETSStringType()) {
2978 AppendString(left->AsBinaryExpression(), builder);
2979 } else {
2980 StringBuilderAppend(left, builder);
2981 }
2982
2983 StringBuilderAppend(right, builder);
2984 }
2985
BuildString(const ir::Expression * node)2986 void ETSGen::BuildString(const ir::Expression *node)
2987 {
2988 RegScope rs(this);
2989
2990 Ra().Emit<InitobjShort, 0>(node, Signatures::BUILTIN_STRING_BUILDER_CTOR, dummyReg_, dummyReg_);
2991 SetAccumulatorType(Checker()->GlobalStringBuilderBuiltinType());
2992
2993 auto builder = AllocReg();
2994 StoreAccumulator(node, builder);
2995
2996 AppendString(node, builder);
2997 CallExact(node, Signatures::BUILTIN_STRING_BUILDER_TO_STRING, builder);
2998
2999 SetAccumulatorType(node->TsType());
3000 }
3001
CallBigIntUnaryOperator(const ir::Expression * node,VReg arg,const util::StringView signature)3002 void ETSGen::CallBigIntUnaryOperator(const ir::Expression *node, VReg arg, const util::StringView signature)
3003 {
3004 LoadAccumulator(node, arg);
3005 CallExact(node, signature, arg);
3006 SetAccumulatorType(Checker()->GlobalETSBigIntType());
3007 }
3008
CallBigIntBinaryOperator(const ir::Expression * node,VReg lhs,VReg rhs,const util::StringView signature)3009 void ETSGen::CallBigIntBinaryOperator(const ir::Expression *node, VReg lhs, VReg rhs, const util::StringView signature)
3010 {
3011 LoadAccumulator(node, lhs);
3012 CallExact(node, signature, lhs, rhs);
3013 SetAccumulatorType(Checker()->GlobalETSBigIntType());
3014 }
3015
CallBigIntBinaryComparison(const ir::Expression * node,VReg lhs,VReg rhs,const util::StringView signature)3016 void ETSGen::CallBigIntBinaryComparison(const ir::Expression *node, VReg lhs, VReg rhs,
3017 const util::StringView signature)
3018 {
3019 LoadAccumulator(node, lhs);
3020 CallExact(node, signature, lhs, rhs);
3021 SetAccumulatorType(Checker()->GlobalETSBooleanType());
3022 }
3023
BuildTemplateString(const ir::TemplateLiteral * node)3024 void ETSGen::BuildTemplateString(const ir::TemplateLiteral *node)
3025 {
3026 RegScope rs(this);
3027
3028 Ra().Emit<InitobjShort, 0>(node, Signatures::BUILTIN_STRING_BUILDER_CTOR, dummyReg_, dummyReg_);
3029 SetAccumulatorType(Checker()->GlobalStringBuilderBuiltinType());
3030
3031 auto builder = AllocReg();
3032 StoreAccumulator(node, builder);
3033
3034 // Just to reduce extra nested level(s):
3035 auto const appendExpressions = [this, &builder](ArenaVector<ir::Expression *> const &expressions,
3036 ArenaVector<ir::TemplateElement *> const &quasis) -> void {
3037 auto const num = expressions.size();
3038 std::size_t i = 0U;
3039
3040 while (i < num) {
3041 StringBuilderAppend(expressions[i], builder);
3042 if (!quasis[++i]->Raw().Empty()) {
3043 StringBuilderAppend(quasis[i], builder);
3044 }
3045 }
3046 };
3047
3048 if (auto const &quasis = node->Quasis(); !quasis.empty()) {
3049 if (!quasis[0]->Raw().Empty()) {
3050 StringBuilderAppend(quasis[0], builder);
3051 }
3052
3053 if (auto const &expressions = node->Expressions(); !expressions.empty()) {
3054 appendExpressions(expressions, quasis);
3055 }
3056 }
3057
3058 CallExact(node, Signatures::BUILTIN_STRING_BUILDER_TO_STRING, builder);
3059
3060 SetAccumulatorType(node->TsType());
3061 }
3062
NewObject(const ir::AstNode * const node,const util::StringView name,VReg athis)3063 void ETSGen::NewObject(const ir::AstNode *const node, const util::StringView name, VReg athis)
3064 {
3065 Ra().Emit<Newobj>(node, athis, name);
3066 SetVRegType(athis, Checker()->GlobalETSObjectType());
3067 }
3068
NewArray(const ir::AstNode * const node,const VReg arr,const VReg dim,const checker::Type * const arrType)3069 void ETSGen::NewArray(const ir::AstNode *const node, const VReg arr, const VReg dim, const checker::Type *const arrType)
3070 {
3071 std::stringstream ss;
3072 arrType->ToAssemblerTypeWithRank(ss);
3073 const auto res = ProgElement()->Strings().emplace(ss.str());
3074
3075 Ra().Emit<Newarr>(node, arr, dim, util::StringView(*res.first));
3076 SetVRegType(arr, arrType);
3077 }
3078
LoadResizableArrayLength(const ir::AstNode * node)3079 void ETSGen::LoadResizableArrayLength(const ir::AstNode *node)
3080 {
3081 Ra().Emit<CallAccShort, 0>(node, Signatures::BUILTIN_ARRAY_LENGTH, dummyReg_, 0);
3082 Sa().Emit<F64toi32>(node);
3083 SetAccumulatorType(Checker()->GlobalIntType());
3084 }
3085
LoadResizableArrayElement(const ir::AstNode * node,const VReg arrObj,const VReg arrIndex)3086 void ETSGen::LoadResizableArrayElement(const ir::AstNode *node, const VReg arrObj, const VReg arrIndex)
3087 {
3088 auto *vRegType = GetVRegType(arrObj);
3089 ES2PANDA_ASSERT(vRegType != nullptr);
3090 auto *elementType = vRegType->AsETSResizableArrayType()->ElementType();
3091 Ra().Emit<CallVirtShort>(node, Signatures::BUILTIN_ARRAY_GET_ELEMENT, arrObj, arrIndex);
3092 SetAccumulatorType(elementType);
3093 }
3094
LoadArrayLength(const ir::AstNode * node,VReg arrayReg)3095 void ETSGen::LoadArrayLength(const ir::AstNode *node, VReg arrayReg)
3096 {
3097 Ra().Emit<Lenarr>(node, arrayReg);
3098 SetAccumulatorType(Checker()->GlobalIntType());
3099 }
3100
LoadArrayElement(const ir::AstNode * node,VReg objectReg)3101 void ETSGen::LoadArrayElement(const ir::AstNode *node, VReg objectReg)
3102 {
3103 ES2PANDA_ASSERT(GetVRegType(objectReg) != nullptr);
3104 auto *elementType = GetVRegType(objectReg)->AsETSArrayType()->ElementType();
3105 if (elementType->IsETSReferenceType()) {
3106 Ra().Emit<LdarrObj>(node, objectReg);
3107 SetAccumulatorType(elementType);
3108 return;
3109 }
3110 switch (checker::ETSChecker::ETSType(elementType)) {
3111 case checker::TypeFlag::ETS_BOOLEAN:
3112 case checker::TypeFlag::BYTE: {
3113 Ra().Emit<Ldarr8>(node, objectReg);
3114 break;
3115 }
3116 case checker::TypeFlag::CHAR: {
3117 Ra().Emit<Ldarru16>(node, objectReg);
3118 break;
3119 }
3120 case checker::TypeFlag::SHORT: {
3121 Ra().Emit<Ldarr16>(node, objectReg);
3122 break;
3123 }
3124 case checker::TypeFlag::INT: {
3125 Ra().Emit<Ldarr>(node, objectReg);
3126 break;
3127 }
3128 case checker::TypeFlag::LONG: {
3129 Ra().Emit<LdarrWide>(node, objectReg);
3130 break;
3131 }
3132 case checker::TypeFlag::FLOAT: {
3133 Ra().Emit<Fldarr32>(node, objectReg);
3134 break;
3135 }
3136 case checker::TypeFlag::DOUBLE: {
3137 Ra().Emit<FldarrWide>(node, objectReg);
3138 break;
3139 }
3140
3141 default: {
3142 ES2PANDA_UNREACHABLE();
3143 }
3144 }
3145
3146 SetAccumulatorType(elementType);
3147 }
3148
StoreArrayElement(const ir::AstNode * node,VReg objectReg,VReg index,const checker::Type * elementType)3149 void ETSGen::StoreArrayElement(const ir::AstNode *node, VReg objectReg, VReg index, const checker::Type *elementType)
3150 {
3151 if (elementType->IsETSReferenceType()) {
3152 Ra().Emit<StarrObj>(node, objectReg, index);
3153 SetAccumulatorType(elementType);
3154 return;
3155 }
3156 switch (checker::ETSChecker::ETSType(elementType)) {
3157 case checker::TypeFlag::ETS_BOOLEAN:
3158 case checker::TypeFlag::BYTE: {
3159 Ra().Emit<Starr8>(node, objectReg, index);
3160 break;
3161 }
3162 case checker::TypeFlag::CHAR:
3163 case checker::TypeFlag::SHORT: {
3164 Ra().Emit<Starr16>(node, objectReg, index);
3165 break;
3166 }
3167 case checker::TypeFlag::INT: {
3168 Ra().Emit<Starr>(node, objectReg, index);
3169 break;
3170 }
3171 case checker::TypeFlag::LONG: {
3172 Ra().Emit<StarrWide>(node, objectReg, index);
3173 break;
3174 }
3175 case checker::TypeFlag::FLOAT: {
3176 Ra().Emit<Fstarr32>(node, objectReg, index);
3177 break;
3178 }
3179 case checker::TypeFlag::DOUBLE: {
3180 Ra().Emit<FstarrWide>(node, objectReg, index);
3181 break;
3182 }
3183
3184 default: {
3185 ES2PANDA_UNREACHABLE();
3186 }
3187 }
3188
3189 SetAccumulatorType(elementType);
3190 }
3191
GetTupleMemberNameForIndex(const std::size_t index) const3192 util::StringView ETSGen::GetTupleMemberNameForIndex(const std::size_t index) const
3193 {
3194 return util::UString("$" + std::to_string(index), Allocator()).View();
3195 }
3196
LoadTupleElement(const ir::AstNode * node,VReg objectReg,const checker::Type * elementType,std::size_t index)3197 void ETSGen::LoadTupleElement(const ir::AstNode *node, VReg objectReg, const checker::Type *elementType,
3198 std::size_t index)
3199 {
3200 ES2PANDA_ASSERT(GetVRegType(objectReg) != nullptr && GetVRegType(objectReg)->IsETSTupleType());
3201 const auto propName = FormClassPropReference(GetVRegType(objectReg)->AsETSTupleType()->GetWrapperType(),
3202 GetTupleMemberNameForIndex(index));
3203
3204 // NOTE (smartin): remove after generics without type erasure is possible
3205 const auto *const boxedElementType = Checker()->MaybeBoxType(elementType);
3206 LoadProperty(node, boxedElementType, objectReg, propName);
3207 }
3208
StoreTupleElement(const ir::AstNode * node,VReg objectReg,const checker::Type * elementType,std::size_t index)3209 void ETSGen::StoreTupleElement(const ir::AstNode *node, VReg objectReg, const checker::Type *elementType,
3210 std::size_t index)
3211 {
3212 ES2PANDA_ASSERT(GetVRegType(objectReg) != nullptr && GetVRegType(objectReg)->IsETSTupleType());
3213 const auto *const tupleType = GetVRegType(objectReg)->AsETSTupleType();
3214 SetVRegType(objectReg, tupleType->GetWrapperType());
3215
3216 // NOTE (smartin): remove after generics without type erasure is possible
3217 const auto *const boxedElementType = Checker()->MaybeBoxType(elementType);
3218 StoreProperty(node, boxedElementType, objectReg, GetTupleMemberNameForIndex(index));
3219 }
3220
3221 template <typename T>
IncrementImmediateRegister(const ir::AstNode * node,VReg reg,const checker::TypeFlag valueType,T const value)3222 void ETSGen::IncrementImmediateRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType,
3223 T const value)
3224 {
3225 switch (valueType) {
3226 // NOTE: operand of increment instruction (INCI) is defined in spec as 32-bit integer,
3227 // but its current implementation actually can work with 64-bit integers as well.
3228 case checker::TypeFlag::INT: {
3229 Ra().Emit<Inci>(node, reg, static_cast<checker::IntType::UType>(value));
3230 break;
3231 }
3232 case checker::TypeFlag::CHAR: {
3233 Ra().Emit<Inci>(node, reg, static_cast<checker::CharType::UType>(value));
3234 break;
3235 }
3236 case checker::TypeFlag::SHORT: {
3237 Ra().Emit<Inci>(node, reg, static_cast<checker::ShortType::UType>(value));
3238 break;
3239 }
3240 case checker::TypeFlag::ETS_BOOLEAN:
3241 [[fallthrough]];
3242 case checker::TypeFlag::BYTE: {
3243 Ra().Emit<Inci>(node, reg, static_cast<checker::ByteType::UType>(value));
3244 break;
3245 }
3246 default: {
3247 ES2PANDA_UNREACHABLE();
3248 }
3249 }
3250 }
3251
3252 template void ETSGen::IncrementImmediateRegister<int32_t>(const ir::AstNode *node, VReg reg,
3253 const checker::TypeFlag valueType, int32_t const value);
3254
LoadStringLength(const ir::AstNode * node)3255 void ETSGen::LoadStringLength(const ir::AstNode *node)
3256 {
3257 Ra().Emit<CallAccShort, 0>(node, Signatures::BUILTIN_STRING_LENGTH, dummyReg_, 0);
3258 SetAccumulatorType(Checker()->GlobalIntType());
3259 }
3260
FloatIsNaN(const ir::AstNode * node)3261 void ETSGen::FloatIsNaN(const ir::AstNode *node)
3262 {
3263 Ra().Emit<CallAccShort, 0>(node, Signatures::BUILTIN_FLOAT_IS_NAN, dummyReg_, 0);
3264 SetAccumulatorType(Checker()->GlobalETSBooleanType());
3265 }
3266
DoubleIsNaN(const ir::AstNode * node)3267 void ETSGen::DoubleIsNaN(const ir::AstNode *node)
3268 {
3269 Ra().Emit<CallAccShort, 0>(node, Signatures::BUILTIN_DOUBLE_IS_NAN, dummyReg_, 0);
3270 SetAccumulatorType(Checker()->GlobalETSBooleanType());
3271 }
3272
LoadStringChar(const ir::AstNode * node,const VReg stringObj,const VReg charIndex)3273 void ETSGen::LoadStringChar(const ir::AstNode *node, const VReg stringObj, const VReg charIndex)
3274 {
3275 Ra().Emit<CallShort>(node, Signatures::BUILTIN_STRING_CHAR_AT, stringObj, charIndex);
3276 SetAccumulatorType(Checker()->GlobalCharType());
3277 }
3278
ThrowException(const ir::Expression * expr)3279 void ETSGen::ThrowException(const ir::Expression *expr)
3280 {
3281 RegScope rs(this);
3282
3283 expr->Compile(this);
3284 VReg arg = AllocReg();
3285 StoreAccumulator(expr, arg);
3286 EmitThrow(expr, arg);
3287 }
3288
ExtendWithFinalizer(ir::AstNode const * node,const ir::AstNode * originalNode,Label * prevFinnaly)3289 bool ETSGen::ExtendWithFinalizer(ir::AstNode const *node, const ir::AstNode *originalNode, Label *prevFinnaly)
3290 {
3291 ES2PANDA_ASSERT(originalNode != nullptr);
3292
3293 if (node == nullptr || !node->IsStatement()) {
3294 return false;
3295 }
3296
3297 if ((originalNode->IsContinueStatement() && originalNode->AsContinueStatement()->Target() == node) ||
3298 (originalNode->IsBreakStatement() && originalNode->AsBreakStatement()->Target() == node)) {
3299 return false;
3300 }
3301
3302 if (node->IsTryStatement() && node->AsTryStatement()->HasFinalizer()) {
3303 Label *beginLabel = nullptr;
3304
3305 if (prevFinnaly == nullptr) {
3306 beginLabel = AllocLabel();
3307 Branch(originalNode, beginLabel);
3308 } else {
3309 beginLabel = prevFinnaly;
3310 }
3311
3312 Label *endLabel = AllocLabel();
3313
3314 if (node->Parent() != nullptr && node->Parent()->IsStatement()) {
3315 if (!ExtendWithFinalizer(node->Parent(), originalNode, endLabel)) {
3316 endLabel = nullptr;
3317 }
3318 } else {
3319 endLabel = nullptr;
3320 }
3321
3322 LabelPair insertion = compiler::LabelPair(beginLabel, endLabel);
3323
3324 auto *tryStatement = const_cast<ir::AstNode *>(node)->AsTryStatement();
3325 tryStatement->AddFinalizerInsertion(insertion, originalNode->AsStatement());
3326
3327 return true;
3328 }
3329
3330 auto *parent = node->Parent();
3331
3332 if (parent == nullptr || !parent->IsStatement()) {
3333 return false;
3334 }
3335
3336 if (parent->IsTryStatement() && node->IsBlockStatement() &&
3337 parent->AsTryStatement()->FinallyBlock() == node->AsBlockStatement()) {
3338 parent = parent->Parent();
3339 }
3340
3341 return ExtendWithFinalizer(parent, originalNode, prevFinnaly);
3342 }
3343
ToAssemblerType(const es2panda::checker::Type * type) const3344 util::StringView ETSGen::ToAssemblerType(const es2panda::checker::Type *type) const
3345 {
3346 ES2PANDA_ASSERT(type != nullptr && type->IsETSReferenceType());
3347
3348 std::stringstream ss;
3349 type->ToAssemblerTypeWithRank(ss);
3350 return util::UString(ss.str(), Allocator()).View();
3351 }
3352
3353 template <typename T>
SetAccumulatorTargetType(const ir::AstNode * node,checker::TypeFlag typeKind,T number)3354 void ETSGen::SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number)
3355 {
3356 switch (typeKind) {
3357 case checker::TypeFlag::ETS_BOOLEAN:
3358 case checker::TypeFlag::BYTE: {
3359 Sa().Emit<Ldai>(node, static_cast<checker::ByteType::UType>(number));
3360 SetAccumulatorType(Checker()->GlobalByteType());
3361 break;
3362 }
3363 case checker::TypeFlag::CHAR: {
3364 Sa().Emit<Ldai>(node, static_cast<checker::CharType::UType>(number));
3365 SetAccumulatorType(Checker()->GlobalCharType());
3366 break;
3367 }
3368 case checker::TypeFlag::SHORT: {
3369 Sa().Emit<Ldai>(node, static_cast<checker::ShortType::UType>(number));
3370 SetAccumulatorType(Checker()->GlobalShortType());
3371 break;
3372 }
3373 case checker::TypeFlag::INT: {
3374 Sa().Emit<Ldai>(node, static_cast<checker::IntType::UType>(number));
3375 SetAccumulatorType(Checker()->GlobalIntType());
3376 break;
3377 }
3378 case checker::TypeFlag::LONG: {
3379 Sa().Emit<LdaiWide>(node, static_cast<checker::LongType::UType>(number));
3380 SetAccumulatorType(Checker()->GlobalLongType());
3381 break;
3382 }
3383 case checker::TypeFlag::FLOAT: {
3384 Sa().Emit<Fldai>(node, static_cast<checker::FloatType::UType>(number));
3385 SetAccumulatorType(Checker()->GlobalFloatType());
3386 break;
3387 }
3388 case checker::TypeFlag::DOUBLE: {
3389 Sa().Emit<FldaiWide>(node, static_cast<checker::DoubleType::UType>(number));
3390 SetAccumulatorType(Checker()->GlobalDoubleType());
3391 break;
3392 }
3393 default: {
3394 ES2PANDA_UNREACHABLE();
3395 }
3396 }
3397 }
3398
3399 template <typename T>
LoadAccumulatorNumber(const ir::AstNode * node,T number,checker::TypeFlag targetType)3400 void ETSGen::LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType)
3401 {
3402 // NOTE (smartin): refactor this to do at a more appropriate place
3403 const bool isContextTargetTypeValid =
3404 targetType_ != nullptr &&
3405 (!targetType_->IsETSObjectType() && !targetType_->IsETSUnionType() && !targetType_->IsETSArrayType() &&
3406 !targetType_->IsETSTupleType() && !targetType_->IsETSTypeParameter());
3407
3408 auto typeKind = isContextTargetTypeValid ? checker::ETSChecker::TypeKind(targetType_) : targetType;
3409
3410 SetAccumulatorTargetType(node, typeKind, number);
3411
3412 if (targetType_ && (targetType_->IsETSObjectType() || targetType_->IsETSUnionType())) {
3413 ApplyConversion(node, targetType_);
3414 }
3415 }
3416 } // namespace ark::es2panda::compiler
3417