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