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