diff --git a/arkoala-arkts/libarkts/src/arkts-api/types.ts b/arkoala-arkts/libarkts/src/arkts-api/types.ts index e48a86cc5f58dcaea1d719fb44dee8514d190699..2b48b844d2270cb25a9ff763be28a417afc7c9d9 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/types.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/types.ts @@ -1031,7 +1031,7 @@ export class ClassProperty extends ClassElement { constructor(peer: KPtr) { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY) super(peer) - this.typeAnnotation = unpackNonNullableNode(global.generatedEs2panda._ClassPropertyTypeAnnotationConst(global.context, this.peer)); + this.typeAnnotation = unpackNode(global.generatedEs2panda._ClassPropertyTypeAnnotationConst(global.context, this.peer)); } static create( @@ -1072,7 +1072,7 @@ export class ClassProperty extends ClassElement { ); } - readonly typeAnnotation: ETSTypeReference; + readonly typeAnnotation?: ETSTypeReference } export class VariableDeclaration extends AstNode { diff --git a/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts b/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts index de723054eecd7cb658e4770a6acfbc4efbe6879e..8d648d4da104069907d7d1238726395ebbd83b1f 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/utilities/public.ts @@ -21,6 +21,7 @@ import { isClassDefinition, isFunctionDeclaration, isScriptFunction } from "../f import { Es2pandaContextState } from "../../generated/Es2pandaEnums" import type { AstNode } from "../peers/AstNode" import type { AnnotationUsage } from "../../generated" +import { MemberExpression } from "../to-be-generated/MemberExpression" export function proceedToState(state: Es2pandaContextState): void { if (state <= global.es2panda._ContextState(global.context)) { @@ -60,6 +61,9 @@ export function rebindSubtree(node: AstNode): void { } export function getDecl(node: AstNode): AstNode | undefined { + if (node instanceof MemberExpression) { + return getDecl(node.property) + } const decl = global.es2panda._DeclarationFromIdentifier(global.context, passNode(node)) if (decl === nullptr) { return undefined diff --git a/arkoala-arkts/libarkts/src/generated/peers/TypedAstNode.ts b/arkoala-arkts/libarkts/src/generated/peers/TypedAstNode.ts index 2e92127278cf729e2499d635eeb284ef3689886a..1cd3e12480ebb9ec0e429cf8fd2bcabb73cf20c6 100644 --- a/arkoala-arkts/libarkts/src/generated/peers/TypedAstNode.ts +++ b/arkoala-arkts/libarkts/src/generated/peers/TypedAstNode.ts @@ -30,7 +30,7 @@ import { export abstract class TypedAstNode extends AstNode { constructor(pointer: KNativePointer) { super(pointer) - console.warn("Warning: stub node TypedAstNode") + // console.warn("Warning: stub node TypedAstNode") } } export function isTypedAstNode(node: AstNode): node is TypedAstNode { diff --git a/arkoala-arkts/memo-plugin/demo/demo.sts b/arkoala-arkts/memo-plugin/demo/demo.sts index 74bb5f28f4ae17e937de5e98fd7b8599d0638d30..ff83381120d557bbbc46f6d3d8e81a7ae704bf7c 100644 --- a/arkoala-arkts/memo-plugin/demo/demo.sts +++ b/arkoala-arkts/memo-plugin/demo/demo.sts @@ -1,8 +1,22 @@ import { GlobalStateManager, memoEntry, StateContext, memo } from "@koalaui/runtime" import { memo_foo } from "./stub" +@memo +function f(s: string) { + console.log(s) + let x = () => { + console.log(s) + } + x() + let y = (s: string) => { + console.log(s) + } + y("she") +} + @memo function foo_wrapper() { + ETSGLOBAL.f("he") memo_foo("hello") } diff --git a/arkoala-arkts/memo-plugin/demo/demo.ts b/arkoala-arkts/memo-plugin/demo/demo.ts index 989a003bf8da4c9e77cfb0b926af4f54cc2d4f10..1d5af07338529b78ce9c12918343892e955781c4 100644 --- a/arkoala-arkts/memo-plugin/demo/demo.ts +++ b/arkoala-arkts/memo-plugin/demo/demo.ts @@ -4,8 +4,21 @@ import { GlobalStateManager, memoEntry, StateContext } from "@koalaui/runtime" import { memo_foo } from "./stub" +/** @memo */ +function f(s: string) { + let x = () => { + console.log(s) + } + x() + let y = (s: string) => { + console.log(s) + } + y("she") +} + /** @memo */ function foo_wrapper() { + f("he") memo_foo("hello") } diff --git a/arkoala-arkts/memo-plugin/src/FunctionTransformer.ts b/arkoala-arkts/memo-plugin/src/FunctionTransformer.ts index 5378693755a4eb364cd4a00b3e6e35fc15b759eb..47e8ad344710d0aa851ab75f3cbcae6d32381e69 100644 --- a/arkoala-arkts/memo-plugin/src/FunctionTransformer.ts +++ b/arkoala-arkts/memo-plugin/src/FunctionTransformer.ts @@ -20,6 +20,7 @@ import { PositionalIdTracker, RuntimeNames } from "./utils" +import { ParameterTransformer } from "./ParameterTransformer" import { ReturnTransformer } from "./ReturnTranformer" function hasMemoAnnotation(node: arkts.ScriptFunction) { @@ -29,60 +30,40 @@ function hasMemoAnnotation(node: arkts.ScriptFunction) { } function updateFunctionBody( - node: arkts.BlockStatement | undefined, + node: arkts.BlockStatement, parameters: arkts.ETSParameterExpression[], returnTypeAnnotation: arkts.AstNode | undefined, hash: arkts.NumberLiteral | arkts.StringLiteral -): [arkts.BlockStatement | undefined, arkts.ReturnStatement | undefined] { - if (node === undefined) - return [node, undefined] - const scopeDeclaration = arkts.factory.createVariableDeclaration( - 0, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST, - [ - arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST, - arkts.factory.createIdentifier(RuntimeNames.SCOPE), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(RuntimeNames.CONTEXT), - arkts.factory.createIdentifier(RuntimeNames.INTERNAL_SCOPE), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - returnTypeAnnotation ? [returnTypeAnnotation] : [arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID)], - [ - factory.createIdArgument(hash), - arkts.factory.createNumericLiteral(parameters.length) - ], - ) +): [ + arkts.BlockStatement, + arkts.VariableDeclaration | undefined, + arkts.ReturnStatement | undefined, +] { + const scopeDeclaration = factory.createScopeDeclaration(returnTypeAnnotation, hash, parameters.length) + const memoParameters = parameters.map((name, id) => { return factory.createMemoParameterDeclarator(id, name.identifier.name) }) + const memoParametersDeclaration = memoParameters.length + ? [ + arkts.factory.createVariableDeclaration( + 0, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST, + memoParameters, ) ] - ) - const memoParameters = parameters.map((name, id) => { return factory.createMemoParameterDeclaration(id, name.identifier.name) }) + : [] const syntheticReturnStatement = factory.createSyntheticReturnStatement() - const unchangedCheck = arkts.factory.createIfStatement( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(RuntimeNames.SCOPE), - arkts.factory.createIdentifier(RuntimeNames.INTERNAL_VALUE_OK), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_NONE, - false, - false, - ), - syntheticReturnStatement, - ) + const unchangedCheck = factory.createIfStatementWithSyntheticReturnStatement(syntheticReturnStatement) if (node.statements.length && node.statements[node.statements.length - 1] instanceof arkts.ReturnStatement) { return [ arkts.factory.updateBlock( node, [ scopeDeclaration, - ...memoParameters, + ...memoParametersDeclaration, unchangedCheck, ...node.statements, ] ), + memoParametersDeclaration.length ? memoParametersDeclaration[0] : undefined, syntheticReturnStatement, ] } else { @@ -91,19 +72,24 @@ function updateFunctionBody( node, [ scopeDeclaration, - ...memoParameters, + ...memoParametersDeclaration, unchangedCheck, ...node.statements, arkts.factory.createReturnStatement(), ] ), + memoParametersDeclaration.length ? memoParametersDeclaration[0] : undefined, syntheticReturnStatement, ] } } export class FunctionTransformer extends AbstractVisitor { - constructor(private positionalIdTracker: PositionalIdTracker, private returnTransformer: ReturnTransformer) { + constructor( + private positionalIdTracker: PositionalIdTracker, + private parameterTransformer: ParameterTransformer, + private returnTransformer: ReturnTransformer + ) { super() } @@ -113,33 +99,37 @@ export class FunctionTransformer extends AbstractVisitor { beforeChildren instanceof arkts.MethodDefinition && hasMemoAnnotation(beforeChildren.scriptFunction) const node = this.visitEachChild(beforeChildren) - if (node instanceof arkts.MethodDefinition) { + if (node instanceof arkts.MethodDefinition && node.scriptFunction.body) { if (methodDefinitionHasMemoAnnotation) { - // TODO: fix - const [body, syntheticReturnStatement] = updateFunctionBody( + const [body, memoParametersDeclaration, syntheticReturnStatement] = updateFunctionBody( node.scriptFunction.body, node.scriptFunction.parameters, node.scriptFunction.returnTypeAnnotation, this.positionalIdTracker.id(node.name.name), ) + const afterParameterTransformer = this.parameterTransformer + .withParameters(node.scriptFunction.parameters) + .skip(memoParametersDeclaration) + .visitor(body) + const afterReturnTransformer = this.returnTransformer + .skip(syntheticReturnStatement) + .visitor(afterParameterTransformer) return arkts.factory.updateMethodDefinition( node, arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, node.name, arkts.factory.createFunctionExpression( - this.returnTransformer.skip(syntheticReturnStatement).visitEachChild( - arkts.factory.updateScriptFunction( - node.scriptFunction, - body, - node.scriptFunction.scriptFunctionFlags, - node.scriptFunction.modifiers, - false, - node.scriptFunction.ident, - [...factory.createHiddenParameters(), ...node.scriptFunction.parameters], - node.scriptFunction.typeParamsDecl, - node.scriptFunction.returnTypeAnnotation - ), - ) as arkts.ScriptFunction, + arkts.factory.updateScriptFunction( + node.scriptFunction, + afterReturnTransformer, + node.scriptFunction.scriptFunctionFlags, + node.scriptFunction.modifiers, + false, + node.scriptFunction.ident, + [...factory.createHiddenParameters(), ...node.scriptFunction.parameters], + node.scriptFunction.typeParamsDecl, + node.scriptFunction.returnTypeAnnotation + ) ), node.modifiers, false diff --git a/arkoala-arkts/memo-plugin/src/MemoFactory.ts b/arkoala-arkts/memo-plugin/src/MemoFactory.ts index 986c7d2c2850bf284b92cc693fb3775dde6d70c5..e4760ac18d28eb529142c1e5f40c0f2a18098c94 100644 --- a/arkoala-arkts/memo-plugin/src/MemoFactory.ts +++ b/arkoala-arkts/memo-plugin/src/MemoFactory.ts @@ -79,34 +79,63 @@ export class factory { static createMemoParameterIdentifier(name: string): arkts.Identifier { return arkts.factory.createIdentifier(`${RuntimeNames.PARAMETER}_${name}`) } - static createMemoParameterDeclaration(id: number, name: string): arkts.AstNode { + static createMemoParameterDeclarator(id: number, name: string): arkts.VariableDeclarator { + return arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST, + factory.createMemoParameterIdentifier(name), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(RuntimeNames.SCOPE), + arkts.factory.createIdentifier(RuntimeNames.INTERNAL_PARAMETER_STATE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createNumericLiteral(id), + arkts.factory.createIdentifier(name), + ], + ) + ) + } + static createMemoParameterAccess(name: string): arkts.MemberExpression { + return arkts.factory.createMemberExpression( + factory.createMemoParameterIdentifier(name), + arkts.factory.createIdentifier(RuntimeNames.VALUE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_GETTER, + false, + false, + ) + } + + // Recache + static createScopeDeclaration(returnTypeAnnotation: arkts.AstNode | undefined, hash: arkts.NumberLiteral | arkts.StringLiteral, cnt: number): arkts.VariableDeclaration { return arkts.factory.createVariableDeclaration( 0, arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_CONST, [ arkts.factory.createVariableDeclarator( arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_CONST, - this.createMemoParameterIdentifier(name), + arkts.factory.createIdentifier(RuntimeNames.SCOPE), arkts.factory.createCallExpression( arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(RuntimeNames.SCOPE), - arkts.factory.createIdentifier(RuntimeNames.INTERNAL_PARAMETER_STATE), + arkts.factory.createIdentifier(RuntimeNames.CONTEXT), + arkts.factory.createIdentifier(RuntimeNames.INTERNAL_SCOPE), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, false ), - undefined, + returnTypeAnnotation ? [returnTypeAnnotation] : [arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID)], [ - arkts.factory.createNumericLiteral(id), - arkts.factory.createIdentifier(name), + factory.createIdArgument(hash), + arkts.factory.createNumericLiteral(cnt) ], ) ) ] ) } - - // Recache static createRecacheCall(arg?: arkts.AstNode): arkts.CallExpression { return arkts.factory.createCallExpression( arkts.factory.createMemberExpression( @@ -131,4 +160,16 @@ export class factory { ), ) } + static createIfStatementWithSyntheticReturnStatement(syntheticReturnStatement: arkts.ReturnStatement): arkts.IfStatement { + return arkts.factory.createIfStatement( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(RuntimeNames.SCOPE), + arkts.factory.createIdentifier(RuntimeNames.INTERNAL_VALUE_OK), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_NONE, + false, + false, + ), + syntheticReturnStatement, + ) + } } diff --git a/arkoala-arkts/memo-plugin/src/MemoTransformer.ts b/arkoala-arkts/memo-plugin/src/MemoTransformer.ts index a4b7df728c9eb0fbf69b1574d5eed288abb20a6a..8ac6ee906c6ef7537c4d5e4568d653459f05e2d6 100644 --- a/arkoala-arkts/memo-plugin/src/MemoTransformer.ts +++ b/arkoala-arkts/memo-plugin/src/MemoTransformer.ts @@ -17,6 +17,7 @@ import * as arkts from "@koalaui/libarkts" import { factory } from "./MemoFactory" import { FunctionTransformer } from "./FunctionTransformer" import { PositionalIdTracker } from "./utils" +import { ParameterTransformer } from "./ParameterTransformer" import { ReturnTransformer } from "./ReturnTranformer" export interface TransformerOptions { @@ -28,8 +29,9 @@ export default function memoTransformer( ) { return (node: arkts.EtsScript) => { const positionalIdTracker = new PositionalIdTracker(arkts.getFileName(), false) + const parameterTransformer = new ParameterTransformer() const returnTransformer = new ReturnTransformer() - const functionTransformer = new FunctionTransformer(positionalIdTracker, returnTransformer) + const functionTransformer = new FunctionTransformer(positionalIdTracker, parameterTransformer, returnTransformer) return functionTransformer.visitor( arkts.factory.updateEtsScript( node, diff --git a/arkoala-arkts/memo-plugin/src/ParameterTransformer.ts b/arkoala-arkts/memo-plugin/src/ParameterTransformer.ts new file mode 100644 index 0000000000000000000000000000000000000000..cf4d874e3b21c517cfaed2db31e40a06cb7853ee --- /dev/null +++ b/arkoala-arkts/memo-plugin/src/ParameterTransformer.ts @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2022-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. + */ + +import * as arkts from "@koalaui/libarkts" +import { factory } from "./MemoFactory" +import { AbstractVisitor } from "./AbstractVisitor" +import { KPointer } from "@koalaui/interop" +import { isMemoParametersDeclaration } from "./utils" + +export class ParameterTransformer extends AbstractVisitor { + private rewrites?: Map arkts.MemberExpression> + private skipNode?: arkts.VariableDeclaration + + withParameters(parameters: arkts.ETSParameterExpression[]): ParameterTransformer { + this.rewrites = new Map(parameters.map((it) => { + return [it.peer, () => factory.createMemoParameterAccess(it.identifier.name)] + })) + return this + } + + skip(memoParametersDeclaration?: arkts.VariableDeclaration): ParameterTransformer { + this.skipNode = memoParametersDeclaration + return this + } + + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + // TODO: temporary checking skip nodes by comparison with expected skip nodes + // Should be fixed when update procedure implemented properly + if (/* beforeChildren === this.skipNode */ isMemoParametersDeclaration(beforeChildren)) { + return beforeChildren + } + const node = this.visitEachChild(beforeChildren) + if (node instanceof arkts.Identifier) { + const decl = arkts.getDecl(node) + if (decl instanceof arkts.ETSParameterExpression && this.rewrites?.get(decl.peer)) { + return this.rewrites.get(decl.peer)?.() ?? node + } + } + return node + } +} diff --git a/arkoala-arkts/memo-plugin/src/ReturnTranformer.ts b/arkoala-arkts/memo-plugin/src/ReturnTranformer.ts index 02573e05fb090b3e2fe30c1cdae0d0655617dcb6..c7361eea51c9e9702fdc94e4ac49ecdf171bd85a 100644 --- a/arkoala-arkts/memo-plugin/src/ReturnTranformer.ts +++ b/arkoala-arkts/memo-plugin/src/ReturnTranformer.ts @@ -16,21 +16,27 @@ import * as arkts from "@koalaui/libarkts" import { factory } from "./MemoFactory" import { AbstractVisitor } from "./AbstractVisitor" -import { RuntimeNames } from "./utils" +import { isSyntheticReturnStatement } from "./utils" export class ReturnTransformer extends AbstractVisitor { - _skip?: arkts.ReturnStatement + private skipNode?: arkts.ReturnStatement + skip(syntheticReturnStatement?: arkts.ReturnStatement): ReturnTransformer { - this._skip = syntheticReturnStatement + this.skipNode = syntheticReturnStatement return this } visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + // TODO: temporary checking skip nodes by comparison with expected skip nodes + // Should be fixed when update procedure implemented properly + if (/* beforeChildren === this.skipNode */ isSyntheticReturnStatement(beforeChildren)) { + return beforeChildren + } if (beforeChildren instanceof arkts.ScriptFunction) { return beforeChildren } const node = this.visitEachChild(beforeChildren) - if (node instanceof arkts.ReturnStatement && !(node.peer == this._skip?.peer)) { + if (node instanceof arkts.ReturnStatement) { return arkts.factory.updateReturnStatement(node, factory.createRecacheCall(node.argument)) } return node diff --git a/arkoala-arkts/memo-plugin/src/utils.ts b/arkoala-arkts/memo-plugin/src/utils.ts index a15f05b32c25caa75e8dde99948894d4def47ff9..96d5befb48f981233d12be01c7f02ae1d3941695 100644 --- a/arkoala-arkts/memo-plugin/src/utils.ts +++ b/arkoala-arkts/memo-plugin/src/utils.ts @@ -79,3 +79,25 @@ export class PositionalIdTracker { : arkts.factory.createNumericLiteral(parseInt(positionId, 16)) } } + +/** + * TODO: + * @deprecated + */ +export function isSyntheticReturnStatement(node: arkts.AstNode) { + return node instanceof arkts.ReturnStatement && + node.argument instanceof arkts.MemberExpression && + node.argument.object instanceof arkts.Identifier && + node.argument.object.name === RuntimeNames.SCOPE && + node.argument.property instanceof arkts.Identifier && + node.argument.property.name === RuntimeNames.INTERNAL_VALUE +} + +/** + * TODO: + * @deprecated + */ +export function isMemoParametersDeclaration(node: arkts.AstNode) { + return node instanceof arkts.VariableDeclaration && + node.declarators.every((it) => it.name.name.startsWith(RuntimeNames.PARAMETER)) +}