diff --git a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp index 1d45f1c3716b04eb2c97fd3c207e1a1601c32c91..a4a2e05b69d95b30a30f47649bfb85fb0faafe5c 100644 --- a/ets2panda/compiler/lowering/ets/lambdaLowering.cpp +++ b/ets2panda/compiler/lowering/ets/lambdaLowering.cpp @@ -58,12 +58,12 @@ struct LambdaClassInvokeInfo { ArenaVector *argNames = nullptr; }; -static std::pair FindEnclosingClassAndFunction(ir::AstNode *ast) +static std::pair FindEnclosingClassAndFunction(ir::AstNode *ast) { ir::ScriptFunction *function = nullptr; for (ir::AstNode *curr = ast->Parent(); curr != nullptr; curr = curr->Parent()) { - if (curr->IsClassDeclaration()) { - return {curr->AsClassDeclaration(), function}; + if (curr->IsClassDeclaration() || curr->IsTSInterfaceDeclaration()) { + return {curr, function}; } if (curr->IsScriptFunction()) { function = curr->AsScriptFunction(); @@ -382,6 +382,22 @@ static void ProcessCalleeMethodBody(ir::AstNode *body, checker::ETSChecker *chec }); } +static ir::MethodDefinition *CheckCalleeMethodCtx(public_lib::Context *ctx, LambdaInfo const *info, + ir::ScriptFunction *func, ir::MethodDefinition *method) +{ + auto *varBinder = ctx->GetChecker()->VarBinder()->AsETSBinder(); + auto bctx = info->calleeClass + ? varbinder::BoundContext {varBinder->GetRecordTable(), info->calleeClass->Definition(), true} + : varbinder::BoundContext {varBinder->GetRecordTable(), info->calleeInterface, true}; + varBinder->ResolveReferencesForScopeWithContext(func, func->Scope()); + auto *objType = info->calleeClass ? info->calleeClass->Definition()->TsType()->AsETSObjectType() + : info->calleeInterface->TsType()->AsETSObjectType(); + auto checkerStatus = info->calleeClass ? checker::CheckerStatus::IN_CLASS : checker::CheckerStatus::IN_INTERFACE; + auto checkerCtx = checker::SavedCheckerContext(ctx->GetChecker(), checkerStatus, objType); + method->Check(ctx->GetChecker()->AsETSChecker()); + return method; +} + static ir::MethodDefinition *SetUpCalleeMethod(public_lib::Context *ctx, LambdaInfo const *info, CalleeMethodInfo const *cmInfo, ir::ScriptFunction *func, varbinder::Scope *scopeForMethod) @@ -389,12 +405,13 @@ static ir::MethodDefinition *SetUpCalleeMethod(public_lib::Context *ctx, LambdaI auto *allocator = ctx->allocator; auto *varBinder = ctx->GetChecker()->VarBinder()->AsETSBinder(); - auto *calleeClass = info->calleeClass; + auto *objType = info->calleeClass ? info->calleeClass->Definition()->TsType()->AsETSObjectType() + : info->calleeInterface->TsType()->AsETSObjectType(); auto *funcScope = func->Scope(); auto *paramScope = funcScope->ParamScope(); - auto modifierFlags = ir::ModifierFlags::PUBLIC | - (info->callReceiver != nullptr ? ir::ModifierFlags::NONE : ir::ModifierFlags::STATIC) | - cmInfo->auxModifierFlags; + auto isStatic = ((info->callReceiver != nullptr || info->calleeInterface) ? ir::ModifierFlags::NONE + : ir::ModifierFlags::STATIC); + auto modifierFlags = ir::ModifierFlags::PUBLIC | isStatic | cmInfo->auxModifierFlags; auto *calleeNameId = allocator->New(cmInfo->calleeName, allocator); func->SetIdent(calleeNameId); @@ -404,8 +421,13 @@ static ir::MethodDefinition *SetUpCalleeMethod(public_lib::Context *ctx, LambdaI auto *funcExpr = util::NodeAllocator::ForceSetParent(allocator, func); auto *method = util::NodeAllocator::ForceSetParent( allocator, ir::MethodDefinitionKind::METHOD, calleeNameClone, funcExpr, modifierFlags, allocator, false); - calleeClass->Definition()->EmplaceBody(method); - method->SetParent(calleeClass->Definition()); + if (info->calleeClass) { + info->calleeClass->Definition()->EmplaceBody(method); + method->SetParent(info->calleeClass->Definition()); + } else { + info->calleeInterface->Body()->Body().emplace_back(method); + method->SetParent(info->calleeInterface->Body()); + } auto *var = std::get<1>(varBinder->NewVarDecl(func->Start(), allocator, cmInfo->calleeName, func)); @@ -417,21 +439,12 @@ static ir::MethodDefinition *SetUpCalleeMethod(public_lib::Context *ctx, LambdaI if (info->callReceiver != nullptr) { auto paramScopeCtx = varbinder::LexicalScope::Enter(varBinder, paramScope); varBinder->AddMandatoryParam(varbinder::TypedBinder::MANDATORY_PARAM_THIS); - calleeClass->Definition()->TsType()->AsETSObjectType()->AddProperty( - var->AsLocalVariable()); + objType->AddProperty(var->AsLocalVariable()); } else { - calleeClass->Definition()->TsType()->AsETSObjectType()->AddProperty( - var->AsLocalVariable()); + objType->AddProperty(var->AsLocalVariable()); } - varbinder::BoundContext bctx {varBinder->GetRecordTable(), calleeClass->Definition(), true}; - varBinder->ResolveReferencesForScopeWithContext(func, funcScope); - - auto checkerCtx = checker::SavedCheckerContext(ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, - calleeClass->Definition()->TsType()->AsETSObjectType()); - method->Check(ctx->GetChecker()->AsETSChecker()); - - return method; + return CheckCalleeMethodCtx(ctx, info, func, method); } using ISS = ir::ScriptFunction::ScriptFunctionData; @@ -442,7 +455,8 @@ static ir::MethodDefinition *CreateCalleeMethod(public_lib::Context *ctx, ir::Ar auto *varBinder = ctx->GetChecker()->VarBinder()->AsETSBinder(); auto *checker = ctx->GetChecker()->AsETSChecker(); - auto *classScope = info->calleeClass->Definition()->Scope()->AsClassScope(); + auto *classScope = info->calleeClass ? info->calleeClass->Definition()->Scope()->AsClassScope() + : info->calleeInterface->Scope()->AsClassScope(); auto *oldTypeParams = (info->enclosingFunction != nullptr) ? info->enclosingFunction->TypeParams() : nullptr; auto enclosingScope = @@ -464,9 +478,8 @@ static ir::MethodDefinition *CreateCalleeMethod(public_lib::Context *ctx, ir::Ar auto returnTypeAnnotation = allocator->New(returnType, allocator); auto funcFlags = ir::ScriptFunctionFlags::METHOD | cmInfo->auxFunctionFlags; - auto modifierFlags = ir::ModifierFlags::PUBLIC | - (info->callReceiver != nullptr ? ir::ModifierFlags::NONE : ir::ModifierFlags::STATIC) | - cmInfo->auxModifierFlags; + auto modifierFlags = cmInfo->auxModifierFlags | ir::ModifierFlags::PUBLIC | + (info->callReceiver != nullptr ? ir::ModifierFlags::NONE : ir::ModifierFlags::STATIC); auto func = util::NodeAllocator::ForceSetParent( allocator, allocator, @@ -477,7 +490,8 @@ static ir::MethodDefinition *CreateCalleeMethod(public_lib::Context *ctx, ir::Ar auto *funcScope = cmInfo->body == nullptr ? allocator->New(allocator, paramScope) : cmInfo->body->Scope()->AsFunctionScope(); ES2PANDA_ASSERT(funcScope); - funcScope->BindName(info->calleeClass->Definition()->TsType()->AsETSObjectType()->AssemblerName()); + auto *tsType = info->calleeClass ? info->calleeClass->Definition()->TsType() : info->calleeInterface->TsType(); + funcScope->BindName(tsType->AsETSObjectType()->AssemblerName()); func->SetScope(funcScope); ProcessCalleeMethodBody(cmInfo->body, checker, paramScope, &substitution, varMap); @@ -1270,7 +1284,13 @@ static ir::AstNode *ConvertLambda(public_lib::Context *ctx, ir::ArrowFunctionExp ES2PANDA_ASSERT(lambda->TsType()->IsETSFunctionType()); LambdaInfo info; - std::tie(info.calleeClass, info.enclosingFunction) = FindEnclosingClassAndFunction(lambda); + ir::AstNode *enclosingClass = nullptr; + std::tie(enclosingClass, info.enclosingFunction) = FindEnclosingClassAndFunction(lambda); + if (enclosingClass->IsClassDeclaration()) { + info.calleeClass = enclosingClass->AsClassDeclaration(); + } else { + info.calleeInterface = enclosingClass->AsTSInterfaceDeclaration(); + } info.name = CreateCalleeName(allocator); if ((lambda->Parent() != nullptr) && lambda->Parent()->IsVariableDeclarator()) { @@ -1287,6 +1307,8 @@ static ir::AstNode *ConvertLambda(public_lib::Context *ctx, ir::ArrowFunctionExp if (auto *thisOrSuper = FindIfNeedThis(lambda, checker); thisOrSuper != nullptr) { info.callReceiver = allocator->New(); info.callReceiver->SetRange(thisOrSuper->Range()); + } else if (info.calleeInterface != nullptr) { + info.callReceiver = allocator->New(); } info.isFunctionReference = false; @@ -1363,9 +1385,11 @@ static ir::ArrowFunctionExpression *CreateWrappingLambda(public_lib::Context *ct varBinder->ResolveReferencesForScopeWithContext(lambda, nearestScope); auto [enclosingClass, _] = FindEnclosingClassAndFunction(parent); + auto *tsType = enclosingClass->IsClassDeclaration() ? enclosingClass->AsClassDeclaration()->Definition()->TsType() + : enclosingClass->AsTSInterfaceDeclaration()->TsType(); - auto checkerCtx = checker::SavedCheckerContext(ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, - enclosingClass->Definition()->TsType()->AsETSObjectType()); + auto checkerCtx = + checker::SavedCheckerContext(ctx->GetChecker(), checker::CheckerStatus::IN_CLASS, tsType->AsETSObjectType()); auto scopeCtx = checker::ScopeContext(ctx->GetChecker(), nearestScope); lambda->Check(ctx->GetChecker()->AsETSChecker()); diff --git a/ets2panda/ir/ts/tsInterfaceBody.cpp b/ets2panda/ir/ts/tsInterfaceBody.cpp index 94c56deddb610c93475c945c57739196999bba2b..cdfe00e7719f34cda928a72915ff421b89d888d5 100644 --- a/ets2panda/ir/ts/tsInterfaceBody.cpp +++ b/ets2panda/ir/ts/tsInterfaceBody.cpp @@ -24,7 +24,8 @@ namespace ark::es2panda::ir { void TSInterfaceBody::TransformChildren(const NodeTransformer &cb, std::string_view transformationName) { - for (auto *&it : VectorIterationGuard(body_)) { + for (size_t ix = 0; ix < body_.size(); ix++) { + auto *&it = body_[ix]; if (auto *transformedNode = cb(it); it != transformedNode) { it->SetTransformedNode(transformationName, transformedNode); it = transformedNode; diff --git a/ets2panda/test/runtime/ets/interface_method_with_lambda.ets b/ets2panda/test/runtime/ets/interface_method_with_lambda.ets new file mode 100644 index 0000000000000000000000000000000000000000..04f0815e4b77a77fe26e81e07080a569990201fe --- /dev/null +++ b/ets2panda/test/runtime/ets/interface_method_with_lambda.ets @@ -0,0 +1,38 @@ +/* + * 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. + */ + +interface Ifoo { + foo: int; + callFunc(): int { + + this.foo = 1; + let bar:int =1; + let callback = () => { + bar = 2; + return this.foo + bar; + } + return callback(); + } +} + +class A implements Ifoo { + foo: int; +} + +function main() { + let foo = new A(); + foo.callFunc(); + arktest.assertEQ(foo.callFunc(), 3); +}