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