/* * Copyright (c) 2021-2025 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ES2PANDA_COMPILER_CORE_ETSGEN_H #define ES2PANDA_COMPILER_CORE_ETSGEN_H #include "ir/astNode.h" #include "compiler/core/codeGen.h" #include "compiler/core/ETSfunction.h" #include "compiler/core/targetTypeContext.h" #include "checker/ETSchecker.h" #include "util/helpers.h" #include namespace ark::es2panda::compiler { class ETSGen final : public CodeGen { public: explicit ETSGen(ArenaAllocator *allocator, RegSpiller *spiller, public_lib::Context *context, std::tuple toCompile) noexcept; [[nodiscard]] const checker::ETSChecker *Checker() const noexcept; [[nodiscard]] const varbinder::ETSBinder *VarBinder() const noexcept; [[nodiscard]] const checker::Type *ReturnType() const noexcept; [[nodiscard]] const checker::ETSObjectType *ContainingObjectType() const noexcept; [[nodiscard]] VReg &Acc() noexcept; [[nodiscard]] VReg Acc() const noexcept; void SetAccumulatorType(const checker::Type *type); [[nodiscard]] const checker::Type *GetAccumulatorType() const; void CompileAndCheck(const ir::Expression *expr); [[nodiscard]] VReg StoreException(const ir::AstNode *node); void ApplyConversionAndStoreAccumulator(const ir::AstNode *node, VReg vreg, const checker::Type *targetType); void StoreAccumulator(const ir::AstNode *node, VReg vreg); void LoadAccumulator(const ir::AstNode *node, VReg vreg); [[nodiscard]] IRNode *AllocMov(const ir::AstNode *node, VReg vd, VReg vs) override; [[nodiscard]] IRNode *AllocMov(const ir::AstNode *node, OutVReg vd, VReg vs) override; void MoveVreg(const ir::AstNode *node, VReg vd, VReg vs); [[nodiscard]] checker::Type const *TypeForVar(varbinder::Variable const *var) const noexcept override; void LoadVar(const ir::Identifier *node, varbinder::Variable const *var); void LoadDynamicModuleVariable(const ir::AstNode *node, varbinder::Variable const *var); void LoadDynamicNamespaceVariable(const ir::AstNode *node, varbinder::Variable const *var); void StoreVar(const ir::Identifier *node, const varbinder::ConstScopeFindResult &result); void LoadStaticProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &fullName); void StoreStaticProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &fullName); void StoreStaticOwnProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &name); [[nodiscard]] util::StringView FormClassPropReference(const checker::ETSObjectType *classType, const util::StringView &name); void StoreProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &name); void LoadProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &fullName); void StorePropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &propName); void LoadPropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg, std::variant property); void StoreElementDynamic(const ir::AstNode *node, VReg objectReg, VReg index); void LoadElementDynamic(const ir::AstNode *node, VReg objectReg); void StorePropertyByName(const ir::AstNode *node, VReg objReg, checker::ETSChecker::NamedAccessMeta const &fieldMeta); void LoadPropertyByName(const ir::AstNode *node, VReg objReg, checker::ETSChecker::NamedAccessMeta const &fieldMeta); void LoadUndefinedDynamic(const ir::AstNode *node, Language lang); void LoadThis(const ir::AstNode *node); [[nodiscard]] VReg GetThisReg() const; const checker::Type *LoadDefaultValue(const ir::AstNode *node, const checker::Type *type); void EmitReturnVoid(const ir::AstNode *node); void ReturnAcc(const ir::AstNode *node); void BranchIfIsInstance(const ir::AstNode *node, VReg srcReg, const checker::Type *target, Label *ifTrue); void IsInstance(const ir::AstNode *node, VReg srcReg, checker::Type const *target); void IsInstanceDynamic(const ir::BinaryExpression *node, VReg srcReg, VReg tgtReg); void EmitFailedTypeCastException(const ir::AstNode *node, VReg src, checker::Type const *target); void BinaryLogic(const ir::AstNode *node, lexer::TokenType op, VReg lhs); void BinaryArithmLogic(const ir::AstNode *node, lexer::TokenType op, VReg lhs); void Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs); void Unary(const ir::AstNode *node, lexer::TokenType op); void Update(const ir::AstNode *node, lexer::TokenType op); void UpdateBigInt(const ir::Expression *node, VReg arg, lexer::TokenType op); void Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *ifFalse); template void ResolveConditionalResultFloat(const ir::AstNode *node, Label *realEndLabel); template void ResolveConditionalResultNumeric(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse, Label **end); template void ResolveConditionalResultReference(const ir::AstNode *node); template void ResolveConditionalResult(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse); template void ResolveConditionalResultIfFalse(const ir::AstNode *node, Label *ifFalse = nullptr); template void ResolveConditionalResultIfTrue(const ir::AstNode *node, Label *ifFalse = nullptr); template void BranchConditional(const ir::AstNode *node, Label *endLabel); void ConditionalFloat(const ir::AstNode *node); void BranchConditionalIfFalse(const ir::AstNode *node, Label *endLabel); void BranchConditionalIfTrue(const ir::AstNode *node, Label *endLabel); void BranchIfFalse(const ir::AstNode *node, Label *ifFalse) { Sa().Emit(node, ifFalse); } void BranchIfTrue(const ir::AstNode *node, Label *ifTrue) { Sa().Emit(node, ifTrue); } void BranchIfUndefined(const ir::AstNode *node, Label *ifNull) { Sa().Emit(node, ifNull); } void BranchIfNotUndefined(const ir::AstNode *node, Label *ifNotNull) { Sa().Emit(node, ifNotNull); } void BranchIfNull(const ir::AstNode *node, Label *ifTaken) { EmitIsNull(node); BranchIfTrue(node, ifTaken); } void BranchIfNotNull(const ir::AstNode *node, Label *ifTaken) { EmitIsNull(node); BranchIfFalse(node, ifTaken); } void BranchIfNullish(const ir::AstNode *node, Label *ifNullish); void BranchIfNotNullish(const ir::AstNode *node, Label *ifNotNullish); void AssumeNonNullish(const ir::AstNode *node, checker::Type const *targetType); void JumpTo(const ir::AstNode *node, Label *labelTo) { Sa().Emit(node, labelTo); } void EmitThrow(const ir::AstNode *node, VReg err) { Ra().Emit(node, err); } void EmitNullishException(const ir::AstNode *node); void ThrowException(const ir::Expression *expr); bool ExtendWithFinalizer(ir::AstNode const *node, const ir::AstNode *originalNode, Label *prevFinnaly = nullptr); void Negate(const ir::AstNode *node); void LogicalNot(const ir::AstNode *node); void LoadAccumulatorByte(const ir::AstNode *node, int8_t number); void LoadAccumulatorShort(const ir::AstNode *node, int16_t number); void LoadAccumulatorInt(const ir::AstNode *node, int32_t number); void LoadAccumulatorWideInt(const ir::AstNode *node, int64_t number); void LoadAccumulatorFloat(const ir::AstNode *node, float number); void LoadAccumulatorDouble(const ir::AstNode *node, double number); void LoadAccumulatorBoolean(const ir::AstNode *node, bool value); void LoadAccumulatorString(const ir::AstNode *node, util::StringView str); void LoadAccumulatorBigInt(const ir::AstNode *node, util::StringView str); void LoadAccumulatorUndefined(const ir::AstNode *node); void LoadAccumulatorNull([[maybe_unused]] const ir::AstNode *node); void LoadAccumulatorPoison(const ir::AstNode *node, const checker::Type *type); void LoadAccumulatorChar(const ir::AstNode *node, char16_t value); void LoadAccumulatorDynamicModule(const ir::AstNode *node, const ir::ETSImportDeclaration *import); void ApplyBoxingConversion(const ir::AstNode *node); void ApplyUnboxingConversion(const ir::AstNode *node); void ApplyConversion(const ir::AstNode *node) { if (targetType_ != nullptr) { ApplyConversion(node, targetType_); } } void ApplyConversionCast(const ir::AstNode *node, const checker::Type *targetType); void ApplyConversion(const ir::AstNode *node, const checker::Type *targetType); void ApplyCast(const ir::AstNode *node, const checker::Type *targetType); void ApplyCastToBoxingFlags(const ir::AstNode *node, const ir::BoxingUnboxingFlags targetType); void EmitBoxingConversion(ir::BoxingUnboxingFlags boxingFlag, const ir::AstNode *node); void EmitBoxingConversion(const ir::AstNode *node); void SwapBinaryOpArgs(const ir::AstNode *node, VReg lhs); VReg MoveAccToReg(const ir::AstNode *node); void LoadResizableArrayLength(const ir::AstNode *node); void LoadResizableArrayElement(const ir::AstNode *node, const VReg arrObj, const VReg arrIndex); void LoadArrayLength(const ir::AstNode *node, VReg arrayReg); void LoadArrayElement(const ir::AstNode *node, VReg objectReg); void StoreArrayElement(const ir::AstNode *node, VReg objectReg, VReg index, const checker::Type *elementType); util::StringView GetTupleMemberNameForIndex(std::size_t index) const; void LoadTupleElement(const ir::AstNode *node, VReg objectReg, const checker::Type *elementType, std::size_t index); void StoreTupleElement(const ir::AstNode *node, VReg objectReg, const checker::Type *elementType, std::size_t index); template void MoveImmediateToRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value); template void IncrementImmediateRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value); template void JumpCompareRegister(const ir::AstNode *node, VReg lhs, Label *ifFalse) { Ra().Emit(node, lhs, ifFalse); } void LoadStringLength(const ir::AstNode *node); void LoadStringChar(const ir::AstNode *node, VReg stringObj, VReg charIndex); void FloatIsNaN(const ir::AstNode *node); void DoubleIsNaN(const ir::AstNode *node); void CompileStatements(const ArenaVector &statements); // Cast void CastToBoolean(const ir::AstNode *node); void CastToByte(const ir::AstNode *node); void CastToChar(const ir::AstNode *node); void CastToShort(const ir::AstNode *node); void CastToDouble(const ir::AstNode *node); void CastToFloat(const ir::AstNode *node); void CastToLong(const ir::AstNode *node); void CastToInt(const ir::AstNode *node); void CastToString(const ir::AstNode *node); void CastToDynamic(const ir::AstNode *node, const checker::ETSDynamicType *type); void CastDynamicTo(const ir::AstNode *node, enum checker::TypeFlag typeFlag); void CastToReftype(const ir::AstNode *node, const checker::Type *targetType, bool unchecked); void CastDynamicToObject(const ir::AstNode *node, const checker::Type *targetType); void InternalIsInstance(const ir::AstNode *node, const checker::Type *target); void InternalCheckCast(const ir::AstNode *node, const checker::Type *target); void CheckedReferenceNarrowing(const ir::AstNode *node, const checker::Type *target); void GuardUncheckedType(const ir::AstNode *node, const checker::Type *unchecked, const checker::Type *target); // Call, Construct void NewArray(const ir::AstNode *node, VReg arr, VReg dim, const checker::Type *arrType); void NewObject(const ir::AstNode *node, util::StringView name, VReg athis); void BuildString(const ir::Expression *node); void CallBigIntUnaryOperator(const ir::Expression *node, VReg arg, util::StringView signature); void CallBigIntBinaryOperator(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature); void CallBigIntBinaryComparison(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature); void BuildTemplateString(const ir::TemplateLiteral *node); void InitObject(const ir::AstNode *node, checker::Signature const *signature, const ArenaVector &arguments) { CallImpl(node, signature, arguments); } bool IsDevirtualizedSignature(const checker::Signature *signature) { ES2PANDA_ASSERT(signature != nullptr && !signature->HasSignatureFlag(checker::SignatureFlags::STATIC)); return signature->HasSignatureFlag(checker::SignatureFlags::FINAL | checker::SignatureFlags::PRIVATE | checker::SignatureFlags::CONSTRUCTOR); } void EmitEtsTypeof([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] const VReg reg) { #ifdef PANDA_WITH_ETS Ra().Emit(node, reg); #else ES2PANDA_UNREACHABLE(); #endif // PANDA_WITH_ETS } void EmitEtsIstrue([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] const VReg reg) { #ifdef PANDA_WITH_ETS Ra().Emit(node, reg); #else ES2PANDA_UNREACHABLE(); #endif // PANDA_WITH_ETS } void CallExact(const ir::AstNode *node, checker::Signature *signature, const ArenaVector &arguments) { CallImpl(node, signature, arguments); } void CallExact(const ir::AstNode *const node, const checker::Signature *signature, const VReg arg0, const ArenaVector &arguments) { CallArgStart(node, signature, arg0, arguments); } void CallExact(const ir::AstNode *const node, const util::StringView name) { Ra().Emit(node, name, dummyReg_, dummyReg_); } void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0) { Ra().Emit(node, name, arg0, dummyReg_); } void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0, const VReg arg1) { Ra().Emit(node, name, arg0, arg1); } void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0, const VReg arg1, const VReg arg2) { Ra().Emit(node, name, arg0, arg1, arg2, dummyReg_); } void CallByName([[maybe_unused]] const ir::AstNode *const node, [[maybe_unused]] const checker::Signature *signature, [[maybe_unused]] const VReg arg0, [[maybe_unused]] const ArenaVector &arguments) { #ifdef PANDA_WITH_ETS CallArgStart(node, signature, arg0, arguments); #else ES2PANDA_UNREACHABLE(); #endif } void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis, const ArenaVector &arguments) { ES2PANDA_ASSERT(!signature->HasSignatureFlag(checker::SignatureFlags::STATIC)); ES2PANDA_ASSERT(!signature->Owner()->GetDeclNode()->IsFinal() || signature->IsFinal()); if (IsDevirtualizedSignature(signature)) { CallArgStart(node, signature, athis, arguments); } else { CallArgStart(node, signature, athis, arguments); } } void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis) { if (IsDevirtualizedSignature(signature)) { CallExact(node, signature->InternalName(), athis); } else { CallVirtual(node, signature->InternalName(), athis); } } void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis, const VReg arg0) { if (IsDevirtualizedSignature(signature)) { CallExact(node, signature->InternalName(), athis, arg0); } else { CallVirtual(node, signature->InternalName(), athis, arg0); } } void CallVirtual(const ir::AstNode *const node, const util::StringView name, const VReg athis) { Ra().Emit(node, name, athis, dummyReg_); } void CallVirtual(const ir::AstNode *const node, const util::StringView name, const VReg athis, const VReg arg0) { Ra().Emit(node, name, athis, arg0); } struct CallDynamicData { const ir::AstNode *node = nullptr; VReg obj; VReg param2; }; void CallDynamic(CallDynamicData data, checker::Signature *signature, const ArenaVector &arguments) { CallDynamicImpl(data, signature, arguments); } void CallDynamic(CallDynamicData data, VReg param3, checker::Signature *signature, const ArenaVector &arguments) { CallDynamicImpl(data, param3, signature, arguments); } // until a lowering for implicit super is available void CallRangeFillUndefined(const ir::AstNode *const node, checker::Signature *const signature, const VReg thisReg); void CreateBigIntObject(const ir::AstNode *node, VReg arg0, std::string_view signature = Signatures::BUILTIN_BIGINT_CTOR); void GetType(const ir::AstNode *node, bool isEtsPrimitive) { if (isEtsPrimitive) { // NOTE: SzD. LoadStaticProperty if ETS stdlib has static TYPE constants otherwise fallback to LdaType } else { ES2PANDA_ASSERT(GetAccumulatorType() != nullptr); auto classRef = GetAccumulatorType()->AsETSObjectType()->AssemblerName(); Sa().Emit(node, classRef); } } ~ETSGen() override = default; NO_COPY_SEMANTIC(ETSGen); NO_MOVE_SEMANTIC(ETSGen); private: const VReg dummyReg_ = VReg::RegStart(); void EmitUnboxedCall(const ir::AstNode *node, std::string_view signatureFlag, const checker::Type *targetType, const checker::Type *boxedType); void LoadConstantObject(const ir::Expression *node, const checker::Type *type); void StringBuilderAppend(const ir::AstNode *node, VReg builder); void AppendString(const ir::Expression *binExpr, VReg builder); void StringBuilder(const ir::Expression *left, const ir::Expression *right, VReg builder); util::StringView FormClassPropReference(varbinder::Variable const *var); void UnaryMinus(const ir::AstNode *node); void UnaryTilde(const ir::AstNode *node); util::StringView ToAssemblerType(const es2panda::checker::Type *type) const; void TestIsInstanceConstituent(const ir::AstNode *node, std::tuple