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