/* * Copyright (c) 2021-2024 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. */ #include "ETSGen.h" #include "generated/signatures.h" #include "ir/base/scriptFunction.h" #include "ir/base/classDefinition.h" #include "ir/statement.h" #include "ir/expressions/assignmentExpression.h" #include "ir/expressions/identifier.h" #include "ir/expressions/binaryExpression.h" #include "ir/expressions/callExpression.h" #include "ir/expressions/memberExpression.h" #include "ir/expressions/templateLiteral.h" #include "ir/statements/breakStatement.h" #include "ir/statements/continueStatement.h" #include "ir/statements/tryStatement.h" #include "ir/ts/tsInterfaceDeclaration.h" #include "varbinder/variableFlags.h" #include "compiler/base/lreference.h" #include "compiler/base/catchTable.h" #include "compiler/core/dynamicContext.h" #include "varbinder/ETSBinder.h" #include "varbinder/variable.h" #include "checker/types/type.h" #include "checker/types/typeFlag.h" #include "checker/checker.h" #include "checker/ETSchecker.h" #include "checker/types/ets/etsObjectType.h" #include "checker/types/ets/etsAsyncFuncReturnType.h" #include "parser/program/program.h" #include "checker/types/globalTypesHolder.h" #include "public/public.h" namespace ark::es2panda::compiler { static inline bool IsWidePrimitiveType(checker::Type const *type) { return type->IsLongType() || type->IsDoubleType(); } ETSGen::ETSGen(ArenaAllocator *allocator, RegSpiller *spiller, public_lib::Context *context, std::tuple toCompile) noexcept : CodeGen(allocator, spiller, context, toCompile), containingObjectType_(util::Helpers::GetContainingObjectType(RootNode())) { ETSFunction::Compile(this); } void ETSGen::SetAccumulatorType(const checker::Type *type) { SetVRegType(acc_, type); } const checker::Type *ETSGen::GetAccumulatorType() const { return GetVRegType(acc_); } void ETSGen::CompileAndCheck(const ir::Expression *expr) { // NOTE: vpukhov. bad accumulator type leads to terrible bugs in codegen // make exact types match mandatory expr->Compile(this); auto const *const accType = GetAccumulatorType(); auto const *const exprType = expr->TsType(); if (Checker()->Relation()->IsIdenticalTo(accType, exprType) || exprType->IsETSTypeParameter() || exprType->IsETSPartialTypeParameter() || exprType->IsETSNonNullishType()) { return; } if (accType->IsETSPrimitiveType() && ((accType->TypeFlags() ^ exprType->TypeFlags()) & ~checker::TypeFlag::CONSTANT) == 0) { return; } ASSERT_PRINT(false, std::string("Type mismatch after Expression::Compile: ") + accType->ToString() + " instead of " + exprType->ToString()); } const checker::ETSChecker *ETSGen::Checker() const noexcept { return Context()->checker->AsETSChecker(); } const varbinder::ETSBinder *ETSGen::VarBinder() const noexcept { return Context()->parserProgram->VarBinder()->AsETSBinder(); } const checker::Type *ETSGen::ReturnType() const noexcept { return RootNode()->AsScriptFunction()->Signature()->ReturnType(); } const checker::ETSObjectType *ETSGen::ContainingObjectType() const noexcept { return containingObjectType_; } VReg &ETSGen::Acc() noexcept { return acc_; } VReg ETSGen::Acc() const noexcept { return acc_; } void ETSGen::ApplyConversionAndStoreAccumulator(const ir::AstNode *const node, const VReg vreg, const checker::Type *const targetType) { ApplyConversion(node, targetType); StoreAccumulator(node, vreg); } VReg ETSGen::StoreException(const ir::AstNode *node) { VReg exception = AllocReg(); Ra().Emit(node, exception); SetAccumulatorType(Checker()->GlobalBuiltinExceptionType()); SetVRegType(exception, GetAccumulatorType()); return exception; } void ETSGen::StoreAccumulator(const ir::AstNode *const node, const VReg vreg) { const auto *const accType = GetAccumulatorType(); ASSERT(accType != nullptr); if (accType->IsETSReferenceType()) { Ra().Emit(node, vreg); } else if (IsWidePrimitiveType(accType)) { Ra().Emit(node, vreg); } else { Ra().Emit(node, vreg); } SetVRegType(vreg, accType); } void ETSGen::LoadAccumulator(const ir::AstNode *node, VReg vreg) { const auto *const vregType = GetVRegType(vreg); ASSERT(vregType != nullptr); if (vregType->IsETSReferenceType()) { Ra().Emit(node, vreg); } else if (IsWidePrimitiveType(vregType)) { Ra().Emit(node, vreg); } else { Ra().Emit(node, vreg); } SetAccumulatorType(vregType); } IRNode *ETSGen::AllocMov(const ir::AstNode *const node, const VReg vd, const VReg vs) { const auto *const sourceType = GetVRegType(vs); // CC-OFFNXT(G.FMT.14-CPP) project code style auto *const mov = [this, sourceType, node, vd, vs]() -> IRNode * { if (sourceType->IsETSReferenceType()) { return Allocator()->New(node, vd, vs); } if (IsWidePrimitiveType(sourceType)) { return Allocator()->New(node, vd, vs); } return Allocator()->New(node, vd, vs); }(); SetVRegType(vd, sourceType); return mov; } IRNode *ETSGen::AllocMov(const ir::AstNode *const node, OutVReg vd, const VReg vs) { ASSERT(vd.type != OperandType::ANY && vd.type != OperandType::NONE); switch (vd.type) { case OperandType::REF: return Allocator()->New(node, *vd.reg, vs); case OperandType::B64: return Allocator()->New(node, *vd.reg, vs); default: break; } return Allocator()->New(node, *vd.reg, vs); } checker::Type const *ETSGen::TypeForVar(varbinder::Variable const *var) const noexcept { return var->TsType(); } void ETSGen::MoveVreg(const ir::AstNode *const node, const VReg vd, const VReg vs) { const auto *const sourceType = GetVRegType(vs); if (sourceType->IsETSReferenceType()) { Ra().Emit(node, vd, vs); } else if (IsWidePrimitiveType(sourceType)) { Ra().Emit(node, vd, vs); } else { Ra().Emit(node, vd, vs); } SetVRegType(vd, sourceType); } util::StringView ETSGen::FormDynamicModulePropReference(const varbinder::Variable *var) { ASSERT(VarBinder()->IsDynamicModuleVariable(var) || VarBinder()->IsDynamicNamespaceVariable(var)); auto *data = VarBinder()->DynamicImportDataForVar(var); ASSERT(data != nullptr); auto *import = data->import; return FormDynamicModulePropReference(import); } void ETSGen::LoadAccumulatorDynamicModule(const ir::AstNode *node, const ir::ETSImportDeclaration *import) { ASSERT(import->Language().IsDynamic()); LoadStaticProperty(node, Checker()->GlobalBuiltinDynamicType(import->Language()), FormDynamicModulePropReference(import)); } util::StringView ETSGen::FormDynamicModulePropReference(const ir::ETSImportDeclaration *import) { std::stringstream ss; if (!VarBinder()->Program()->OmitModuleName()) { ss << VarBinder()->Program()->ModuleName() << compiler::Signatures::METHOD_SEPARATOR; } ss << compiler::Signatures::DYNAMIC_MODULE_CLASS << compiler::Signatures::METHOD_SEPARATOR << import->AssemblerName(); return util::UString(ss.str(), Allocator()).View(); } void ETSGen::LoadDynamicModuleVariable(const ir::AstNode *node, varbinder::Variable const *const var) { RegScope rs(this); auto *data = VarBinder()->DynamicImportDataForVar(var); auto *import = data->import; LoadStaticProperty(node, var->TsType(), FormDynamicModulePropReference(var)); auto objReg = AllocReg(); StoreAccumulator(node, objReg); auto *id = data->specifier->AsImportSpecifier()->Imported(); auto lang = import->Language(); LoadPropertyDynamic(node, Checker()->GlobalBuiltinDynamicType(lang), objReg, id->Name()); ApplyConversion(node); } void ETSGen::LoadDynamicNamespaceVariable(const ir::AstNode *node, varbinder::Variable const *const var) { LoadStaticProperty(node, var->TsType(), FormDynamicModulePropReference(var)); } void ETSGen::LoadVar(const ir::Identifier *node, varbinder::Variable const *const var) { if (VarBinder()->IsDynamicModuleVariable(var)) { LoadDynamicModuleVariable(node, var); return; } if (VarBinder()->IsDynamicNamespaceVariable(var)) { LoadDynamicNamespaceVariable(node, var); return; } auto *local = var->AsLocalVariable(); switch (ETSLReference::ResolveReferenceKind(var)) { case ReferenceKind::STATIC_FIELD: { auto fullName = FormClassPropReference(var); LoadStaticProperty(node, var->TsType(), fullName); break; } case ReferenceKind::FIELD: { const auto fullName = FormClassPropReference(GetVRegType(GetThisReg())->AsETSObjectType(), var->Name()); LoadProperty(node, var->TsType(), GetThisReg(), fullName); break; } case ReferenceKind::METHOD: case ReferenceKind::STATIC_METHOD: case ReferenceKind::CLASS: case ReferenceKind::STATIC_CLASS: { SetAccumulatorType(var->TsType()); break; } case ReferenceKind::LOCAL: { LoadAccumulator(node, local->Vreg()); SetAccumulatorType(GetVRegType(local->Vreg())); break; } default: { UNREACHABLE(); } } } void ETSGen::StoreVar(const ir::Identifier *node, const varbinder::ConstScopeFindResult &result) { auto *local = result.variable->AsLocalVariable(); ApplyConversion(node, local->TsType()); switch (ETSLReference::ResolveReferenceKind(result.variable)) { case ReferenceKind::STATIC_FIELD: { auto fullName = FormClassPropReference(result.variable); StoreStaticProperty(node, result.variable->TsType(), fullName); break; } case ReferenceKind::FIELD: { StoreProperty(node, result.variable->TsType(), GetThisReg(), result.name); break; } case ReferenceKind::LOCAL: { StoreAccumulator(node, local->Vreg()); SetVRegType(local->Vreg(), GetAccumulatorType()); break; } default: { UNREACHABLE(); } } } util::StringView ETSGen::FormClassPropReference(const checker::ETSObjectType *classType, const util::StringView &name) { std::stringstream ss; auto *iter = classType; std::string fullName = classType->AssemblerName().Mutf8(); while (iter->EnclosingType() != nullptr) { auto enclosingName = iter->EnclosingType()->Name().Mutf8().append(".").append(fullName); if (iter->EnclosingType()->GetDeclNode()->Type() == ir::AstNodeType::IDENTIFIER) { fullName = enclosingName; } iter = iter->EnclosingType(); } if (fullName != classType->AssemblerName().Mutf8()) { fullName.append(".").append(Signatures::ETS_GLOBAL); } ss << fullName << '.' << name; auto res = ProgElement()->Strings().emplace(ss.str()); return util::StringView(*res.first); } util::StringView ETSGen::FormClassPropReference(varbinder::Variable const *const var) { auto containingObjectType = util::Helpers::GetContainingObjectType(var->Declaration()->Node()); return FormClassPropReference(containingObjectType, var->Name()); } void ETSGen::StoreStaticOwnProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &name) { util::StringView fullName = FormClassPropReference(containingObjectType_, name); StoreStaticProperty(node, propType, fullName); } void ETSGen::StoreStaticProperty(const ir::AstNode *const node, const checker::Type *propType, const util::StringView &fullName) { if (propType->IsETSReferenceType()) { Sa().Emit(node, fullName); } else if (IsWidePrimitiveType(propType)) { Sa().Emit(node, fullName); } else { Sa().Emit(node, fullName); } } void ETSGen::LoadStaticProperty(const ir::AstNode *const node, const checker::Type *propType, const util::StringView &fullName) { if (propType->IsETSReferenceType()) { Sa().Emit(node, fullName); } else if (IsWidePrimitiveType(propType)) { Sa().Emit(node, fullName); } else { Sa().Emit(node, fullName); } SetAccumulatorType(propType); } void ETSGen::StoreProperty(const ir::AstNode *const node, const checker::Type *propType, const VReg objReg, const util::StringView &name) { auto *objType = Checker()->GetApparentType(GetVRegType(objReg))->AsETSObjectType(); const auto fullName = FormClassPropReference(objType, name); if (propType->IsETSReferenceType()) { Ra().Emit(node, objReg, fullName); } else if (IsWidePrimitiveType(propType)) { Ra().Emit(node, objReg, fullName); } else { Ra().Emit(node, objReg, fullName); } } void ETSGen::LoadProperty(const ir::AstNode *const node, const checker::Type *propType, const VReg objReg, const util::StringView &fullName) { if (propType->IsETSReferenceType()) { Ra().Emit(node, objReg, fullName); } else if (IsWidePrimitiveType(propType)) { Ra().Emit(node, objReg, fullName); } else { Ra().Emit(node, objReg, fullName); } SetAccumulatorType(propType); } void ETSGen::StoreUnionProperty([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] const checker::Type *propType, [[maybe_unused]] VReg objReg, [[maybe_unused]] const util::StringView &propName) { #ifdef PANDA_WITH_ETS if (propType->IsETSReferenceType()) { Ra().Emit(node, objReg, propName); } else if (IsWidePrimitiveType(propType)) { Ra().Emit(node, objReg, propName); } else { Ra().Emit(node, objReg, propName); } #else UNREACHABLE(); #endif // PANDA_WITH_ETS } void ETSGen::LoadUnionProperty([[maybe_unused]] const ir::AstNode *const node, [[maybe_unused]] const checker::Type *propType, [[maybe_unused]] const VReg objReg, [[maybe_unused]] const util::StringView &propName) { #ifdef PANDA_WITH_ETS if (propType->IsETSReferenceType()) { Ra().Emit(node, objReg, propName); } else if (IsWidePrimitiveType(propType)) { Ra().Emit(node, objReg, propName); } else { Ra().Emit(node, objReg, propName); } SetAccumulatorType(propType); #else UNREACHABLE(); #endif // PANDA_WITH_ETS } void ETSGen::StorePropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &propName) { auto const lang = GetVRegType(objReg)->AsETSDynamicType()->Language(); std::string_view methodName {}; if (propType->IsETSBooleanType()) { methodName = Signatures::Dynamic::SetPropertyBooleanBuiltin(lang); } else if (propType->IsByteType()) { methodName = Signatures::Dynamic::SetPropertyByteBuiltin(lang); } else if (propType->IsCharType()) { methodName = Signatures::Dynamic::SetPropertyCharBuiltin(lang); } else if (propType->IsShortType()) { methodName = Signatures::Dynamic::SetPropertyShortBuiltin(lang); } else if (propType->IsIntType()) { methodName = Signatures::Dynamic::SetPropertyIntBuiltin(lang); } else if (propType->IsLongType()) { methodName = Signatures::Dynamic::SetPropertyLongBuiltin(lang); } else if (propType->IsFloatType()) { methodName = Signatures::Dynamic::SetPropertyFloatBuiltin(lang); } else if (propType->IsDoubleType()) { methodName = Signatures::Dynamic::SetPropertyDoubleBuiltin(lang); } else if (propType->IsETSStringType()) { methodName = Signatures::Dynamic::SetPropertyStringBuiltin(lang); } else if (propType->IsETSObjectType() || propType->IsETSTypeParameter()) { methodName = Signatures::Dynamic::SetPropertyDynamicBuiltin(lang); // NOTE: vpukhov. add non-dynamic builtin if (!propType->IsETSDynamicType()) { CastToDynamic(node, Checker()->GlobalBuiltinDynamicType(lang)->AsETSDynamicType()); } } else { ASSERT_PRINT(false, "Unsupported property type"); } RegScope rs(this); VReg propValueReg = AllocReg(); VReg propNameReg = AllocReg(); StoreAccumulator(node, propValueReg); // Load property name LoadAccumulatorString(node, propName); StoreAccumulator(node, propNameReg); // Set property by name Ra().Emit(node, methodName, objReg, propNameReg, propValueReg, dummyReg_); SetAccumulatorType(Checker()->GlobalBuiltinJSValueType()); } void ETSGen::LoadPropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg, const util::StringView &propName) { auto const lang = GetVRegType(objReg)->AsETSDynamicType()->Language(); auto *type = propType; std::string_view methodName {}; if (propType->IsETSBooleanType()) { methodName = Signatures::Dynamic::GetPropertyBooleanBuiltin(lang); } else if (propType->IsByteType()) { methodName = Signatures::Dynamic::GetPropertyByteBuiltin(lang); } else if (propType->IsCharType()) { methodName = Signatures::Dynamic::GetPropertyCharBuiltin(lang); } else if (propType->IsShortType()) { methodName = Signatures::Dynamic::GetPropertyShortBuiltin(lang); } else if (propType->IsIntType()) { methodName = Signatures::Dynamic::GetPropertyIntBuiltin(lang); } else if (propType->IsLongType()) { methodName = Signatures::Dynamic::GetPropertyLongBuiltin(lang); } else if (propType->IsFloatType()) { methodName = Signatures::Dynamic::GetPropertyFloatBuiltin(lang); } else if (propType->IsDoubleType()) { methodName = Signatures::Dynamic::GetPropertyDoubleBuiltin(lang); } else if (propType->IsETSStringType()) { methodName = Signatures::Dynamic::GetPropertyStringBuiltin(lang); } else if (propType->IsETSObjectType() || propType->IsETSTypeParameter()) { methodName = Signatures::Dynamic::GetPropertyDynamicBuiltin(lang); type = Checker()->GlobalBuiltinDynamicType(lang); } else { ASSERT_PRINT(false, "Unsupported property type"); } RegScope rs(this); // Load property name LoadAccumulatorString(node, propName); VReg propNameObject = AllocReg(); StoreAccumulator(node, propNameObject); // Get property by name Ra().Emit(node, methodName, objReg, propNameObject); SetAccumulatorType(type); if (propType != type && !propType->IsETSDynamicType()) { CastDynamicToObject(node, propType); } } void ETSGen::StoreElementDynamic(const ir::AstNode *node, VReg objectReg, VReg index) { auto const lang = GetVRegType(objectReg)->AsETSDynamicType()->Language(); std::string_view methodName = Signatures::Dynamic::SetElementDynamicBuiltin(lang); RegScope rs(this); VReg valueReg = AllocReg(); StoreAccumulator(node, valueReg); // Set property by index Ra().Emit(node, methodName, objectReg, index, valueReg, dummyReg_); SetAccumulatorType(Checker()->GlobalVoidType()); } void ETSGen::LoadElementDynamic(const ir::AstNode *node, VReg objectReg) { auto const lang = GetVRegType(objectReg)->AsETSDynamicType()->Language(); std::string_view methodName = Signatures::Dynamic::GetElementDynamicBuiltin(lang); RegScope rs(this); VReg indexReg = AllocReg(); StoreAccumulator(node, indexReg); // Get property by index Ra().Emit(node, methodName, objectReg, indexReg); SetAccumulatorType(Checker()->GlobalBuiltinDynamicType(lang)); } void ETSGen::LoadUndefinedDynamic(const ir::AstNode *node, Language lang) { RegScope rs(this); Ra().Emit(node, Signatures::Dynamic::GetUndefinedBuiltin(lang), dummyReg_, dummyReg_); SetAccumulatorType(Checker()->GlobalBuiltinDynamicType(lang)); } void ETSGen::LoadThis(const ir::AstNode *node) { LoadAccumulator(node, GetThisReg()); } void ETSGen::CreateBigIntObject(const ir::AstNode *node, VReg arg0, std::string_view signature) { Ra().Emit(node, signature, arg0, dummyReg_); } VReg ETSGen::GetThisReg() const { const auto res = Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS); return res.variable->AsLocalVariable()->Vreg(); } const checker::Type *ETSGen::LoadDefaultValue([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] const checker::Type *type) { if (type->IsETSAsyncFuncReturnType()) { LoadDefaultValue(node, type->AsETSAsyncFuncReturnType()->GetPromiseTypeArg()); return type; } if (type->IsETSUnionType()) { if (type->AsETSUnionType()->HasUndefinedType()) { type = Checker()->GetGlobalTypesHolder()->GlobalETSUndefinedType(); } else { type = Checker()->GetGlobalTypesHolder()->GlobalETSObjectType(); } } // NOTE(vpukhov): #19701 void refactoring if (type->IsUndefinedType() || type->IsETSUndefinedType() || type->IsETSVoidType()) { LoadAccumulatorUndefined(node); } else if (type->IsETSObjectType() || type->IsETSArrayType() || type->IsETSTypeParameter() || type->IsETSNullType() || type->IsETSPartialTypeParameter() || type->IsETSNeverType()) { // NOTE: need rework about ETSNeverType #20340 LoadAccumulatorNull(node, type); } else if (type->IsETSBooleanType()) { LoadAccumulatorBoolean(node, type->AsETSBooleanType()->GetValue()); } else { const auto ttctx = TargetTypeContext(this, type); LoadAccumulatorInt(node, 0); } return type; } void ETSGen::EmitReturnVoid(const ir::AstNode *node) { Sa().Emit(node); } void ETSGen::ReturnAcc(const ir::AstNode *node) { const auto *const accType = GetAccumulatorType(); if (accType->IsETSReferenceType()) { Sa().Emit(node); } else if (IsWidePrimitiveType(accType)) { Sa().Emit(node); } else { Sa().Emit(node); } } static bool IsAnyReferenceSupertype(checker::Type const *type) { if (!type->IsETSUnionType()) { return false; } auto const &constituent = type->AsETSUnionType()->ConstituentTypes(); return constituent.size() == 3U && std::all_of(constituent.begin(), constituent.end(), [](checker::Type *t) { return t->IsETSNullType() || t->IsETSUndefinedType() || (t->IsETSObjectType() && t->AsETSObjectType()->IsGlobalETSObjectType()); }); // CC-OFF(G.FMT.02) project code style } void ETSGen::IsInstanceDynamic(const ir::BinaryExpression *const node, const VReg srcReg, [[maybe_unused]] const VReg tgtReg) { ASSERT(node->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF); const checker::Type *lhsType = node->Left()->TsType(); const checker::Type *rhsType = node->Right()->TsType(); ASSERT(rhsType->IsETSDynamicType() || lhsType->IsETSDynamicType()); const RegScope rs(this); if (rhsType->IsETSDynamicType()) { ASSERT(node->Right()->TsType()->AsETSDynamicType()->HasDecl()); if (lhsType->IsETSDynamicType()) { VReg dynTypeReg = MoveAccToReg(node); // Semantics: // let dyn_val: JSValue = ... // dyn_value instanceof DynamicDecl // Bytecode: // call runtime intrinsic_dynamic CallExact(node, Signatures::BUILTIN_JSRUNTIME_INSTANCE_OF_DYNAMIC, srcReg, dynTypeReg); } else if (lhsType == Checker()->GlobalETSObjectType()) { // Semantics: // let obj: Object = ... // obj instanceof DynamicDecl // Bytecode: // if isinstance : // checkcast // return call runtime intrinsic_dynamic // return false Label *ifFalse = AllocLabel(); Language lang = rhsType->AsETSDynamicType()->Language(); VReg dynTypeReg = MoveAccToReg(node); LoadAccumulator(node, srcReg); Sa().Emit(node, Checker()->GlobalBuiltinDynamicType(lang)->AssemblerName()); BranchIfFalse(node, ifFalse); LoadAccumulator(node, srcReg); Sa().Emit(node, Checker()->GlobalBuiltinDynamicType(lang)->AssemblerName()); CallExact(node, Signatures::BUILTIN_JSRUNTIME_INSTANCE_OF_DYNAMIC, srcReg, dynTypeReg); SetLabel(node, ifFalse); } else { // Semantics: // let obj: EtsType = ... // obj instanceof DynamicDecl // Bytecode: // False Sa().Emit(node, 0); } } else { if (lhsType->IsETSDynamicType()) { if (rhsType == Checker()->GlobalETSObjectType()) { // Semantics: // let dyn_val: JSValue = ... // dyn_val instanceof Object // Bytecode: // True Sa().Emit(node, 1); } else { // Semantics: // let dyn_val: JSValue = ... // dyn_val instanceof EtsType // Bytecode: // lda.type + call runtime instrinsic_static Sa().Emit(node, rhsType->AsETSObjectType()->AssemblerName()); VReg typeReg = MoveAccToReg(node); CallExact(node, Signatures::BUILTIN_JSRUNTIME_INSTANCE_OF_STATIC, srcReg, typeReg); } } else { UNREACHABLE(); } } SetAccumulatorType(Checker()->GlobalETSBooleanType()); } void ETSGen::TestIsInstanceConstant(const ir::AstNode *node, Label *ifTrue, VReg srcReg, checker::Type const *target) { if (!target->IsConstantType()) { return; } RegScope rs(this); VReg rhs = AllocReg(); auto ifNotEquals = AllocLabel(); LoadAccumulator(node, srcReg); LoadConstantObject(node->AsExpression(), target); StoreAccumulator(node, rhs); EmitEtsEquals(node, srcReg, rhs); BranchIfFalse(node, ifNotEquals); BranchIfTrue(node, ifTrue); SetLabel(node, ifNotEquals); SetAccumulatorType(nullptr); } // Implemented on top of the runtime type system, do not relax checks, do not introduce new types void ETSGen::TestIsInstanceConstituent(const ir::AstNode *const node, std::tuple