From 220c75e2679346ed59fb618f50d414f0e9da1be4 Mon Sep 17 00:00:00 2001 From: Laszlo Lango Date: Thu, 5 Jun 2025 13:30:22 +0200 Subject: [PATCH] Implement FunctionN and LambdaN Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICH6GL Fixes #25891 internal issue. Change-Id: I90c054ac196c1c3524bbdc348e540d07077c647b Signed-off-by: Laszlo Lango --- ets2panda/checker/ETSchecker.cpp | 11 +- ets2panda/checker/ETSchecker.h | 1 + .../checker/types/ets/etsFunctionType.cpp | 17 +- ets2panda/checker/types/ets/etsObjectType.cpp | 8 +- .../types/ets/etsObjectTypeConstants.h | 6 - ets2panda/checker/types/globalTypesHolder.cpp | 2 +- .../compiler/lowering/ets/lambdaLowering.cpp | 260 ++++++++++++++---- .../lambda_n/lambda_n_too_many_arg_neg_1.ets | 43 +++ .../lambda_n/lambda_n_too_many_arg_neg_2.ets | 43 +++ .../lambda_n/lambda_n_too_many_arg_neg_3.ets | 42 +++ .../lambda_n/lambda_n_too_many_arg_neg_4.ets | 42 +++ .../lambda_n/lambda_n_too_many_arg_neg_5.ets | 42 +++ .../runtime/ets/lambda_n/lambda_n_basic.ets | 92 +++++++ .../ets/lambda_n/lambda_n_optional.ets | 47 ++++ .../runtime/ets/lambda_n/lambda_n_type.ets | 25 ++ .../ets/lambda_n/lambda_n_with_default.ets | 46 ++++ .../ets/lambda_n/lambda_n_with_rest.ets | 100 +++++++ .../ets-runtime/ets-runtime-ignored.txt | 2 +- 18 files changed, 759 insertions(+), 70 deletions(-) create mode 100644 ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_1.ets create mode 100644 ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_2.ets create mode 100644 ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_3.ets create mode 100644 ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_4.ets create mode 100644 ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_5.ets create mode 100644 ets2panda/test/runtime/ets/lambda_n/lambda_n_basic.ets create mode 100644 ets2panda/test/runtime/ets/lambda_n/lambda_n_optional.ets create mode 100644 ets2panda/test/runtime/ets/lambda_n/lambda_n_type.ets create mode 100644 ets2panda/test/runtime/ets/lambda_n/lambda_n_with_default.ets create mode 100644 ets2panda/test/runtime/ets/lambda_n/lambda_n_with_rest.ets diff --git a/ets2panda/checker/ETSchecker.cpp b/ets2panda/checker/ETSchecker.cpp index abf418beae..dc071e5d0f 100644 --- a/ets2panda/checker/ETSchecker.cpp +++ b/ets2panda/checker/ETSchecker.cpp @@ -258,7 +258,7 @@ static void IntializeFunctionInterfaces(GlobalTypesHolder *typeHolder) return typeHolder->GlobalFunctionBuiltinType(arity, hasRest)->AsETSObjectType(); }; - for (size_t arity = 0; arity < typeHolder->VariadicFunctionTypeThreshold(); arity++) { + for (size_t arity = 0; arity <= typeHolder->VariadicFunctionTypeThreshold(); arity++) { getItf(arity, false)->AddObjectFlag(ETSObjectFlags::FUNCTIONAL); getItf(arity, true)->AddObjectFlag(ETSObjectFlags::FUNCTIONAL); } @@ -798,4 +798,13 @@ bool ETSChecker::TypeHasDefaultValue(Type *tp) const Relation()->IsSupertypeOf(GlobalETSUndefinedType(), tp); } +/* Invoke method name in functional interfaces */ +std::string ETSChecker::FunctionalInterfaceInvokeName(size_t arity, bool hasRest) +{ + if (arity < GlobalBuiltinFunctionTypeVariadicThreshold()) { + return (hasRest ? "invokeR" : "invoke") + std::to_string(arity); + } + return "invokeN"; +} + } // namespace ark::es2panda::checker diff --git a/ets2panda/checker/ETSchecker.h b/ets2panda/checker/ETSchecker.h index 62c5776c6b..4f2d18c447 100644 --- a/ets2panda/checker/ETSchecker.h +++ b/ets2panda/checker/ETSchecker.h @@ -580,6 +580,7 @@ public: void ReplaceScope(ir::AstNode *root, ir::AstNode *oldNode, varbinder::Scope *newScope); // Helpers + std::string FunctionalInterfaceInvokeName(size_t arity, bool hasRest); static std::string GetAsyncImplName(const util::StringView &name); static std::string GetAsyncImplName(ir::MethodDefinition *asyncMethod); static bool IsAsyncImplMethod(ir::MethodDefinition const *method); diff --git a/ets2panda/checker/types/ets/etsFunctionType.cpp b/ets2panda/checker/types/ets/etsFunctionType.cpp index 3c356758c8..f7fa98e476 100644 --- a/ets2panda/checker/types/ets/etsFunctionType.cpp +++ b/ets2panda/checker/types/ets/etsFunctionType.cpp @@ -53,9 +53,8 @@ ETSFunctionType::ETSFunctionType(ETSChecker *checker, Signature *signature) } // #22951: proper this type implementation -static void HackThisParameterInExtensionFunctionInvoke(ETSObjectType *interface, size_t arity) +static void HackThisParameterInExtensionFunctionInvoke(ETSObjectType *interface, std::string &invokeName) { - auto invokeName = FunctionalInterfaceInvokeName(arity, false); auto &callSigsOfInvoke0 = interface->AsETSObjectType() ->GetOwnProperty(util::StringView(invokeName)) ->TsType() @@ -74,6 +73,11 @@ static ETSObjectType *FunctionTypeToFunctionalInterfaceType(ETSChecker *checker, if (signature->RestVar() != nullptr) { auto nPosParams = signature->Params().size(); auto *functionN = checker->GlobalBuiltinFunctionType(nPosParams, true)->AsETSObjectType(); + + if (nPosParams >= checker->GetGlobalTypesHolder()->VariadicFunctionTypeThreshold()) { + return functionN; + } + auto substitution = Substitution {}; for (size_t i = 0; i < nPosParams; i++) { substitution.emplace(functionN->TypeArguments()[i]->AsETSTypeParameter(), @@ -93,14 +97,12 @@ static ETSObjectType *FunctionTypeToFunctionalInterfaceType(ETSChecker *checker, ES2PANDA_ASSERT(arity >= signature->MinArgCount() && arity <= signature->ArgCount()); - // Note: FunctionN is not supported yet + auto *funcIface = checker->GlobalBuiltinFunctionType(arity, false)->AsETSObjectType(); if (arity >= checker->GetGlobalTypesHolder()->VariadicFunctionTypeThreshold()) { - return nullptr; + return funcIface; } - auto *funcIface = checker->GlobalBuiltinFunctionType(arity, false)->AsETSObjectType(); auto substitution = Substitution {}; - for (size_t i = 0; i < arity; i++) { substitution.emplace(funcIface->TypeArguments()[i]->AsETSTypeParameter(), checker->MaybeBoxType(signature->Params()[i]->TsType())); @@ -110,7 +112,8 @@ static ETSObjectType *FunctionTypeToFunctionalInterfaceType(ETSChecker *checker, auto result = funcIface->Substitute(checker->Relation(), &substitution, true, isExtensionHack); if (signature->HasSignatureFlag(SignatureFlags::THIS_RETURN_TYPE)) { - HackThisParameterInExtensionFunctionInvoke(result, arity); + auto invokeName = checker->FunctionalInterfaceInvokeName(arity, false); + HackThisParameterInExtensionFunctionInvoke(result, invokeName); } result->AddObjectFlag(checker::ETSObjectFlags::FUNCTIONAL); diff --git a/ets2panda/checker/types/ets/etsObjectType.cpp b/ets2panda/checker/types/ets/etsObjectType.cpp index e7d2fb4229..1b6af74633 100644 --- a/ets2panda/checker/types/ets/etsObjectType.cpp +++ b/ets2panda/checker/types/ets/etsObjectType.cpp @@ -708,12 +708,12 @@ void ETSObjectType::AssignmentTarget(TypeRelation *const relation, Type *source) ETSFunctionType *ETSObjectType::GetFunctionalInterfaceInvokeType() const { ES2PANDA_ASSERT(HasObjectFlag(ETSObjectFlags::FUNCTIONAL)); + auto checker = GetRelation()->GetChecker()->AsETSChecker(); // NOTE(vpukhov): this is still better than to retain any "functional" state in ETSObjectType - auto [foundArity, hasRest] = [this]() { - auto checker = GetRelation()->GetChecker()->AsETSChecker(); + auto [foundArity, hasRest] = [this, checker]() { auto baseType = GetConstOriginalBaseType(); - for (size_t arity = 0; arity < checker->GetGlobalTypesHolder()->VariadicFunctionTypeThreshold(); ++arity) { + for (size_t arity = 0; arity <= checker->GlobalBuiltinFunctionTypeVariadicThreshold(); ++arity) { if (auto itf = checker->GlobalBuiltinFunctionType(arity, false); itf == baseType) { return std::make_pair(arity, false); } @@ -724,7 +724,7 @@ ETSFunctionType *ETSObjectType::GetFunctionalInterfaceInvokeType() const ES2PANDA_UNREACHABLE(); }(); - std::string invokeName = FunctionalInterfaceInvokeName(foundArity, hasRest); + std::string invokeName = checker->FunctionalInterfaceInvokeName(foundArity, hasRest); auto *invoke = GetOwnProperty(util::StringView(invokeName)); ES2PANDA_ASSERT(invoke != nullptr && invoke->TsType() != nullptr && invoke->TsType()->IsETSFunctionType()); return invoke->TsType()->AsETSFunctionType(); diff --git a/ets2panda/checker/types/ets/etsObjectTypeConstants.h b/ets2panda/checker/types/ets/etsObjectTypeConstants.h index a13f0d4685..007a89a06d 100644 --- a/ets2panda/checker/types/ets/etsObjectTypeConstants.h +++ b/ets2panda/checker/types/ets/etsObjectTypeConstants.h @@ -120,12 +120,6 @@ enum class PropertyType { COUNT, }; -/* Invoke method name in functional interfaces */ -inline std::string FunctionalInterfaceInvokeName(size_t arity, bool hasRest) -{ - return (hasRest ? "invokeR" : "invoke") + std::to_string(arity); -} - } // namespace ark::es2panda::checker namespace enumbitops { diff --git a/ets2panda/checker/types/globalTypesHolder.cpp b/ets2panda/checker/types/globalTypesHolder.cpp index 1a3dd61220..e6f337aa26 100644 --- a/ets2panda/checker/types/globalTypesHolder.cpp +++ b/ets2panda/checker/types/globalTypesHolder.cpp @@ -85,7 +85,7 @@ void GlobalTypesHolder::AddFunctionTypes(ArenaAllocator *allocator) addTypes("LambdaR", GlobalTypeId::ETS_LAMBDAR0_CLASS, GlobalTypeId::ETS_LAMBDAR16_CLASS); builtinNameMappings_.emplace("FunctionN", GlobalTypeId::ETS_FUNCTIONN_CLASS); - builtinNameMappings_.emplace("LambdaN", GlobalTypeId::ETS_FUNCTIONN_CLASS); + builtinNameMappings_.emplace("LambdaN", GlobalTypeId::ETS_LAMBDAN_CLASS); } void GlobalTypesHolder::AddTupleTypes(ArenaAllocator *allocator) diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index 17cfcc6a9a..046671e24e 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -512,7 +512,7 @@ static void CreateLambdaClassConstructor(public_lib::Context *ctx, ir::ClassDefi // NOTE(vpukhov): requires the optimization based on the array type // CC-OFFNXT(G.FUN.01, huge_method) solid logic static ArenaVector CreateRestArgumentsArrayReallocation( - public_lib::Context *ctx, LambdaClassInvokeInfo const *lciInfo) + public_lib::Context *ctx, LambdaClassInvokeInfo const *lciInfo, size_t startIdx) { if (!lciInfo->lambdaSignature->HasRestParameter() || lciInfo->lambdaSignature->RestVar()->TsType()->IsETSTupleType()) { @@ -531,51 +531,46 @@ static ArenaVector CreateRestArgumentsArrayReall ? elementType : checker->CreateETSUnionType({elementType, checker->GlobalETSUndefinedType()}); std::stringstream statements; - auto restParameterIndex = GenName(allocator).View(); - auto spreadArrIterator = GenName(allocator).View(); + auto restParameterLen = GenName(allocator).View(); ir::Statement *args = nullptr; if (restParameterSubstituteType->IsETSArrayType()) { auto tmpArray = GenName(allocator).View(); - statements << "let @@I1: int = 0;"; - if (elementType->IsETSReferenceType()) { - // NOTE(vpukhov): this is a clear null-safety violation that should be rewitten with a runtime intrinsic - statements << "let @@I2: FixedArray<@@T3> = new (@@T4)[@@I5.length];"; - } else { - statements << "let @@I2: FixedArray<@@T3> = new (@@T4)[@@I5.length];"; - } - statements << "let @@I6 = @@I7 as FixedArray<@@T8>;" + // NOTE(vpukhov): this is a clear null-safety violation (when elementType->IsETSReferenceType()) that should be + // rewitten with a runtime intrinsic + // CC-OFFNXT(G.FMT.06) false positive + statements << "let @@I1: int = @@I2.length - " << startIdx + << ";" + // CC-OFFNXT(G.FMT.06) false positive + << "let @@I3: FixedArray<@@T4> = new (@@T5)[@@I6];" // CC-OFFNXT(G.FMT.06) false positive - << "for (let @@I9: @@T10 of @@I11){" + << "let @@I7 = @@I8 as FixedArray<@@T9>;" // CC-OFFNXT(G.FMT.06) false positive - << " @@I12[@@I13] = @@I14 as @@T15;" - // CC-OFFNXT(G.FMT.06, G.FMT.06-CPP) false positive - << " @@I16 = @@I17 + 1;" + << "for (let i: int = 0; i < @@I10; i = i + 1) {" + // CC-OFFNXT(G.FMT.06) false positive + << " @@I11[i] = @@I12[i + " << startIdx << "] as @@T13;" << "}"; args = parser->CreateFormattedStatement( - statements.str(), restParameterIndex, tmpArray, elementTypeWithDefault, elementTypeWithDefault, - lciInfo->restParameterIdentifier, lciInfo->restArgumentIdentifier, tmpArray, elementType, spreadArrIterator, - checker->GlobalETSAnyType(), lciInfo->restParameterIdentifier, lciInfo->restArgumentIdentifier, - restParameterIndex, spreadArrIterator, elementType, restParameterIndex, restParameterIndex); + statements.str(), restParameterLen, lciInfo->restParameterIdentifier, tmpArray, elementTypeWithDefault, + elementTypeWithDefault, restParameterLen, lciInfo->restArgumentIdentifier, tmpArray, elementType, + restParameterLen, lciInfo->restArgumentIdentifier, lciInfo->restParameterIdentifier, elementType); } else { ES2PANDA_ASSERT(restParameterSubstituteType->IsETSResizableArrayType()); auto *typeNode = allocator->New( checker->GetElementTypeOfArray(lciInfo->lambdaSignature->RestVar()->TsType()), allocator); - statements << "let @@I1: int = 0;" - // CC-OFFNXT(G.FMT.06) false positive - << "let @@I2 = new Array<@@T3>(@@I4.length);" + // CC-OFFNXT(G.FMT.06) false positive + statements << "let @@I1: int = @@I2.length - " << startIdx + << ";" // CC-OFFNXT(G.FMT.06) false positive - << "for (let @@I5:@@T6 of @@I7){" + << "let @@I3 = new Array<@@T4>(@@I5);" // CC-OFFNXT(G.FMT.06) false positive - << " @@I8.$_set(@@I9, @@I10 as @@T11);" - // CC-OFFNXT(G.FMT.06) false positive - << " @@I12 = @@I13 + 1;" + << "for (let i: int = 0; i < @@I6; i = i + 1) {" // CC-OFFNXT(G.FMT.06) false positive + << " @@I7.$_set(i, @@I8[i + " << startIdx << "] as @@T9);" << "}"; - args = parser->CreateFormattedStatement( - statements.str(), restParameterIndex, lciInfo->restArgumentIdentifier, typeNode, - lciInfo->restParameterIdentifier, spreadArrIterator, checker->GlobalETSAnyType(), - lciInfo->restParameterIdentifier, lciInfo->restArgumentIdentifier, restParameterIndex, spreadArrIterator, - checker->MaybeBoxType(elementType), restParameterIndex, restParameterIndex); + args = parser->CreateFormattedStatement(statements.str(), restParameterLen, lciInfo->restParameterIdentifier, + lciInfo->restArgumentIdentifier, typeNode, restParameterLen, + restParameterLen, lciInfo->restArgumentIdentifier, + lciInfo->restParameterIdentifier, typeNode); } return ArenaVector(args->AsBlockStatement()->Statements()); @@ -590,9 +585,11 @@ static void CreateInvokeMethodRestParameter(public_lib::Context *ctx, LambdaClas auto *restIdent = Gensym(allocator); lciInfo->restParameterIdentifier = restIdent->Name(); auto *spread = allocator->New(ir::AstNodeType::REST_ELEMENT, allocator, restIdent); - auto *arr = lciInfo->lambdaSignature->RestVar()->TsType()->IsETSTupleType() - ? lciInfo->lambdaSignature->RestVar()->TsType() + auto *restVar = lciInfo->lambdaSignature->RestVar(); + auto *arr = (restVar != nullptr && restVar->TsType()->IsETSTupleType()) + ? restVar->TsType() : checker->CreateETSArrayType(checker->GlobalETSAnyType()); + auto *typeAnnotation = allocator->New(arr, allocator); spread->SetTsTypeAnnotation(typeAnnotation); @@ -651,6 +648,56 @@ static ArenaVector CreateCallArgumentsForLambdaClassInvoke(pub return callArguments; } +static ArenaVector CreateCallArgumentsForLambdaClassInvokeN(public_lib::Context *ctx, + LambdaInfo const *info, + LambdaClassInvokeInfo const *lciInfo, + ArenaVector argNames) +{ + auto *allocator = ctx->allocator; + auto *parser = ctx->parser->AsETSParser(); + auto *checker = ctx->GetChecker()->AsETSChecker(); + + auto callArguments = ArenaVector(allocator->Adapter()); + for (auto *captured : *info->capturedVars) { + auto *arg = parser->CreateFormattedExpression("this.@@I1", AvoidMandatoryThis(captured->Name())); + callArguments.push_back(arg); + } + + ES2PANDA_ASSERT(argNames.size() == lciInfo->lambdaSignature->ArgCount() - lciInfo->lambdaSignature->MinArgCount()); + + for (size_t idx = 0; idx < lciInfo->lambdaSignature->MinArgCount(); ++idx) { + auto lambdaParam = lciInfo->lambdaSignature->Params().at(idx); + auto argName = lciInfo->restParameterIdentifier; + auto *type = lambdaParam->TsType()->Substitute(checker->Relation(), lciInfo->substitution); + auto arg = parser->CreateFormattedExpression("@@I1[" + std::to_string(idx) + "] as @@T2 as @@T3", argName, + checker->MaybeBoxType(type), type); + callArguments.push_back(arg); + } + for (size_t nameIdx = 0, idx = lciInfo->lambdaSignature->MinArgCount(); idx < lciInfo->lambdaSignature->ArgCount(); + ++idx, ++nameIdx) { + auto arg = parser->CreateFormattedExpression("@@I1", argNames[nameIdx]); + callArguments.push_back(arg); + } + + if (!lciInfo->lambdaSignature->HasRestParameter()) { + return callArguments; + } + + auto restType = lciInfo->lambdaSignature->RestVar()->TsType(); + auto *restIdent = allocator->New( + restType->IsETSTupleType() ? lciInfo->restParameterIdentifier : lciInfo->restArgumentIdentifier, allocator); + if (restType->IsETSArrayType() || restType->IsETSTupleType()) { + auto *spread = allocator->New(ir::AstNodeType::SPREAD_ELEMENT, allocator, restIdent); + restIdent->SetParent(spread); + callArguments.push_back(spread); + } else { + ES2PANDA_ASSERT(restType->IsETSResizableArrayType()); + restIdent->AddAstNodeFlags(ir::AstNodeFlags::RESIZABLE_REST); + callArguments.push_back(restIdent); + } + return callArguments; +} + static ir::CallExpression *CreateCallForLambdaClassInvoke(public_lib::Context *ctx, LambdaInfo const *info, LambdaClassInvokeInfo const *lciInfo, bool wrapToObject) { @@ -686,6 +733,42 @@ static ir::CallExpression *CreateCallForLambdaClassInvoke(public_lib::Context *c return call; } +static ir::CallExpression *CreateCallForLambdaClassInvokeN(public_lib::Context *ctx, LambdaInfo const *info, + LambdaClassInvokeInfo const *lciInfo, + ArenaVector argNames) +{ + auto *allocator = ctx->allocator; + auto *parser = ctx->parser->AsETSParser(); + + auto callArguments = CreateCallArgumentsForLambdaClassInvokeN(ctx, info, lciInfo, std::move(argNames)); + ir::Expression *calleeReceiver; + if (info->callReceiver != nullptr) { + calleeReceiver = parser->CreateFormattedExpression("this.@@I1", "$this"); + } else { + calleeReceiver = lciInfo->callee->Parent()->AsClassDefinition()->Ident()->Clone(allocator, nullptr); + } + + auto *calleeMemberExpr = util::NodeAllocator::ForceSetParent( + allocator, calleeReceiver, lciInfo->callee->Key()->Clone(allocator, nullptr)->AsExpression(), + ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); + auto *call = parser->CreateFormattedExpression("@@E1(@@[E2)", calleeMemberExpr, std::move(callArguments)) + ->AsCallExpression(); + + if (lciInfo->classDefinition->TypeParams() != nullptr) { + auto typeArgs = ArenaVector(allocator->Adapter()); + for (auto *tp : lciInfo->classDefinition->TypeParams()->Params()) { + typeArgs.push_back( + allocator->New(tp->Name()->AsIdentifier()->Variable()->TsType(), allocator)); + } + auto *typeArg = + util::NodeAllocator::ForceSetParent(allocator, std::move(typeArgs)); + call->SetTypeParams(typeArg); + typeArg->SetParent(call); + } + + return call; +} + static ir::BlockStatement *CreateLambdaClassInvokeBody(public_lib::Context *ctx, LambdaInfo const *info, LambdaClassInvokeInfo const *lciInfo, bool wrapToObject) { @@ -695,7 +778,7 @@ static ir::BlockStatement *CreateLambdaClassInvokeBody(public_lib::Context *ctx, auto *anyType = checker->GlobalETSAnyType(); auto *call = CreateCallForLambdaClassInvoke(ctx, info, lciInfo, wrapToObject); - auto bodyStmts = CreateRestArgumentsArrayReallocation(ctx, lciInfo); + auto bodyStmts = CreateRestArgumentsArrayReallocation(ctx, lciInfo, 0); if (lciInfo->lambdaSignature->ReturnType() == checker->GlobalVoidType()) { auto *callStmt = util::NodeAllocator::ForceSetParent(allocator, call); @@ -764,16 +847,87 @@ static void CreateLambdaClassInvokeMethod(public_lib::Context *ctx, LambdaInfo c invokeMethod->SetParent(lciInfo->classDefinition); } +static ir::BlockStatement *CreateLambdaClassInvokeNBody(public_lib::Context *ctx, LambdaInfo const *info, + LambdaClassInvokeInfo const *lciInfo) +{ + auto *allocator = ctx->allocator; + auto *checker = ctx->GetChecker()->AsETSChecker(); + auto *parser = ctx->parser->AsETSParser(); + + auto bodyStmts = CreateRestArgumentsArrayReallocation(ctx, lciInfo, lciInfo->lambdaSignature->MinArgCount()); + auto tempVarNames = ArenaVector(allocator->Adapter()); + + for (size_t idx = lciInfo->lambdaSignature->MinArgCount(); idx < lciInfo->arity; ++idx) { + auto lambdaParam = lciInfo->lambdaSignature->Params().at(idx); + auto argName = lciInfo->restParameterIdentifier; + auto *type = lambdaParam->TsType()->Substitute(checker->Relation(), lciInfo->substitution); + std::stringstream stream; + stream << "let @@I1 = @@I2.length > " << idx << " ? @@I3[" << idx << "] as @@T4 as @@T5 : undefined;"; + tempVarNames.push_back(GenName(allocator)); + auto stmt = parser->CreateFormattedStatement(stream.str(), tempVarNames.back(), argName, argName, + checker->MaybeBoxType(type), type); + bodyStmts.push_back(stmt); + } + + auto *call = CreateCallForLambdaClassInvokeN(ctx, info, lciInfo, tempVarNames); + + if (lciInfo->lambdaSignature->ReturnType() == checker->GlobalVoidType()) { + auto *callStmt = util::NodeAllocator::ForceSetParent(allocator, call); + bodyStmts.push_back(callStmt); + auto *returnStmt = + util::NodeAllocator::ForceSetParent(allocator, allocator->New()); + bodyStmts.push_back(returnStmt); + } else { + auto *returnStmt = util::NodeAllocator::ForceSetParent(allocator, call); + bodyStmts.push_back(returnStmt); + } + + return util::NodeAllocator::ForceSetParent(allocator, allocator, std::move(bodyStmts)); +} + +static void CreateLambdaClassInvokeN(public_lib::Context *ctx, LambdaInfo const *info, LambdaClassInvokeInfo *lciInfo) +{ + auto *allocator = ctx->allocator; + auto *checker = ctx->GetChecker()->AsETSChecker(); + auto *anyType = checker->GlobalETSAnyType(); + + auto params = ArenaVector(allocator->Adapter()); + CreateInvokeMethodRestParameter(ctx, lciInfo, ¶ms); + + if (lciInfo->lambdaSignature->HasRestParameter()) { + lciInfo->restArgumentIdentifier = GenName(allocator).View(); + } + + auto *returnType = allocator->New(anyType, allocator); + bool hasReceiver = lciInfo->lambdaSignature->HasSignatureFlag(checker::SignatureFlags::EXTENSION_FUNCTION); + ir::ScriptFunctionFlags functionFlag = ir::ScriptFunctionFlags::METHOD; + auto *func = util::NodeAllocator::ForceSetParent( + allocator, allocator, + ir::ScriptFunction::ScriptFunctionData { + CreateLambdaClassInvokeNBody(ctx, info, lciInfo), + ir::FunctionSignature(nullptr, std::move(params), returnType, hasReceiver), functionFlag}); + + auto *invokeId = allocator->New("invokeN", allocator); + func->SetIdent(invokeId); + + auto *funcExpr = util::NodeAllocator::ForceSetParent(allocator, func); + + auto *invokeIdClone = invokeId->Clone(allocator, nullptr); + auto *invokeMethod = util::NodeAllocator::ForceSetParent( + allocator, ir::MethodDefinitionKind::METHOD, invokeIdClone, funcExpr, ir::ModifierFlags::NONE, allocator, + false); + ES2PANDA_ASSERT(!invokeMethod->IsStatic()); + + lciInfo->classDefinition->EmplaceBody(invokeMethod); + invokeMethod->SetParent(lciInfo->classDefinition); +} + static checker::ETSObjectType *FunctionTypeToLambdaProviderType(checker::ETSChecker *checker, checker::Signature *signature) { if (signature->RestVar() != nullptr) { return checker->GlobalBuiltinLambdaType(signature->ArgCount(), true)->AsETSObjectType(); } - // Note: FunctionN is not supported yet - if (signature->ArgCount() >= checker->GlobalBuiltinFunctionTypeVariadicThreshold()) { - return nullptr; - } return checker->GlobalBuiltinLambdaType(signature->ArgCount(), false)->AsETSObjectType(); } @@ -783,9 +937,10 @@ static checker::ETSObjectType *FunctionTypeToLambdaProviderType(checker::ETSChec static void CorrectTheTrueThisForExtensionLambda(public_lib::Context *ctx, ir::ClassDeclaration *lambdaClass, size_t arity, bool hasRestParam) { + auto *checker = ctx->GetChecker()->AsETSChecker(); auto *classScope = lambdaClass->Definition()->Scope(); ArenaVector invokeFuncsOfLambda(ctx->Allocator()->Adapter()); - auto invokeName = checker::FunctionalInterfaceInvokeName(arity, hasRestParam); + auto invokeName = checker->FunctionalInterfaceInvokeName(arity, hasRestParam); invokeFuncsOfLambda.emplace_back( classScope->FindLocal(compiler::Signatures::LAMBDA_OBJECT_INVOKE, varbinder::ResolveBindingOptions::METHODS)); invokeFuncsOfLambda.emplace_back( @@ -898,17 +1053,24 @@ static ir::ClassDeclaration *CreateLambdaClass(public_lib::Context *ctx, checker lciInfo.substitution = &substitution; lciInfo.lambdaSignature = signature; - for (size_t arity = signature->MinArgCount(); arity <= signature->ArgCount(); ++arity) { - lciInfo.arity = arity; - auto invokeMethodName = - util::UString {checker::FunctionalInterfaceInvokeName(arity, signature->HasRestParameter()), ctx->allocator} - .View(); - CreateLambdaClassInvokeMethod(ctx, info, &lciInfo, invokeMethodName, true); - // NOTE(vpukhov): for optional methods, the required invokeRk k={min, max-1} is not emitted + if (signature->ArgCount() < checker->GlobalBuiltinFunctionTypeVariadicThreshold()) { + for (size_t arity = signature->MinArgCount(); arity <= signature->ArgCount(); ++arity) { + lciInfo.arity = arity; + auto invokeMethodName = + util::UString {checker->FunctionalInterfaceInvokeName(arity, signature->HasRestParameter()), + ctx->allocator} + .View(); + CreateLambdaClassInvokeMethod(ctx, info, &lciInfo, invokeMethodName, true); + // NOTE(vpukhov): for optional methods, the required invokeRk k={min, max-1} is not emitted + } + } else { + lciInfo.arity = signature->ArgCount(); + CreateLambdaClassInvokeN(ctx, info, &lciInfo); } + if (signature->HasRestParameter() && signature->RestVar()->TsType()->IsETSTupleType()) { auto invokeMethodName = - util::UString {checker::FunctionalInterfaceInvokeName(lciInfo.arity + 1, signature->HasRestParameter()), + util::UString {checker->FunctionalInterfaceInvokeName(lciInfo.arity + 1, signature->HasRestParameter()), ctx->allocator} .View(); CreateLambdaClassInvokeMethod(ctx, info, &lciInfo, invokeMethodName, true); @@ -1135,8 +1297,6 @@ static ir::AstNode *ConvertFunctionReference(public_lib::Context *ctx, ir::Expre ES2PANDA_ASSERT(funcRef->TsType()->IsETSArrowType()); auto *lambdaClass = CreateLambdaClass(ctx, funcRef->TsType()->AsETSFunctionType(), method, &info); auto *constructorCall = CreateConstructorCall(ctx, funcRef, lambdaClass, &info); - constructorCall->TsType()->AsETSObjectType()->AddObjectFlag(checker::ETSObjectFlags::FUNCTIONAL_REFERENCE); - return constructorCall; } @@ -1189,7 +1349,7 @@ static ir::AstNode *InsertInvokeCall(public_lib::Context *ctx, ir::CallExpressio (oldType->IsETSFunctionType() && oldType->AsETSFunctionType()->ArrowSignature()->HasRestParameter()) || call->Signature()->HasRestParameter(); util::StringView invokeMethodName = - util::UString {checker::FunctionalInterfaceInvokeName(arity, hasRestParam), allocator}.View(); + util::UString {checker->FunctionalInterfaceInvokeName(arity, hasRestParam), allocator}.View(); auto *prop = ifaceType->GetProperty(invokeMethodName, checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD | checker::PropertySearchFlags::SEARCH_IN_INTERFACES); diff --git a/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_1.ets b/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_1.ets new file mode 100644 index 0000000000..e61d3b64e1 --- /dev/null +++ b/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_1.ets @@ -0,0 +1,43 @@ +/* + * Copyright (c) 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. + */ + +let fn = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17: String, + str18: String +): void => { }; + + +function main(): void { + fn("hello", "world", "!", "!", "!", "!", "!", "!", "!"); +} + +/* @@? 39:4 Error TypeError: Expected 18 arguments, got 9. */ +/* @@? 39:4 Error TypeError: No matching call signature for ("hello", "world", "!", "!", "!", "!", "!", "!", "!") */ diff --git a/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_2.ets b/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_2.ets new file mode 100644 index 0000000000..74e21ab03d --- /dev/null +++ b/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_2.ets @@ -0,0 +1,43 @@ +/* + * Copyright (c) 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. + */ + +let fn = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17: String, + str18: String +): void => { }; + + +function main(): void { + fn("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); +} + +/* @@? 39:4 Error TypeError: Expected 18 arguments, got 23. */ +/* @@? 39:4 Error TypeError: No matching call signature for ("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!") */ diff --git a/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_3.ets b/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_3.ets new file mode 100644 index 0000000000..20dbdd8818 --- /dev/null +++ b/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_3.ets @@ -0,0 +1,42 @@ +/* + * Copyright (c) 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. + */ + +let fn = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17?: String +): void => { }; + + +function main(): void { + fn("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); +} + +/* @@? 38:4 Error TypeError: Expected 16 arguments, got 18. */ +/* @@? 38:4 Error TypeError: No matching call signature for ("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!") */ diff --git a/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_4.ets b/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_4.ets new file mode 100644 index 0000000000..57d60ac879 --- /dev/null +++ b/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_4.ets @@ -0,0 +1,42 @@ +/* + * Copyright (c) 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. + */ + +let fn = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17?: String +): void => { }; + + +function main(): void { + fn("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); +} + +/* @@? 38:4 Error TypeError: Expected 16 arguments, got 15. */ +/* @@? 38:4 Error TypeError: No matching call signature for ("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!") */ diff --git a/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_5.ets b/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_5.ets new file mode 100644 index 0000000000..3c97a6f8c3 --- /dev/null +++ b/ets2panda/test/ast/parser/ets/lambda_n/lambda_n_too_many_arg_neg_5.ets @@ -0,0 +1,42 @@ +/* + * Copyright (c) 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. + */ + +let fn = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17: String, + ...restStr: String[] +): void => { }; + +function main(): void { + fn("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); +} + +/* @@? 38:4 Error TypeError: Expected 17 arguments, got 15. */ +/* @@? 38:4 Error TypeError: No matching call signature for ("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!") */ diff --git a/ets2panda/test/runtime/ets/lambda_n/lambda_n_basic.ets b/ets2panda/test/runtime/ets/lambda_n/lambda_n_basic.ets new file mode 100644 index 0000000000..4457180430 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_n/lambda_n_basic.ets @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024-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. + */ + +function main(): void { + let fnVoid = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17: String, + str18: String + ): void => { + let res: String = str1 + str2 + str3 + str4 + str5 + str6 + str7 + str8 + str9 + str10 + str11 + str12 + str13 + str14 + str15 + str16 + str17 + str18; + arktest.assertEQ(res, "helloworld!!!!!!!!!!!!!!!!"); + }; + fnVoid("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); + + let fnStr = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17: String, + str18: String + ): String => { + let res: String = str1 + str2 + str3 + str4 + str5 + str6 + str7 + str8 + str9 + str10 + str11 + str12 + str13 + str14 + str15 + str16 + str17 + str18; + return res; + }; + let fnStrRetVal = fnStr("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); + arktest.assertEQ(fnStrRetVal, "helloworld!!!!!!!!!!!!!!!!"); + + let fnStrImmediatelyInvokedRetVal = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17: String, + str18: String + ): String => { + let res: String = str1 + str2 + str3 + str4 + str5 + str6 + str7 + str8 + str9 + str10 + str11 + str12 + str13 + str14 + str15 + str16 + str17 + str18; + return res; + }("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); + arktest.assertEQ(fnStrImmediatelyInvokedRetVal, "helloworld!!!!!!!!!!!!!!!!"); +} diff --git a/ets2panda/test/runtime/ets/lambda_n/lambda_n_optional.ets b/ets2panda/test/runtime/ets/lambda_n/lambda_n_optional.ets new file mode 100644 index 0000000000..d63538644a --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_n/lambda_n_optional.ets @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024-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. + */ + +function main(): void { + let fn = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17: String, + str18?: String + ): void => { + let res: String = str1 + str2 + str3 + str4 + str5 + str6 + str7 + str8 + str9 + str10 + str11 + str12 + str13 + str14 + str15 + str16 + str17; + if (str18) { + res += str18; + arktest.assertEQ(res, "helloworld!!!!!!!!!!!!!!!!"); + } else { + arktest.assertEQ(res, "helloworld!!!!!!!!!!!!!!!"); + } + }; + fn("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); + fn("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); +} diff --git a/ets2panda/test/runtime/ets/lambda_n/lambda_n_type.ets b/ets2panda/test/runtime/ets/lambda_n/lambda_n_type.ets new file mode 100644 index 0000000000..0deea4f33f --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_n/lambda_n_type.ets @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024-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. + */ +function main(): void { + type fn_type = (s1: String, s2: String, s3: String, s4: String, s5: String, + s6: String, s7: String, s8: String, s9: String, s10: String, + s11: String, s12: String, s13: String, s14: String, s15: String, + s16: String, s17: String) => void; + + let fn: fn_type = (str1: String, str2: String, str3: String): void => { + arktest.assertEQ(str1 + str2 + str3, "helloworld!"); + }; + fn("hello", "world", "!"); +} diff --git a/ets2panda/test/runtime/ets/lambda_n/lambda_n_with_default.ets b/ets2panda/test/runtime/ets/lambda_n/lambda_n_with_default.ets new file mode 100644 index 0000000000..c4ec5e4b4d --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_n/lambda_n_with_default.ets @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024-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. + */ + +function main(): void { + let fn = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17: String, + str18: String = "kittens" + ): void => { + let res: String = str1 + str2 + str3 + str4 + str5 + str6 + str7 + str8 + str9 + str10 + str11 + str12 + str13 + str14 + str15 + str16 + str17 + str18; + if (str18 != "") { + arktest.assertEQ(res, "helloworld!!!!!!!!!!!!!!!kittens"); + } else { + arktest.assertEQ(res, "helloworld!!!!!!!!!!!!!!!"); + } + }; + fn("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", ""); + fn("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); +} diff --git a/ets2panda/test/runtime/ets/lambda_n/lambda_n_with_rest.ets b/ets2panda/test/runtime/ets/lambda_n/lambda_n_with_rest.ets new file mode 100644 index 0000000000..a5d4abc556 --- /dev/null +++ b/ets2panda/test/runtime/ets/lambda_n/lambda_n_with_rest.ets @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024-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. + */ + +function main(): void { + let fn = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17: String, + ...restStr: String[] + ): void => { + let res: String = str1 + str2 + str3 + str4 + str5 + str6 + str7 + str8 + str9 + str10 + str11 + str12 + str13 + str14 + str15 + str16 + str17; + for (let s of restStr) { + res += s; + } + arktest.assertEQ(res, "helloworld!!!!!!!!!!!!!!!!!!"); + }; + fn("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); + + let fnFixed = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17: String, + ...restStr: FixedArray + ): void => { + let res: String = str1 + str2 + str3 + str4 + str5 + str6 + str7 + str8 + str9 + str10 + str11 + str12 + str13 + str14 + str15 + str16 + str17; + for (let s of restStr) { + res += s; + } + arktest.assertEQ(res, "helloworld!!!!!!!!!!!!!!!!!!"); + }; + fnFixed("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); + + let fnStrImmediatelyInvokedRetVal = ( + str1: String, + str2: String, + str3: String, + str4: String, + str5: String, + str6: String, + str7: String, + str8: String, + str9: String, + str10: String, + str11: String, + str12: String, + str13: String, + str14: String, + str15: String, + str16: String, + str17: String, + ...restStr: FixedArray + ): String => { + let res: String = str1 + str2 + str3 + str4 + str5 + str6 + str7 + str8 + str9 + str10 + str11 + str12 + str13 + str14 + str15 + str16 + str17; + for (let s of restStr) { + res += s; + } + return res; + }("hello", "world", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!", "!"); + arktest.assertEQ(fnStrImmediatelyInvokedRetVal, "helloworld!!!!!!!!!!!!!!!!!!"); +} diff --git a/ets2panda/test/test-lists/ets-runtime/ets-runtime-ignored.txt b/ets2panda/test/test-lists/ets-runtime/ets-runtime-ignored.txt index 1d93b6ed84..ee873256f8 100644 --- a/ets2panda/test/test-lists/ets-runtime/ets-runtime-ignored.txt +++ b/ets2panda/test/test-lists/ets-runtime/ets-runtime-ignored.txt @@ -119,4 +119,4 @@ Recursive_Parameter_3.ets # No-primitives #24986 [end] # 27159 Recursive type alias with generics -RecursiveTypeAlias10.ets \ No newline at end of file +RecursiveTypeAlias10.ets -- Gitee