From e21aaf29a4d84ab4637350dd2f5fc2da403c7983 Mon Sep 17 00:00:00 2001 From: Jiakai Shi Date: Mon, 9 Jun 2025 17:33:17 +0800 Subject: [PATCH] add ui cache used for collecting memoable ast-nodes Signed-off-by: Jiakai Shi Change-Id: I673b8d3986e68fe876caa21ca07ae89a7a0e0dbb --- .../collectors/memo-collectors/factory.ts | 95 ++ .../memo-collectors/function-collector.ts | 218 +++++ .../memo-collectors/memo-visitor.ts | 26 + .../collectors/memo-collectors/utils.ts | 813 ++++++++++++++++++ arkui-plugins/common/arkts-utils.ts | 60 ++ arkui-plugins/common/predefines.ts | 5 + arkui-plugins/jest-test.config.js | 5 +- .../memo-plugins/function-transformer.ts | 23 + arkui-plugins/memo-plugins/index.ts | 7 +- .../memo-plugins/memo-cache-factory.ts | 395 +++++++++ arkui-plugins/memo-plugins/memo-factory.ts | 37 +- .../memo-plugins/parameter-transformer.ts | 5 +- arkui-plugins/memo-plugins/utils.ts | 99 ++- arkui-plugins/package.json | 2 +- .../demo/localtest/build_config_template.json | 3 +- .../demo/mock/memo/lambdas/with-receiver.ets | 11 +- .../mock/memo/methods/non-void-method.ets | 7 + .../test/ut/common/annotation.test.ts | 23 +- .../argument-call.test.ts | 12 +- .../complex-memo-intrinsic.test.ts | 16 +- .../declare-and-call.test.ts | 10 +- .../inner-functions.test.ts | 18 +- .../internal-memo-arg.test.ts | 22 +- .../non-void-return-type.test.ts | 18 +- .../type-reference.test.ts | 23 +- .../void-return-type.test.ts | 6 +- .../lambda-literals/argument-call.test.ts | 36 +- .../function-with-receiver.test.ts | 10 +- .../lambda-literals/trailing-lambdas.test.ts | 22 +- .../lambda-literals/void-lambda.test.ts | 4 +- .../lambda-literals/with-receiver.test.ts | 68 +- .../method-declarations/argument-call.test.ts | 16 +- .../method-declarations/callable.test.ts | 12 +- .../declare-and-call.test.ts | 8 +- .../internal-calls.test.ts | 28 +- .../non-void-method.test.ts | 31 +- .../method-declarations/void-method.test.ts | 18 +- .../class-constructor.test.ts | 12 +- .../class-properties.test.ts | 12 +- .../property-declarations/interfaces.test.ts | 18 +- .../animation/animatable-extend-basic.test.ts | 8 +- .../animation/animation-basic.test.ts | 8 +- .../custom-component-call.test.ts | 20 +- .../builder-lambda/simple-component.test.ts | 12 +- .../style-with-receiver.test.ts | 8 +- .../builder-param-passing.test.ts | 16 +- .../init-with-local-builder.test.ts | 8 +- .../decorators/builder/global-builder.test.ts | 8 +- .../decorators/builder/local-builder.test.ts | 10 +- .../link/link-to-link-prop-state.test.ts | 12 +- .../decorators/link/state-to-link.test.ts | 32 +- .../objectlink/objectlink-observed.test.ts | 24 +- .../observed-track-complex-type.test.ts | 2 +- .../decorators/prop/state-to-prop.test.ts | 28 +- .../resource/resource-in-build.test.ts | 31 +- .../resource/resource-in-property.test.ts | 9 +- .../reusable/reusable-complex.test.ts | 22 +- .../decorators/state/state-basic-type.test.ts | 4 + .../decorators/state/state-to-state.test.ts | 10 +- .../storagelink-appstorage.test.ts | 16 +- .../storagelink-complex-type.test.ts | 2 +- .../storagelink-primitive-type.test.ts | 2 +- .../storageprop-appstorage.test.ts | 16 +- .../storageprop-complex-type.test.ts | 2 +- .../storageprop-primitive-type.test.ts | 2 +- .../decorators/watch/watch-basic.test.ts | 4 +- .../double-dollar-griditem.test.ts | 22 +- .../ui-plugins/imports/import-struct.test.ts | 8 +- .../ut/ui-plugins/imports/kit-import.test.ts | 16 +- .../wrap-builder/init-with-builder.test.ts | 8 +- .../wrap-builder/wrap-builder-in-ui.test.ts | 12 +- arkui-plugins/test/utils/artkts-config.ts | 43 +- arkui-plugins/test/utils/compile.ts | 4 +- arkui-plugins/test/utils/global.ts | 11 +- arkui-plugins/test/utils/plugin-tester.ts | 2 +- .../utils/plugins/before-memo-no-recheck.ts | 47 + arkui-plugins/test/utils/plugins/index.ts | 1 + .../test/utils/plugins/memo-no-recheck.ts | 15 +- arkui-plugins/tsconfig.build.json | 1 + arkui-plugins/tsconfig.json | 1 + .../builder-lambda-transformer.ts | 2 +- .../builder-lambda-translators/factory.ts | 205 +++-- .../builder-lambda-translators/utils.ts | 35 +- .../ui-plugins/checked-transformer.ts | 46 +- .../ui-plugins/component-transformer.ts | 6 +- .../ui-plugins/entry-translators/factory.ts | 4 +- arkui-plugins/ui-plugins/index.ts | 3 - .../property-translators/builderParam.ts | 44 +- .../property-translators/factory.ts | 3 +- .../ui-plugins/property-translators/utils.ts | 34 +- .../ui-plugins/struct-translators/factory.ts | 43 +- .../struct-translators/struct-transformer.ts | 16 +- .../ui-plugins/struct-translators/utils.ts | 35 +- arkui-plugins/ui-plugins/ui-factory.ts | 15 +- arkui-plugins/ui-plugins/utils.ts | 87 +- koala-wrapper/native/src/bridges.cc | 28 +- koala-wrapper/src/Es2pandaNativeModule.ts | 15 +- koala-wrapper/src/arkts-api/class-by-peer.ts | 2 +- koala-wrapper/src/arkts-api/index.ts | 145 ++-- .../node-utilities/ArrowFunctionExpression.ts | 7 +- .../node-utilities/CallExpression.ts | 7 +- .../arkts-api/node-utilities/ClassProperty.ts | 7 +- .../node-utilities/ETSFunctionType.ts | 7 +- .../node-utilities/ETSParameterExpression.ts | 9 +- .../arkts-api/node-utilities/ETSUnionType.ts | 7 +- .../arkts-api/node-utilities/Identifier.ts | 7 +- .../node-utilities/MethodDefinition.ts | 7 +- .../src/arkts-api/node-utilities/Property.ts | 8 +- .../node-utilities/ReturnStatement.ts | 7 +- .../node-utilities/ScriptFunction.ts | 7 +- .../node-utilities/TSTypeAliasDeclaration.ts | 7 +- .../node-utilities/VariableDeclarator.ts | 7 +- koala-wrapper/src/arkts-api/peers/AstNode.ts | 2 +- koala-wrapper/src/arkts-api/types.ts | 9 +- .../src/arkts-api/utilities/nodeCache.ts | 103 +++ .../src/arkts-api/utilities/private.ts | 4 - .../src/arkts-api/utilities/public.ts | 15 +- koala-wrapper/src/arkts-api/visitor.ts | 33 +- 118 files changed, 2869 insertions(+), 920 deletions(-) create mode 100644 arkui-plugins/collectors/memo-collectors/factory.ts create mode 100644 arkui-plugins/collectors/memo-collectors/function-collector.ts create mode 100644 arkui-plugins/collectors/memo-collectors/memo-visitor.ts create mode 100644 arkui-plugins/collectors/memo-collectors/utils.ts create mode 100644 arkui-plugins/memo-plugins/memo-cache-factory.ts create mode 100644 arkui-plugins/test/utils/plugins/before-memo-no-recheck.ts create mode 100644 koala-wrapper/src/arkts-api/utilities/nodeCache.ts diff --git a/arkui-plugins/collectors/memo-collectors/factory.ts b/arkui-plugins/collectors/memo-collectors/factory.ts new file mode 100644 index 000000000..e1d6e1745 --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/factory.ts @@ -0,0 +1,95 @@ +/* + * 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. + */ + +import * as arkts from '@koalaui/libarkts'; +import { + addMemoAnnotation, + collectMemoFromCallExpression, + findCanAddMemoFromArrowFunction, + findCanAddMemoFromClassProperty, + findCanAddMemoFromMethod, + findCanAddMemoFromParameter, + findCanAddMemoFromProperty, + findCanAddMemoFromTypeAlias, +} from './utils'; + +export function findAndCollectMemoableNode(node: arkts.AstNode): arkts.AstNode { + const type = arkts.nodeType(node); + if (collectByType.has(type)) { + return collectByType.get(type)!(node); + } + return node; +} + +export class factory { + static findAndCollectMemoableProperty(node: arkts.Property): arkts.Property { + if (findCanAddMemoFromProperty(node)) { + addMemoAnnotation(node.value! as arkts.ArrowFunctionExpression); + } + return node; + } + + static findAndCollectMemoableClassProperty(node: arkts.ClassProperty): arkts.ClassProperty { + if (findCanAddMemoFromClassProperty(node)) { + addMemoAnnotation(node); + } + return node; + } + + static findAndCollectMemoableTypeAlias(node: arkts.TSTypeAliasDeclaration): arkts.TSTypeAliasDeclaration { + if (findCanAddMemoFromTypeAlias(node)) { + addMemoAnnotation(node); + } + return node; + } + + static findAndCollectMemoableParameter(node: arkts.ETSParameterExpression): arkts.ETSParameterExpression { + if (findCanAddMemoFromParameter(node)) { + addMemoAnnotation(node); + } + return node; + } + + static findAndCollectMemoableMethod(node: arkts.MethodDefinition): arkts.MethodDefinition { + if (findCanAddMemoFromMethod(node)) { + addMemoAnnotation(node.scriptFunction); + } + return node; + } + + static findAndCollectMemoableArrowFunction(node: arkts.ArrowFunctionExpression): arkts.ArrowFunctionExpression { + if (findCanAddMemoFromArrowFunction(node)) { + addMemoAnnotation(node.scriptFunction); + } + return node; + } + + static findAndCollectMemoableCallExpression(node: arkts.CallExpression): arkts.CallExpression { + collectMemoFromCallExpression(node); + return node; + } +} + +type CollectFactoryFn = (node: any) => arkts.AstNode; + +const collectByType = new Map([ + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY, factory.findAndCollectMemoableProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY, factory.findAndCollectMemoableClassProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION, factory.findAndCollectMemoableTypeAlias], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION, factory.findAndCollectMemoableParameter], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION, factory.findAndCollectMemoableMethod], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION, factory.findAndCollectMemoableArrowFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CALL_EXPRESSION, factory.findAndCollectMemoableCallExpression], +]); diff --git a/arkui-plugins/collectors/memo-collectors/function-collector.ts b/arkui-plugins/collectors/memo-collectors/function-collector.ts new file mode 100644 index 000000000..df1f8fd4f --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/function-collector.ts @@ -0,0 +1,218 @@ +/* + * 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. + */ + +import * as arkts from '@koalaui/libarkts'; +import { AbstractVisitor } from '../../common/abstract-visitor'; +import { + checkIsMemoFromMemoableInfo, + collectMemoableInfoInFunctionReturnType, + collectMemoableInfoInScriptFunction, + collectMemoableInfoInVariableDeclarator, + collectMemoableInfoMapInFunctionParams, + collectMemoScriptFunctionBody, + findIdentifierFromCallee, + getDeclResolveAlias, + MemoableInfo, +} from './utils'; + +export class MemoFunctionCollector extends AbstractVisitor { + private returnMemoableInfo: MemoableInfo | undefined; + private paramMemoableInfoMap: Map | undefined; + private _disableCollectReturn: boolean = false; + private _shouldCollectReturn: boolean = true; + + private get shouldCollectReturn(): boolean { + if (this._disableCollectReturn) { + return false; + } + return this._shouldCollectReturn; + } + + private set shouldCollectReturn(newValue: boolean) { + if (this._disableCollectReturn) { + return; + } + this._shouldCollectReturn = newValue; + } + + private collectMemoAstNode(node: arkts.AstNode, info: MemoableInfo): void { + if (checkIsMemoFromMemoableInfo(info, false)) { + arkts.NodeCache.getInstance().collect(node); + } + } + + private collectCallWithDeclaredPeerInParamMap(node: arkts.CallExpression, peer: arkts.AstNode['peer']): void { + const memoableInfo = this.paramMemoableInfoMap!.get(peer)!; + if (checkIsMemoFromMemoableInfo(memoableInfo, true)) { + arkts.NodeCache.getInstance().collect(node); + } + } + + private collectCallWithDeclaredIdInVariableDeclarator( + node: arkts.CallExpression, + declarator: arkts.VariableDeclarator + ): void { + const shouldCollect = + arkts.NodeCache.getInstance().has(declarator) || + (!!declarator.initializer && arkts.NodeCache.getInstance().has(declarator.initializer)); + if (shouldCollect) { + arkts.NodeCache.getInstance().collect(node); + } + } + + private visitVariableDeclarator(node: arkts.VariableDeclarator): arkts.AstNode { + let memoableInfo: MemoableInfo; + if (this.paramMemoableInfoMap?.has(node.name.peer)) { + memoableInfo = this.paramMemoableInfoMap.get(node.name.peer)!; + } else { + memoableInfo = collectMemoableInfoInVariableDeclarator(node); + } + this.collectMemoAstNode(node, memoableInfo); + if (!node.initializer) { + return node; + } + if (arkts.isArrowFunctionExpression(node.initializer)) { + const localInfo = collectMemoableInfoInScriptFunction(node.initializer.scriptFunction); + const shouldCollectParameter = + (localInfo.hasBuilder || localInfo.hasMemo) && !localInfo.hasMemoEntry && !localInfo.hasMemoIntrinsic; + const shouldCollectReturn = + localInfo.hasBuilder || localInfo.hasMemo || memoableInfo.hasBuilder || memoableInfo.hasMemo; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(node.initializer.scriptFunction); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams( + node.initializer.scriptFunction, + shouldCollectParameter + ); + if ( + !!node.initializer.scriptFunction.body && + arkts.isBlockStatement(node.initializer.scriptFunction.body) + ) { + collectMemoScriptFunctionBody( + node.initializer.scriptFunction.body, + returnMemoableInfo, + paramMemoableInfoMap, + gensymCount, + !shouldCollectReturn + ); + } + return node; + } + this.shouldCollectReturn = !!memoableInfo.hasMemo || !!memoableInfo.hasBuilder; + this.visitor(node.initializer); + return node; + } + + private visitCallExpression(node: arkts.CallExpression): arkts.AstNode { + if (arkts.NodeCache.getInstance().has(node)) { + this.shouldCollectReturn = false; + this.visitEachChild(node); + this.shouldCollectReturn = true; + return node; + } + const expr = findIdentifierFromCallee(node.expression); + const decl = (expr && getDeclResolveAlias(expr)) ?? node.expression; + if (!decl) { + this.shouldCollectReturn = false; + this.visitEachChild(node); + this.shouldCollectReturn = true; + return node; + } + if (arkts.NodeCache.getInstance().has(decl)) { + arkts.NodeCache.getInstance().collect(node); + } + if (this.paramMemoableInfoMap?.has(decl.peer)) { + this.collectCallWithDeclaredPeerInParamMap(node, decl.peer); + } else if (arkts.isEtsParameterExpression(decl) && this.paramMemoableInfoMap?.has(decl.identifier.peer)) { + this.collectCallWithDeclaredPeerInParamMap(node, decl.identifier.peer); + } else if (arkts.isIdentifier(decl) && !!decl.parent && arkts.isVariableDeclarator(decl.parent)) { + this.collectCallWithDeclaredIdInVariableDeclarator(node, decl.parent); + } + this.shouldCollectReturn = false; + this.visitEachChild(node); + this.shouldCollectReturn = true; + return node; + } + + private visitIdentifier(node: arkts.Identifier): arkts.AstNode { + const decl = getDeclResolveAlias(node); + if (!decl) { + return node; + } + if (this.paramMemoableInfoMap?.has(decl.peer)) { + arkts.NodeCache.getInstance().collect(node); + } else if (arkts.isEtsParameterExpression(decl) && this.paramMemoableInfoMap?.has(decl.identifier.peer)) { + arkts.NodeCache.getInstance().collect(node); + } + return node; + } + + private visitReturnStatement(node: arkts.ReturnStatement): arkts.AstNode { + if (!!this.returnMemoableInfo && !!node.argument && arkts.isArrowFunctionExpression(node.argument)) { + this.collectMemoAstNode(node.argument, this.returnMemoableInfo); + } + arkts.NodeCache.getInstance().collect(node); + this.visitEachChild(node); + return node; + } + + registerReturnInfo(info: MemoableInfo): this { + this.returnMemoableInfo = info; + return this; + } + + registerParamInfoMap(infoMap: Map): this { + this.paramMemoableInfoMap = infoMap; + return this; + } + + disableCollectReturn(): this { + this._disableCollectReturn = true; + return this; + } + + enableCollectReturn(): this { + this._disableCollectReturn = false; + return this; + } + + reset(): void { + this.returnMemoableInfo = undefined; + this.paramMemoableInfoMap = undefined; + this._shouldCollectReturn = true; + this._disableCollectReturn = false; + } + + visitor(node: arkts.AstNode): arkts.AstNode { + if (arkts.isVariableDeclarator(node)) { + return this.visitVariableDeclarator(node); + } + if (arkts.isCallExpression(node)) { + return this.visitCallExpression(node); + } + if (!!this.paramMemoableInfoMap && arkts.isIdentifier(node)) { + return this.visitIdentifier(node); + } + if (arkts.isReturnStatement(node) && this.shouldCollectReturn) { + return this.visitReturnStatement(node); + } + if ( + arkts.isArrowFunctionExpression(node) && + !arkts.NodeCache.getInstance().has(node) && + !arkts.NodeCache.getInstance().has(node.scriptFunction) + ) { + this.shouldCollectReturn = false; + } + return this.visitEachChild(node); + } +} diff --git a/arkui-plugins/collectors/memo-collectors/memo-visitor.ts b/arkui-plugins/collectors/memo-collectors/memo-visitor.ts new file mode 100644 index 000000000..233288748 --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/memo-visitor.ts @@ -0,0 +1,26 @@ +/* + * 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. + */ + +import * as arkts from '@koalaui/libarkts'; +import { AbstractVisitor } from '../../common/abstract-visitor'; +import { findAndCollectMemoableNode } from './factory'; + +export class MemoVisitor extends AbstractVisitor { + visitor(node: arkts.AstNode): arkts.AstNode { + const newNode = this.visitEachChild(node); + findAndCollectMemoableNode(newNode); + return newNode; + } +} diff --git a/arkui-plugins/collectors/memo-collectors/utils.ts b/arkui-plugins/collectors/memo-collectors/utils.ts new file mode 100644 index 000000000..0be9d2f07 --- /dev/null +++ b/arkui-plugins/collectors/memo-collectors/utils.ts @@ -0,0 +1,813 @@ +/* + * 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. + */ + +import * as arkts from '@koalaui/libarkts'; +import { annotation, forEachArgWithParam, isDecoratorAnnotation } from '../../common/arkts-utils'; +import { ImportCollector } from '../../common/import-collector'; +import { DecoratorNames, GenSymPrefix, MEMO_IMPORT_SOURCE_NAME } from '../../common/predefines'; +import { MemoFunctionCollector } from './function-collector'; + +export enum MemoNames { + MEMO = 'memo', + MEMO_SKIP = 'memo_skip', + MEMO_INTRINSIC = 'memo_intrinsic', + MEMO_ENTRY = 'memo_entry', +} + +export type MemoAstNode = + | arkts.ScriptFunction + | arkts.ETSParameterExpression + | arkts.ClassProperty + | arkts.TSTypeAliasDeclaration + | arkts.ETSFunctionType + | arkts.ArrowFunctionExpression + | arkts.ETSUnionType + | arkts.VariableDeclaration; + +interface MemoableAnnotationInfo { + hasMemo?: boolean; + hasMemoSkip?: boolean; + hasMemoIntrinsic?: boolean; + hasMemoEntry?: boolean; + hasBuilder?: boolean; + hasBuilderParam?: boolean; +} + +export type MemoableInfo = MemoableAnnotationInfo & { + hasProperType?: boolean; +}; + +export function isMemoAnnotation(node: arkts.AnnotationUsage, memoName: MemoNames): boolean { + return node.expr !== undefined && arkts.isIdentifier(node.expr) && node.expr.name === memoName; +} + +export function hasMemoAnnotation(node: T): boolean { + return node.annotations.some((it) => isMemoAnnotation(it, MemoNames.MEMO)); +} + +export function addMemoAnnotation(node: T, memoName: MemoNames = MemoNames.MEMO): T { + collectMemoAnnotationSource(memoName); + if (arkts.isETSUnionType(node)) { + return arkts.factory.updateUnionType( + node, + node.types.map((type) => { + if (arkts.isETSFunctionType(type)) { + return addMemoAnnotation(type, memoName); + } + return type; + }) + ) as T; + } + const newAnnotations: arkts.AnnotationUsage[] = [ + ...node.annotations.filter((it) => !isMemoAnnotation(it, memoName)), + annotation(memoName), + ]; + collectMemoAnnotationImport(memoName); + if (arkts.isEtsParameterExpression(node)) { + node.annotations = newAnnotations; + arkts.NodeCache.getInstance().collect(node); + return node; + } + const newNode = node.setAnnotations(newAnnotations) as T; + arkts.NodeCache.getInstance().collect(newNode); + return newNode; +} + +export function hasMemoableAnnotation(node: T): MemoableAnnotationInfo { + let hasBuilder: boolean = false; + let hasBuilderParam: boolean = false; + let hasMemo: boolean = false; + let hasMemoSkip: boolean = false; + let hasMemoIntrinsic: boolean = false; + let hasMemoEntry: boolean = false; + node.annotations.forEach((it) => { + hasBuilder ||= isDecoratorAnnotation(it, DecoratorNames.BUILDER); + hasBuilderParam ||= isDecoratorAnnotation(it, DecoratorNames.BUILDER_PARAM); + hasMemo ||= isMemoAnnotation(it, MemoNames.MEMO); + hasMemoSkip ||= isMemoAnnotation(it, MemoNames.MEMO_SKIP); + hasMemoIntrinsic ||= isMemoAnnotation(it, MemoNames.MEMO_INTRINSIC); + hasMemoEntry ||= isMemoAnnotation(it, MemoNames.MEMO_ENTRY); + }); + return { + ...(hasMemo ? { hasMemo } : {}), + ...(hasMemoSkip ? { hasMemoSkip } : {}), + ...(hasMemoIntrinsic ? { hasMemoIntrinsic } : {}), + ...(hasMemoEntry ? { hasMemoEntry } : {}), + ...(hasBuilder ? { hasBuilder } : {}), + ...(hasBuilderParam ? { hasBuilderParam } : {}), + }; +} + +export function collectMemoAnnotationImport(memoName: MemoNames = MemoNames.MEMO): void { + ImportCollector.getInstance().collectImport(memoName); +} + +export function collectMemoAnnotationSource(memoName: MemoNames = MemoNames.MEMO): void { + ImportCollector.getInstance().collectSource(memoName, MEMO_IMPORT_SOURCE_NAME); +} + +export function collectMemoableInfoInUnionType(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isETSUnionType(node)) { + return currInfo; + } + node.types.forEach((t) => { + currInfo = { + ...currInfo, + ...collectMemoableInfoInTypeReference(t), + ...collectMemoableInfoInFunctionType(t), + ...collectMemoableInfoInUnionType(t), + }; + }); + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + return currInfo; +} + +export function collectMemoableInfoInTypeReference(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isETSTypeReference(node) || !node.part || !arkts.isETSTypeReferencePart(node.part)) { + return currInfo; + } + const expr = node.part.name; + let decl: arkts.AstNode | undefined; + if (!expr || !(decl = arkts.getDecl(expr))) { + return currInfo; + } + return { + ...currInfo, + ...collectMemoableInfoInTypeAlias(decl), + }; +} + +export function collectMemoableInfoInFunctionType(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isETSFunctionType(node)) { + return currInfo; + } + currInfo.hasProperType = true; + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + return currInfo; +} + +export function collectMemoableInfoInTypeAlias(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isTSTypeAliasDeclaration(node)) { + return currInfo; + } + currInfo = { + ...currInfo, + ...hasMemoableAnnotation(node), + }; + if (!!node.typeAnnotation) { + return { + ...currInfo, + ...collectMemoableInfoInType(node.typeAnnotation), + }; + } + return currInfo; +} + +export function collectMemoableInfoInParameter(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isEtsParameterExpression(node)) { + return currInfo; + } + currInfo = { + ...currInfo, + ...hasMemoableAnnotation(node), + }; + if (!!node.type) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInType(node.type), + }; + } + if (!!node.initializer) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInArrowFunction(node.initializer), + }; + } + return currInfo; +} + +export function collectMemoableInfoInVariableDeclarator(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isVariableDeclarator(node)) { + return currInfo; + } + if (!!node.name.typeAnnotation) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInType(node.name.typeAnnotation), + }; + } + if (!!node.initializer && arkts.isArrowFunctionExpression(node.initializer)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInArrowFunction(node.initializer), + }; + } + if (!!node.parent && arkts.isVariableDeclaration(node.parent)) { + currInfo = { + ...currInfo, + ...hasMemoableAnnotation(node.parent), + }; + } + const decl = arkts.getDecl(node.name); + if (!decl) { + return currInfo; + } + if (arkts.isMethodDefinition(decl)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInScriptFunction(decl.scriptFunction), + }; + } else if (arkts.isClassProperty(decl)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInClassProperty(decl), + }; + } + return currInfo; +} + +export function collectMemoableInfoInProperty(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + const property = node as arkts.Property; + const hasProperType = !!property.value && arkts.isArrowFunctionExpression(property.value); + return { ...currInfo, hasMemo: true, hasProperType }; + } + if (!arkts.isProperty(node) || !node.key || !arkts.isIdentifier(node.key)) { + return currInfo; + } + const decl = arkts.getDecl(node.key); + if (!decl || !arkts.isMethodDefinition(decl)) { + return currInfo; + } + const hasReceiver = decl.scriptFunction.hasReceiver; + const isSetter = decl.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET; + const isGetter = decl.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + let newInfo: MemoableInfo = {}; + if (isSetter && decl.scriptFunction.params.length > 0) { + if (hasReceiver && decl.scriptFunction.params.length === 2) { + newInfo = collectMemoableInfoInParameter(decl.scriptFunction.params.at(1)!); + } else { + newInfo = collectMemoableInfoInParameter(decl.scriptFunction.params.at(0)!); + } + } else if (isGetter) { + newInfo = collectMemoableInfoInFunctionReturnType(decl.scriptFunction); + } + currInfo = { ...currInfo, ...collectMemoableInfoInScriptFunction(decl.scriptFunction), ...newInfo }; + currInfo.hasProperType = false; + if (!!node.value && arkts.isArrowFunctionExpression(node.value)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInScriptFunction(node.value.scriptFunction), + }; + } + return currInfo; +} + +export function collectMemoableInfoInClassProperty(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isClassProperty(node)) { + return currInfo; + } + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + if (!!node.typeAnnotation) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInType(node.typeAnnotation), + }; + } + if (!!node.value) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInArrowFunction(node.value), + }; + } + return currInfo; +} + +export function collectMemoableInfoInArrowFunction(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isArrowFunctionExpression(node)) { + return currInfo; + } + currInfo.hasProperType = true; + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + if (!!node.scriptFunction) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInScriptFunction(node.scriptFunction), + }; + } + if (!!node.parent && arkts.isAssignmentExpression(node.parent) && !!node.parent.left) { + const expr = arkts.isMemberExpression(node.parent.left) ? node.parent.left.property : node.parent.left; + const decl = arkts.getDecl(expr); + if (!decl) { + return currInfo; + } + if (arkts.isClassProperty(decl)) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInClassProperty(decl), + }; + } + } + return currInfo; +} + +export function collectMemoableInfoInScriptFunction(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (arkts.NodeCache.getInstance().has(node)) { + return { ...currInfo, hasMemo: true, hasProperType: true }; + } + if (!arkts.isScriptFunction(node)) { + return currInfo; + } + currInfo.hasProperType = true; + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + return currInfo; +} + +export function collectMemoableInfoInMethod(node: arkts.MethodDefinition): MemoableInfo { + const hasReceiver = node.scriptFunction.hasReceiver; + const isSetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET; + const isGetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + let info: MemoableInfo = {}; + if (isSetter && node.scriptFunction.params.length > 0) { + if (hasReceiver && node.scriptFunction.params.length === 2) { + info = collectMemoableInfoInParameter(node.scriptFunction.params.at(1)!); + } else { + info = collectMemoableInfoInParameter(node.scriptFunction.params.at(0)!); + } + } else if (isGetter) { + info = collectMemoableInfoInFunctionReturnType(node.scriptFunction); + } + return collectMemoableInfoInScriptFunction(node.scriptFunction, info); +} + +export function collectMemoableInfoInType(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + return { + ...currInfo, + ...collectMemoableInfoInFunctionType(node), + ...collectMemoableInfoInUnionType(node), + ...collectMemoableInfoInTypeReference(node), + }; +} + +export function collectMemoableInfoInFunctionReturnType(node: arkts.ScriptFunction): MemoableInfo { + if (!!node.returnTypeAnnotation) { + let memoableInfo: MemoableInfo; + if (arkts.NodeCache.getInstance().has(node.returnTypeAnnotation)) { + memoableInfo = { hasMemo: true, hasProperType: true }; + } else { + memoableInfo = collectMemoableInfoInType(node.returnTypeAnnotation); + } + if ((memoableInfo.hasMemo || memoableInfo.hasBuilder) && memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(node.returnTypeAnnotation); + } + return memoableInfo; + } + return {}; +} + +export function collectGensymDeclarator(declarator: arkts.VariableDeclarator, info: MemoableInfo): void { + if (!info.hasMemo && !info.hasBuilder) { + return; + } + arkts.NodeCache.getInstance().collect(declarator); + const initializer = declarator.initializer; + if (!initializer || !arkts.isConditionalExpression(initializer)) { + return; + } + const alternate = initializer.alternate; + if (!alternate) { + return; + } + let arrowFunc: arkts.ArrowFunctionExpression | undefined; + if (arkts.isTSAsExpression(alternate) && !!alternate.expr && arkts.isArrowFunctionExpression(alternate.expr)) { + arrowFunc = alternate.expr; + } else if (arkts.isArrowFunctionExpression(alternate)) { + arrowFunc = alternate; + } + if (!!arrowFunc) { + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(arrowFunc.scriptFunction); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams(arrowFunc.scriptFunction); + if (!!arrowFunc.scriptFunction.body && arkts.isBlockStatement(arrowFunc.scriptFunction.body)) { + collectMemoScriptFunctionBody( + arrowFunc.scriptFunction.body, + returnMemoableInfo, + paramMemoableInfoMap, + gensymCount + ); + } + } +} + +export function collectMemoableInfoMapInFunctionParams( + node: arkts.ScriptFunction, + shouldCollectParameter: boolean = true +): [Map, number] { + const hasReceiver = node.hasReceiver; + const paramMap: Map = new Map(); + let gensymCount: number = 0; + node.params.slice(hasReceiver ? 1 : 0).forEach((p) => { + const info = collectMemoableInfoInFunctionParam(node, p, gensymCount, shouldCollectParameter); + gensymCount = info.gensymCount; + info.peers.forEach((peer) => paramMap.set(peer, info.memoableInfo)); + }); + return [paramMap, gensymCount]; +} + +interface FunctionParamCollectInfo { + peers: arkts.AstNode['peer'][]; + gensymCount: number; + memoableInfo: MemoableInfo; +} + +function collectMemoableInfoInFunctionParam( + node: arkts.ScriptFunction, + param: arkts.Expression, + gensymCount: number, + shouldCollectParameter: boolean = true +): FunctionParamCollectInfo { + const peers: arkts.AstNode['peer'][] = []; + let memoableInfo: MemoableInfo; + const _param = param as arkts.ETSParameterExpression; + if (arkts.NodeCache.getInstance().has(_param)) { + const metadata = arkts.NodeCache.getInstance().get(_param)!.metadata ?? {}; + const { hasMemoSkip } = metadata; + memoableInfo = { hasMemo: true, hasMemoSkip, hasProperType: true }; + } else { + memoableInfo = collectMemoableInfoInParameter(_param); + } + if (_param.identifier.name.startsWith(GenSymPrefix.INTRINSIC) && !!node.body && arkts.isBlockStatement(node.body)) { + const declaration = node.body.statements.at(gensymCount); + if (!!declaration && arkts.isVariableDeclaration(declaration) && declaration.declarators.length > 0) { + const declarator = declaration.declarators[0]; + collectGensymDeclarator(declarator, memoableInfo); + if (!memoableInfo.hasMemoSkip && shouldCollectParameter) { + peers.push(declarator.name.peer); + } + gensymCount++; + } + } + if (checkIsMemoFromMemoableInfo(memoableInfo)) { + arkts.NodeCache.getInstance().collect(_param, { hasMemoSkip: memoableInfo.hasMemoSkip }); + } + if (!memoableInfo.hasMemoSkip && shouldCollectParameter) { + peers.push(_param.identifier.peer); + } + return { peers, memoableInfo, gensymCount }; +} + +/** + * Collect `@memo` annotated `arkts.TypeNode` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.TypeNode` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromTypeAnnotation( + typeAnnotation: arkts.AstNode | undefined +): typeAnnotation is arkts.ETSFunctionType { + if (!typeAnnotation) { + return false; + } + const memoableInfo = collectMemoableInfoInType(typeAnnotation); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(typeAnnotation); + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.Property` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.Property` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromProperty(property: arkts.AstNode): property is arkts.Property { + const memoableInfo = collectMemoableInfoInProperty(property); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(property); + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.ClassProperty` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.ClassProperty` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromClassProperty(property: arkts.AstNode): property is arkts.ClassProperty { + const memoableInfo = collectMemoableInfoInClassProperty(property); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(property); + } + return ( + (!!memoableInfo.hasBuilder || !!memoableInfo.hasBuilderParam) && + !memoableInfo.hasMemo && + !!memoableInfo.hasProperType + ); +} + +/** + * Collect `@memo` annotated `arkts.ETSParameterExpression` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.ETSParameterExpression` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromParameter(param: arkts.AstNode | undefined): param is arkts.ETSParameterExpression { + if (!param) { + return false; + } + const memoableInfo = collectMemoableInfoInParameter(param); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(param, { hasMemoSkip: memoableInfo.hasMemoSkip }); + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.ArrowFunctionExpression` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.ArrowFunctionExpression` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromArrowFunction(node: arkts.AstNode): node is arkts.ArrowFunctionExpression { + if (!arkts.isArrowFunctionExpression(node)) { + return false; + } + const memoableInfo = collectMemoableInfoInArrowFunction(node); + const { hasMemoEntry, hasMemoIntrinsic } = memoableInfo; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(node.scriptFunction); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams( + node.scriptFunction, + !hasMemoEntry && !hasMemoIntrinsic + ); + const isMemoReturnType = checkIsMemoFromMemoableInfo(returnMemoableInfo); + if (isMemoReturnType) { + arkts.NodeCache.getInstance().collect(node.scriptFunction.returnTypeAnnotation!); + } + const isMemo = checkIsMemoFromMemoableInfo(memoableInfo); + if (isMemo && !arkts.NodeCache.getInstance().has(node)) { + arkts.NodeCache.getInstance().collect(node, { hasMemoEntry, hasMemoIntrinsic }); + if (!!node.scriptFunction.body && arkts.isBlockStatement(node.scriptFunction.body)) { + const disableCollectReturn = hasMemoEntry || hasMemoIntrinsic; + collectMemoScriptFunctionBody( + node.scriptFunction.body, + returnMemoableInfo, + paramMemoableInfoMap, + gensymCount, + disableCollectReturn + ); + } + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.TSTypeAliasDeclaration` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.TSTypeAliasDeclaration` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromTypeAlias(node: arkts.AstNode): node is arkts.TSTypeAliasDeclaration { + const memoableInfo = collectMemoableInfoInTypeAlias(node); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(node); + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.MethodDefinition` node. And find whether it can be `@memo` annotated. + * + * @param node `arkts.MethodDefinition` node. + * @returns true if it is not `@memo` annotated but can add `@memo` to it. + */ +export function findCanAddMemoFromMethod(node: arkts.AstNode): node is arkts.MethodDefinition { + if (!arkts.isMethodDefinition(node)) { + return false; + } + const memoableInfo = collectMemoableInfoInMethod(node); + const { hasMemoEntry, hasMemoIntrinsic } = memoableInfo; + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(node.scriptFunction); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams( + node.scriptFunction, + !hasMemoEntry && !hasMemoIntrinsic + ); + const isMemo = checkIsMemoFromMemoableInfo(memoableInfo); + const isMemoReturnType = checkIsMemoFromMemoableInfo(returnMemoableInfo); + if (isMemoReturnType) { + arkts.NodeCache.getInstance().collect(node.scriptFunction.returnTypeAnnotation!); + } + if (isMemo && !arkts.NodeCache.getInstance().has(node)) { + const metadata = collectMetadataInMethod(node); + arkts.NodeCache.getInstance().collect(node, { + ...metadata, + hasMemoEntry, + hasMemoIntrinsic, + }); + if (!!node.scriptFunction.body && arkts.isBlockStatement(node.scriptFunction.body)) { + const disableCollectReturn = hasMemoEntry || hasMemoIntrinsic; + collectMemoScriptFunctionBody( + node.scriptFunction.body, + returnMemoableInfo, + paramMemoableInfoMap, + gensymCount, + disableCollectReturn + ); + } + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo && !!memoableInfo.hasProperType; +} + +/** + * Collect `@memo` annotated `arkts.CallExpression` node from corresponding declared method, + * as well as collect each `@memo` annotated argument from corresponding declared method parameter. + * + * @param node `arkts.CallExpression` node. + */ +export function collectMemoFromCallExpression(node: arkts.CallExpression): void { + if (arkts.NodeCache.getInstance().has(node)) { + return; + } + const expr = findIdentifierFromCallee(node.expression); + const decl = (expr && getDeclResolveAlias(expr)) ?? node.expression; + if (!decl) { + return; + } + if (arkts.NodeCache.getInstance().has(decl)) { + arkts.NodeCache.getInstance().collect(node); + } + if (arkts.isMethodDefinition(decl)) { + collectCallWithDeclaredMethod(node, decl); + } +} + +export function collectCallWithDeclaredMethod(node: arkts.CallExpression, decl: arkts.MethodDefinition): void { + const hasReceiver = decl.scriptFunction.hasReceiver; + const params = decl.scriptFunction.params; + const args = node.arguments; + const hasRestParameter = decl.scriptFunction.hasRestParameter; + const isTrailingCall = node.isTrailingCall; + const options = { hasRestParameter, isTrailingCall }; + forEachArgWithParam(args, params, collectCallArgsWithMethodParams, options); + if (arkts.NodeCache.getInstance().has(decl)) { + const { hasMemoEntry, hasMemoIntrinsic } = arkts.NodeCache.getInstance().get(decl)!.metadata ?? {}; + arkts.NodeCache.getInstance().collect(node, { hasReceiver, hasMemoEntry, hasMemoIntrinsic }); + } else { + const memoableInfo = collectMemoableInfoInScriptFunction(decl.scriptFunction); + if (checkIsMemoFromMemoableInfo(memoableInfo, true)) { + const { hasMemoEntry, hasMemoIntrinsic } = memoableInfo; + arkts.NodeCache.getInstance().collect(node, { hasReceiver, hasMemoEntry, hasMemoIntrinsic }); + } + } +} + +export function collectCallArgsWithMethodParams(arg: arkts.Expression | undefined, param: arkts.Expression): void { + if (!arg) { + return; + } + let info: MemoableInfo; + if (arkts.NodeCache.getInstance().has(param)) { + info = { hasMemo: true, hasProperType: true }; + } else { + info = collectMemoableInfoInParameter(param); + } + if (checkIsMemoFromMemoableInfo(info) && arkts.isArrowFunctionExpression(arg)) { + arkts.NodeCache.getInstance().collect(arg); + const returnMemoableInfo = collectMemoableInfoInFunctionReturnType(arg.scriptFunction); + const [paramMemoableInfoMap, gensymCount] = collectMemoableInfoMapInFunctionParams(arg.scriptFunction); + if (!!arg.scriptFunction.body && arkts.isBlockStatement(arg.scriptFunction.body)) { + collectMemoScriptFunctionBody( + arg.scriptFunction.body, + returnMemoableInfo, + paramMemoableInfoMap, + gensymCount + ); + } + } +} + +export function findIdentifierFromCallee(callee: arkts.AstNode | undefined): arkts.Identifier | undefined { + if (!callee) { + return undefined; + } + if (arkts.isIdentifier(callee)) { + return callee; + } + if (arkts.isMemberExpression(callee)) { + return findIdentifierFromCallee(callee.property); + } + return undefined; +} + +export function collectMemoScriptFunctionBody( + body: arkts.BlockStatement, + returnMemoableInfo: MemoableInfo, + paramMemoableInfoMap: Map, + gensymCount: number, + disableCollectReturn?: boolean +): void { + const collector = new MemoFunctionCollector(); + body.statements.forEach((st, index) => { + if (index < gensymCount) { + return; + } + if (disableCollectReturn) { + collector.disableCollectReturn(); + } + collector.registerReturnInfo(returnMemoableInfo).registerParamInfoMap(paramMemoableInfoMap).visitor(st); + collector.reset(); + }); +} + +export function collectMetadataInMethod(node: arkts.MethodDefinition): arkts.AstNodeCacheValue['metadata'] { + const callName = node.name.name; + const hasReceiver = node.scriptFunction.hasReceiver; + const isSetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET; + const isGetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + return { callName, hasReceiver, isSetter, isGetter }; +} + +export function checkIsMemoFromMemoableInfo(info: MemoableInfo, ignoreType: boolean = false): boolean { + return ( + (!!info.hasMemo || !!info.hasMemoIntrinsic || !!info.hasMemoEntry || !!info.hasBuilder) && + (ignoreType || !!info.hasProperType) + ); +} + +export function getDeclResolveAlias(node: arkts.AstNode): arkts.AstNode | undefined { + const decl = arkts.getDecl(node); + if (!!decl && !!decl.parent && arkts.isIdentifier(decl) && arkts.isVariableDeclarator(decl.parent)) { + if (!!decl.parent.initializer && arkts.isIdentifier(decl.parent.initializer)) { + return getDeclResolveAlias(decl.parent.initializer); + } + if (!!decl.parent.initializer && arkts.isMemberExpression(decl.parent.initializer)) { + return getDeclResolveAlias(decl.parent.initializer.property); + } + } + return decl; +} + +export function parametersBlockHasReceiver(params: readonly arkts.Expression[]): boolean { + return params.length > 0 && arkts.isEtsParameterExpression(params[0]) && isThisParam(params[0]); +} + +export function parametrizedNodeHasReceiver(node: arkts.ScriptFunction | arkts.ETSFunctionType | undefined): boolean { + if (node === undefined) { + return false; + } + return parametersBlockHasReceiver(node.params); +} + +function isThisParam(node: arkts.Expression | undefined): boolean { + if (node === undefined || !arkts.isEtsParameterExpression(node)) { + return false; + } + return node.identifier?.isReceiver ?? false; +} diff --git a/arkui-plugins/common/arkts-utils.ts b/arkui-plugins/common/arkts-utils.ts index bf056c788..59016f2fd 100644 --- a/arkui-plugins/common/arkts-utils.ts +++ b/arkui-plugins/common/arkts-utils.ts @@ -14,6 +14,8 @@ */ import * as arkts from '@koalaui/libarkts'; +import { DeclarationCollector } from './declaration-collector'; +import { ARKUI_IMPORT_PREFIX_NAMES, DecoratorNames } from './predefines'; /** @@ -51,6 +53,28 @@ export function isAnnotation(node: arkts.AnnotationUsage, annoName: string) { return node.expr !== undefined && arkts.isIdentifier(node.expr) && node.expr.name === annoName; } +export function isDecoratorAnnotation( + anno: arkts.AnnotationUsage, + decoratorName: DecoratorNames, + ignoreDecl?: boolean +): boolean { + if (!(!!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === decoratorName)) { + return false; + } + if (!ignoreDecl) { + const decl = arkts.getDecl(anno.expr); + if (!decl) { + return false; + } + const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; + if (!moduleName || !matchPrefix(ARKUI_IMPORT_PREFIX_NAMES, moduleName)) { + return false; + } + DeclarationCollector.getInstance().collect(decl); + } + return true; +} + export function removeAnnotationByName( annotations: readonly arkts.AnnotationUsage[], annoName: string @@ -111,3 +135,39 @@ export function moveToFront(arr: T[], idx: number): T[] { const [item] = copy.splice(idx, 1); return [item, ...copy]; } + +/** + * Performs the specified action for each argument in a `arkts.CallExpression`'s arguments array + * paired with corresponding parameter from the function declaration node. + * + * @param args An arguments array from a `arkts.CallExpression` node. + * @param params A parameters array from a function declaration node. + * @param callbackFn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array. + * @param options Additional options field that accepts special conditions of calls and function, used for pairing arguments with parameters. + */ +export function forEachArgWithParam( + args: readonly arkts.Expression[], + params: readonly arkts.Expression[], + callbackFn: (arg: arkts.Expression | undefined, param: arkts.Expression, index?: number) => void, + options?: { isTrailingCall?: boolean; hasReceiver?: boolean; hasRestParameter?: boolean } +): void { + const argLen: number = args.length; + const paramLen: number = params.length; + if (argLen === 0 || paramLen === 0) { + return; + } + const hasRestParam: boolean = !!options?.hasRestParameter; + const isTrailingCall: boolean = !!options?.isTrailingCall; + const maxLen = hasRestParam ? argLen : paramLen; + let index: number = 0; + while (index < maxLen - 1) { + const param = params.at(index) ?? params.at(paramLen - 1)!; + const argument = isTrailingCall && index >= argLen - 1 ? undefined : args.at(index); + callbackFn(argument, param, index); + index++; + } + const lastParam = params.at(paramLen - 1)!; + const lastIndex = isTrailingCall ? argLen - 1 : maxLen - 1; + const lastArg = args.at(lastIndex); + callbackFn(lastArg, lastParam, maxLen - 1); +} diff --git a/arkui-plugins/common/predefines.ts b/arkui-plugins/common/predefines.ts index cf481fd9b..f9608f048 100644 --- a/arkui-plugins/common/predefines.ts +++ b/arkui-plugins/common/predefines.ts @@ -279,4 +279,9 @@ export const IMPORT_SOURCE_MAP_V2: Map = new Map export enum GetSetTypes { GET = 'get', SET = 'set', +} + +export enum GenSymPrefix { + INTRINSIC = 'gensym%%', + UI = 'gensym__' } \ No newline at end of file diff --git a/arkui-plugins/jest-test.config.js b/arkui-plugins/jest-test.config.js index 98b801f70..cbca1f13e 100644 --- a/arkui-plugins/jest-test.config.js +++ b/arkui-plugins/jest-test.config.js @@ -30,6 +30,7 @@ module.exports = { moduleFileExtensions: ['ts', 'js', 'json', 'node'], coverageDirectory: './test/report', collectCoverageFrom: [ + 'collectors/**', 'common/**', 'memo-plugins/**', 'ui-plugins/**' @@ -46,13 +47,13 @@ module.exports = { 'ui-plugins/printer-transformer.ts', 'ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts', 'ui-plugins/entry-translators/entry-transformer.ts', - 'ui-plugins/struct-translators/struct-transformer.ts' + 'ui-plugins/struct-translators/struct-transformer.ts', ], verbose: true, globals: { SDK_PATH: sdkPath, PANDA_SDK_PATH: pandaSdkPath, API_PATH: apiPath, - KIT_PATH: kitPath + KIT_PATH: kitPath, }, }; diff --git a/arkui-plugins/memo-plugins/function-transformer.ts b/arkui-plugins/memo-plugins/function-transformer.ts index c48edac98..4f2c55f1f 100644 --- a/arkui-plugins/memo-plugins/function-transformer.ts +++ b/arkui-plugins/memo-plugins/function-transformer.ts @@ -55,6 +55,7 @@ import { ReturnTransformer } from './return-transformer'; import { SignatureTransformer } from './signature-transformer'; import { moveToFront } from '../common/arkts-utils'; import { InternalsTransformer } from './internal-transformer'; +import { CachedMetadata, rewriteByType } from './memo-cache-factory'; interface ScopeInfo extends MemoInfo { regardAsSameScope?: boolean; @@ -66,6 +67,7 @@ export interface FunctionTransformerOptions extends VisitorOptions { returnTransformer: ReturnTransformer; signatureTransformer: SignatureTransformer; internalsTransformer?: InternalsTransformer; + useCache?: boolean; } export class FunctionTransformer extends AbstractVisitor { @@ -74,6 +76,7 @@ export class FunctionTransformer extends AbstractVisitor { private readonly returnTransformer: ReturnTransformer; private readonly signatureTransformer: SignatureTransformer; private readonly internalsTransformer?: InternalsTransformer; + private readonly useCache: boolean; /* Tracking whether should import `__memo_context_type` and `__memo_id_type` */ private modified = false; @@ -85,6 +88,7 @@ export class FunctionTransformer extends AbstractVisitor { this.returnTransformer = options.returnTransformer; this.signatureTransformer = options.signatureTransformer; this.internalsTransformer = options.internalsTransformer; + this.useCache = !!options.useCache; } private scopes: ScopeInfo[] = []; @@ -677,7 +681,26 @@ export class FunctionTransformer extends AbstractVisitor { ); } + private visitorWithCache(beforeChildren: arkts.AstNode): arkts.AstNode { + const node = this.visitEachChild(beforeChildren); + if (arkts.NodeCache.getInstance().has(node)) { + const value = arkts.NodeCache.getInstance().get(node)!; + if (rewriteByType.has(value.type)) { + this.modified = true; + const metadata: CachedMetadata = { ...value.metadata, internalsTransformer: this.internalsTransformer }; + return rewriteByType.get(value.type)!(node, metadata); + } + } + if (arkts.isEtsScript(node) && this.modified) { + factory.createContextTypesImportDeclaration(this.program); + } + return node; + } + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { + if (this.useCache) { + return this.visitorWithCache(beforeChildren); + } this.enter(beforeChildren); const node = this.visitEachChild(beforeChildren); this.exit(beforeChildren); diff --git a/arkui-plugins/memo-plugins/index.ts b/arkui-plugins/memo-plugins/index.ts index 9d5c2ed4d..5c19bc841 100644 --- a/arkui-plugins/memo-plugins/index.ts +++ b/arkui-plugins/memo-plugins/index.ts @@ -99,13 +99,17 @@ function checkedProgramVisit( const parameterTransformer = new ParameterTransformer({ positionalIdTracker }); const returnTransformer = new ReturnTransformer(); const signatureTransformer = new SignatureTransformer(); - const internalsTransformer = new InternalsTransformer({ positionalIdTracker }); + let internalsTransformer: InternalsTransformer | undefined; + if (isFrameworkMode) { + internalsTransformer = new InternalsTransformer({ positionalIdTracker }); + } const functionTransformer = new FunctionTransformer({ positionalIdTracker, parameterTransformer, returnTransformer, signatureTransformer, internalsTransformer, + useCache: arkts.NodeCache.getInstance().isCollected(), }); const skipPrefixNames = isFrameworkMode ? EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK @@ -118,6 +122,7 @@ function checkedProgramVisit( pluginContext, }); program = programVisitor.programVisitor(program); + arkts.NodeCache.getInstance().clear(); } return program; } diff --git a/arkui-plugins/memo-plugins/memo-cache-factory.ts b/arkui-plugins/memo-plugins/memo-cache-factory.ts new file mode 100644 index 000000000..240ee283f --- /dev/null +++ b/arkui-plugins/memo-plugins/memo-cache-factory.ts @@ -0,0 +1,395 @@ +/* + * 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 './memo-factory'; +import { + filterMemoSkipParams, + findLocalReturnTypeFromTypeAnnotation, + findUnmemoizedScopeInFunctionBody, + fixGensymParams, + getFunctionParamsBeforeUnmemoized, + isVoidType, + mayAddLastReturn, + parametrizedNodeHasReceiver, + ParamInfo, + PositionalIdTracker, +} from './utils'; +import { InternalsTransformer } from './internal-transformer'; +import { GenSymPrefix } from '../common/predefines'; + +export interface CachedMetadata extends arkts.AstNodeCacheValueMetadata { + internalsTransformer?: InternalsTransformer; +} + +export class RewriteFactory { + static rewriteUnionType(node: arkts.ETSUnionType, metadata?: CachedMetadata): arkts.ETSUnionType { + return arkts.factory.updateUnionType( + node, + node.types.map((t) => { + if (arkts.isETSFunctionType(t)) { + return RewriteFactory.rewriteFunctionType(t, metadata); + } + if (arkts.isETSUnionType(t)) { + return RewriteFactory.rewriteUnionType(t, metadata); + } + return t; + }) + ); + } + + static rewriteFunctionType(node: arkts.ETSFunctionType, metadata?: CachedMetadata): arkts.ETSFunctionType { + const hasReceiver = metadata?.hasReceiver ?? parametrizedNodeHasReceiver(node); + return factory.updateFunctionTypeWithMemoParameters(node, hasReceiver); + } + + /** + * @internal + */ + static rewriteType(node: arkts.TypeNode | undefined, metadata?: CachedMetadata): arkts.TypeNode | undefined { + let newNodeType = node; + if (!!newNodeType && arkts.isETSFunctionType(newNodeType)) { + newNodeType = RewriteFactory.rewriteFunctionType(newNodeType, metadata); + } else if (!!newNodeType && arkts.isETSUnionType(newNodeType)) { + newNodeType = RewriteFactory.rewriteUnionType(newNodeType, metadata); + } + return newNodeType; + } + + static rewriteTypeAlias( + node: arkts.TSTypeAliasDeclaration, + metadata?: CachedMetadata + ): arkts.TSTypeAliasDeclaration { + if (!node.typeAnnotation) { + return node; + } + const newNodeType = RewriteFactory.rewriteType(node.typeAnnotation); + return arkts.factory.updateTSTypeAliasDeclaration(node, node.id, node.typeParams, newNodeType); + } + + static rewriteParameter( + node: arkts.ETSParameterExpression, + metadata?: CachedMetadata + ): arkts.ETSParameterExpression { + if (!node.type && !node.initializer) { + return node; + } + node.type = RewriteFactory.rewriteType(node.type as arkts.TypeNode); + return arkts.factory.updateParameterDeclaration(node, node.identifier, node.initializer); + } + + static rewriteProperty(node: arkts.Property, metadata?: CachedMetadata): arkts.Property { + if (!node.value || !arkts.isArrowFunctionExpression(node.value)) { + return node; + } + return arkts.factory.updateProperty(node, node.key, RewriteFactory.rewriteArrowFunction(node.value, metadata)); + } + + static rewriteClassProperty(node: arkts.ClassProperty, metadata?: CachedMetadata): arkts.ClassProperty { + const newType = !!node.typeAnnotation ? RewriteFactory.rewriteType(node.typeAnnotation) : undefined; + const newValue = + !!node.value && arkts.isArrowFunctionExpression(node.value) + ? RewriteFactory.rewriteArrowFunction(node.value, metadata) + : node.value; + return arkts.factory.updateClassProperty(node, node.key, newValue, newType, node.modifiers, node.isComputed); + } + + static rewriteArrowFunction( + node: arkts.ArrowFunctionExpression, + metadata?: arkts.AstNodeCacheValueMetadata, + expectReturn?: arkts.TypeNode + ): arkts.ArrowFunctionExpression { + return arkts.factory.updateArrowFunction( + node, + RewriteFactory.rewriteScriptFunction(node.scriptFunction, metadata, expectReturn) + ); + } + + /** + * @internal + */ + static rewriteScriptFunctionBody( + node: arkts.ScriptFunction, + body: arkts.BlockStatement, + positionalIdTracker: PositionalIdTracker, + callName?: string, + hasReceiver?: boolean, + expectReturn?: arkts.TypeNode + ): arkts.BlockStatement { + const _hasReceiver = hasReceiver ?? node.hasReceiver; + const _callName = callName ?? node.id?.name; + const parameters = getFunctionParamsBeforeUnmemoized(node.params, _hasReceiver); + const filteredParameters = filterMemoSkipParams(parameters); + const declaredParams: ParamInfo[] = filteredParameters.map((p) => { + const param = p as arkts.ETSParameterExpression; + return { ident: param.identifier, param }; + }); + const _gensymCount = fixGensymParams(declaredParams, body); + if (findUnmemoizedScopeInFunctionBody(body, _gensymCount)) { + return body; + } + const returnType = + node.returnTypeAnnotation ?? + expectReturn ?? + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID); + const _isVoidReturn = isVoidType(returnType); + const scopeDeclaration = factory.createScopeDeclaration( + returnType, + positionalIdTracker.id(_callName), + declaredParams.length + ); + const memoParametersDeclaration = node.params.length + ? factory.createMemoParameterDeclaration(declaredParams.map((p) => p.ident.name)) + : undefined; + const syntheticReturnStatement = factory.createSyntheticReturnStatement(false); + const unchangedCheck = factory.createIfStatementWithSyntheticReturnStatement( + syntheticReturnStatement, + _isVoidReturn + ); + const lastReturn = mayAddLastReturn(body) + ? factory.createWrappedReturnStatement(factory.createRecacheCall(), _isVoidReturn) + : undefined; + return arkts.factory.updateBlock(body, [ + ...body.statements.slice(0, _gensymCount), + scopeDeclaration, + ...(!!memoParametersDeclaration ? [memoParametersDeclaration] : []), + unchangedCheck, + ...body.statements.slice(_gensymCount), + ...(!!lastReturn ? [lastReturn] : []), + ]); + } + + static rewriteScriptFunction( + node: arkts.ScriptFunction, + metadata?: CachedMetadata, + expectReturn?: arkts.TypeNode + ): arkts.ScriptFunction { + const _callName = metadata?.callName; + const _hasReceiver = metadata?.hasReceiver ?? node.hasReceiver; + const _isSetter = !!metadata?.isSetter; + const _isGetter = !!metadata?.isGetter; + const _hasMemoEntry = !!metadata?.hasMemoEntry; + const _hasMemoIntrinsic = !!metadata?.hasMemoIntrinsic; + const _internalsTransformer = metadata?.internalsTransformer; + const _isDecl = arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); + const newParams: readonly arkts.Expression[] = prepareRewriteScriptFunctionParameters( + node, + _isSetter, + _isGetter, + _hasReceiver + ); + const newReturnType: arkts.TypeNode | undefined = prepareRewriteScriptFunctionReturnType( + node, + _isGetter, + _hasReceiver + ); + const newBody: arkts.AstNode | undefined = prepareRewriteScriptFunctionBody( + node, + expectReturn, + _internalsTransformer, + _isDecl, + _hasMemoEntry, + _hasMemoIntrinsic, + _callName, + _hasReceiver + ); + return arkts.factory.updateScriptFunction( + node, + newBody, + arkts.factory.createFunctionSignature(node.typeParams, newParams, newReturnType, _hasReceiver), + node.flags, + node.modifiers + ); + } + + static rewriteMethodDefinition(node: arkts.MethodDefinition, metadata?: CachedMetadata): arkts.MethodDefinition { + const isSetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET; + const isGetter = node.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + if (node.overloads.length > 0) { + node.setOverloads(node.overloads.map((o) => RewriteFactory.rewriteMethodDefinition(o, metadata))); + } + return arkts.factory.updateMethodDefinition( + node, + node.kind, + node.name, + RewriteFactory.rewriteScriptFunction(node.scriptFunction, { + callName: node.name.name, + ...metadata, + isSetter, + isGetter, + }), + node.modifiers, + false + ); + } + + static rewriteCallExpression(node: arkts.CallExpression, metadata?: CachedMetadata): arkts.CallExpression { + const _hasMemoEntry = !!metadata?.hasMemoEntry; + if (_hasMemoEntry) { + return node; + } + const _hasReceiver = metadata?.hasReceiver; + let _callName: string | undefined = metadata?.callName; + if (!!_callName && arkts.isIdentifier(node.expression)) { + _callName = node.expression.name; + } else if ( + !!_callName && + arkts.isMemberExpression(node.expression) && + arkts.isIdentifier(node.expression.property) + ) { + _callName = node.expression.property.name; + } + return factory.insertHiddenArgumentsToCall( + node, + PositionalIdTracker.getInstance(arkts.getFileName()).id(_callName), + _hasReceiver + ); + } + + static rewriteIdentifier( + node: arkts.Identifier, + metadata?: CachedMetadata + ): arkts.Identifier | arkts.MemberExpression { + if (!node.name.startsWith(GenSymPrefix.INTRINSIC) && !node.name.startsWith(GenSymPrefix.UI)) { + return factory.createMemoParameterAccess(node.name); + } + return node; + } + + static rewriteReturnStatement( + node: arkts.ReturnStatement, + metadata?: CachedMetadata + ): arkts.ReturnStatement | arkts.BlockStatement { + return factory.createWrappedReturnStatement(factory.createRecacheCall(node.argument), !node.argument); + } + + static rewriteVariableDeclarator( + node: arkts.VariableDeclarator, + metadata?: CachedMetadata + ): arkts.VariableDeclarator { + const expectReturnType = findLocalReturnTypeFromTypeAnnotation(node.name.typeAnnotation); + const variableType = RewriteFactory.rewriteType(node.name.typeAnnotation); + let initializer = node.initializer; + if (!!initializer && arkts.isConditionalExpression(initializer) && !!initializer.alternate) { + let alternate = initializer.alternate; + if (arkts.isTSAsExpression(alternate)) { + alternate = arkts.factory.updateTSAsExpression( + alternate, + !!alternate.expr && arkts.isArrowFunctionExpression(alternate.expr) + ? RewriteFactory.rewriteArrowFunction(alternate.expr, metadata, expectReturnType) + : alternate.expr, + RewriteFactory.rewriteType(alternate.typeAnnotation), + alternate.isConst + ); + } else if (arkts.isArrowFunctionExpression(alternate)) { + alternate = RewriteFactory.rewriteArrowFunction(alternate, metadata, expectReturnType); + } + initializer = arkts.factory.updateConditionalExpression( + initializer, + initializer.test, + initializer.consequent, + alternate + ); + } else if (!!initializer && arkts.isArrowFunctionExpression(initializer)) { + initializer = RewriteFactory.rewriteArrowFunction(initializer, metadata, expectReturnType); + } + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + arkts.factory.updateIdentifier(node.name, node.name.name, variableType), + initializer + ); + } +} + +function prepareRewriteScriptFunctionParameters( + node: arkts.ScriptFunction, + isSetter?: boolean, + isGetter?: boolean, + hasReceiver?: boolean +): readonly arkts.Expression[] { + let newParams: readonly arkts.Expression[] = node.params; + if (!isSetter && !isGetter) { + newParams = factory.createHiddenParameterIfNotAdded(node.params, node.hasReceiver); + } else if (isSetter && node.params.length > 0) { + if (hasReceiver && node.params.length === 2) { + newParams = [ + node.params.at(0)!, + RewriteFactory.rewriteParameter(node.params.at(1)! as arkts.ETSParameterExpression), + ]; + } else { + newParams = [RewriteFactory.rewriteParameter(node.params.at(0)! as arkts.ETSParameterExpression)]; + } + } + return newParams; +} + +function prepareRewriteScriptFunctionReturnType( + node: arkts.ScriptFunction, + isGetter?: boolean, + hasReceiver?: boolean +): arkts.TypeNode | undefined { + let newReturnType: arkts.TypeNode | undefined = node.returnTypeAnnotation; + if (!!node.returnTypeAnnotation && isGetter) { + newReturnType = RewriteFactory.rewriteType(node.returnTypeAnnotation, { hasReceiver }); + } + return newReturnType; +} + +function prepareRewriteScriptFunctionBody( + node: arkts.ScriptFunction, + expectReturn?: arkts.TypeNode, + internalsTransformer?: InternalsTransformer, + isDecl?: boolean, + hasMemoEntry?: boolean, + hasMemoIntrinsic?: boolean, + callName?: string, + hasReceiver?: boolean +): arkts.AstNode | undefined { + if (isDecl || !node.body || !arkts.isBlockStatement(node.body)) { + return node.body; + } + + let newBody: arkts.AstNode | undefined; + const positionalIdTracker = PositionalIdTracker.getInstance(arkts.getFileName()); + newBody = internalsTransformer?.visitor(node.body) ?? node.body; + if (!hasMemoEntry && !hasMemoIntrinsic) { + newBody = RewriteFactory.rewriteScriptFunctionBody( + node, + newBody as arkts.BlockStatement, + positionalIdTracker, + callName, + hasReceiver, + expectReturn + ); + } + return newBody; +} + +export const rewriteByType = new Map arkts.AstNode>([ + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNION_TYPE, RewriteFactory.rewriteUnionType], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_FUNCTION_TYPE, RewriteFactory.rewriteFunctionType], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION, RewriteFactory.rewriteTypeAlias], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION, RewriteFactory.rewriteParameter], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY, RewriteFactory.rewriteClassProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION, RewriteFactory.rewriteArrowFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_SCRIPT_FUNCTION, RewriteFactory.rewriteScriptFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION, RewriteFactory.rewriteMethodDefinition], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CALL_EXPRESSION, RewriteFactory.rewriteCallExpression], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_IDENTIFIER, RewriteFactory.rewriteIdentifier], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_RETURN_STATEMENT, RewriteFactory.rewriteReturnStatement], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_VARIABLE_DECLARATOR, RewriteFactory.rewriteVariableDeclarator], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_PROPERTY, RewriteFactory.rewriteProperty], +]); diff --git a/arkui-plugins/memo-plugins/memo-factory.ts b/arkui-plugins/memo-plugins/memo-factory.ts index 37eed959b..a9d43b591 100644 --- a/arkui-plugins/memo-plugins/memo-factory.ts +++ b/arkui-plugins/memo-plugins/memo-factory.ts @@ -17,12 +17,12 @@ import * as arkts from '@koalaui/libarkts'; import { fixGensymParams, buildeParamInfos, - isUnmemoizedInFunction, + isUnmemoizedInFunctionParams, mayAddLastReturn, ParamInfo, ReturnTypeInfo, RuntimeNames, - parametrizedNodeHasReceiver + parametrizedNodeHasReceiver, } from './utils'; import { moveToFront } from '../common/arkts-utils'; @@ -88,7 +88,7 @@ export class factory { hasReceiver: boolean = false ): readonly arkts.Expression[] { const _params = params ?? []; - if (isUnmemoizedInFunction(_params)) { + if (isUnmemoizedInFunctionParams(_params)) { return _params; } let newParams: arkts.Expression[] = [...factory.createHiddenParameters(), ..._params]; @@ -97,12 +97,15 @@ export class factory { } return newParams; } - static updateFunctionTypeWithMemoParameters(type: arkts.ETSFunctionType): arkts.ETSFunctionType { + static updateFunctionTypeWithMemoParameters( + type: arkts.ETSFunctionType, + hasReceiver: boolean = false + ): arkts.ETSFunctionType { return arkts.factory.updateFunctionType( type, arkts.factory.createFunctionSignature( undefined, - factory.createHiddenParameterIfNotAdded(type.params), + factory.createHiddenParameterIfNotAdded(type.params, hasReceiver), type.returnType, false ), @@ -298,6 +301,18 @@ export class factory { returnStatement ); } + static createWrappedReturnStatement( + argument: arkts.Expression, + isReturnVoid: boolean + ): arkts.ReturnStatement | arkts.BlockStatement { + if (!isReturnVoid) { + return arkts.factory.createReturnStatement(argument); + } + return arkts.factory.createBlock([ + arkts.factory.createExpressionStatement(argument), + arkts.factory.createReturnStatement(), + ]); + } // Compute static createLambdaWrapper(node: arkts.Expression): arkts.ArrowFunctionExpression { @@ -389,11 +404,13 @@ export class factory { static insertHiddenArgumentsToCall( node: arkts.CallExpression, - hash: arkts.NumberLiteral | arkts.StringLiteral + hash: arkts.NumberLiteral | arkts.StringLiteral, + hasReceiver?: boolean ): arkts.CallExpression { - return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, [ - ...factory.createHiddenArguments(hash), - ...node.arguments, - ]); + let updatedArguments = [...factory.createHiddenArguments(hash), ...node.arguments]; + if (!!hasReceiver) { + updatedArguments = moveToFront(updatedArguments, 2); + } + return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, updatedArguments); } } diff --git a/arkui-plugins/memo-plugins/parameter-transformer.ts b/arkui-plugins/memo-plugins/parameter-transformer.ts index 061e5b80b..8aef0822c 100644 --- a/arkui-plugins/memo-plugins/parameter-transformer.ts +++ b/arkui-plugins/memo-plugins/parameter-transformer.ts @@ -22,8 +22,7 @@ import { findReturnTypeFromTypeAnnotation, isMemoETSParameterExpression, isMemoParametersDeclaration, - isUnmemoizedInFunction, - isVoidType, + isUnmemoizedInFunctionParams, MemoInfo, ParamInfo, PositionalIdTracker, @@ -140,7 +139,7 @@ export class ParameterTransformer extends AbstractVisitor { if (!scriptFunction.body || !arkts.isBlockStatement(scriptFunction.body)) { return initializer; } - if (isUnmemoizedInFunction(scriptFunction.params)) { + if (isUnmemoizedInFunctionParams(scriptFunction.params)) { return initializer; } const returnTypeInfo: ReturnTypeInfo = buildReturnTypeInfo( diff --git a/arkui-plugins/memo-plugins/utils.ts b/arkui-plugins/memo-plugins/utils.ts index 526b594d1..e3b415e7f 100644 --- a/arkui-plugins/memo-plugins/utils.ts +++ b/arkui-plugins/memo-plugins/utils.ts @@ -20,7 +20,7 @@ const UniqueId = common.UniqueId; export enum RuntimeNames { __CONTEXT = '__context', __ID = '__id', - __KEY = "__key", + __KEY = '__key', ANNOTATION_BUILDER = 'Builder', ANNOTATION = 'memo', ANNOTATION_ENTRY = 'memo_entry', @@ -43,7 +43,7 @@ export enum RuntimeNames { SCOPE = '__memo_scope', THIS = 'this', VALUE = 'value', - EQUAL_T = '=t' + EQUAL_T = '=t', } export interface ReturnTypeInfo { @@ -71,6 +71,15 @@ export class PositionalIdTracker { // Global for the whole program. static callCount: number = 0; + private static instance: PositionalIdTracker; + + static getInstance(fileName: string): PositionalIdTracker { + if (!this.instance) { + this.instance = new PositionalIdTracker(fileName); + } + return this.instance; + } + // Set `stable` to true if you want to have more predictable values. // For example for tests. // Don't use it in production! @@ -117,7 +126,9 @@ export type MemoAstNode = | arkts.VariableDeclaration; export function hasMemoAnnotation(node: T): boolean { - return node.annotations.some((it) => isMemoAnnotation(it, RuntimeNames.ANNOTATION) || isMemoAnnotation(it, RuntimeNames.ANNOTATION_BUILDER)); + return node.annotations.some( + (it) => isMemoAnnotation(it, RuntimeNames.ANNOTATION) || isMemoAnnotation(it, RuntimeNames.ANNOTATION_BUILDER) + ); } export function hasMemoIntrinsicAnnotation(node: T): boolean { @@ -453,7 +464,7 @@ export function findMemoFromTypeAnnotation(typeAnnotation: arkts.AstNode | undef if (arkts.isETSTypeReference(typeAnnotation) && !!typeAnnotation.part && !!typeAnnotation.part.name) { let decl: arkts.AstNode | undefined = arkts.getDecl(typeAnnotation.part.name); if (!decl || !arkts.isTSTypeAliasDeclaration(decl)) { - return false; + return false; } let isMemo: boolean = hasMemoAnnotation(decl) || hasMemoIntrinsicAnnotation(decl); if (!isMemo && !!decl.typeAnnotation) { @@ -492,6 +503,21 @@ export function findReturnTypeFromTypeAnnotation( return undefined; } +export function findLocalReturnTypeFromTypeAnnotation( + typeAnnotation: arkts.AstNode | undefined +): arkts.TypeNode | undefined { + if (!typeAnnotation) { + return undefined; + } + if (arkts.isETSFunctionType(typeAnnotation)) { + return typeAnnotation.returnType; + } + if (arkts.isETSUnionType(typeAnnotation)) { + return typeAnnotation.types.find((type) => arkts.isETSFunctionType(type))?.returnType; + } + return undefined; +} + export function getDeclResolveAlias(node: arkts.AstNode): arkts.AstNode | undefined { const decl = arkts.getDecl(node); if (!!decl && !!decl.parent && arkts.isIdentifier(decl) && arkts.isVariableDeclarator(decl.parent)) { @@ -506,11 +532,17 @@ export function getDeclResolveAlias(node: arkts.AstNode): arkts.AstNode | undefi } export function mayAddLastReturn(node: arkts.BlockStatement): boolean { - return ( - node.statements.length === 0 || - (!arkts.isReturnStatement(node.statements[node.statements.length - 1]) && - !arkts.isThrowStatement(node.statements[node.statements.length - 1])) - ); + if (node.statements.length === 0) { + return true; + } + const lastStatement = node.statements[node.statements.length - 1]; + if (arkts.isBlockStatement(lastStatement)) { + return mayAddLastReturn(lastStatement); + } + if (arkts.isReturnStatement(lastStatement) || arkts.isThrowStatement(lastStatement)) { + return false; + } + return true; } export function fixGensymParams(params: ParamInfo[], body: arkts.BlockStatement): number { @@ -534,16 +566,49 @@ export function fixGensymParams(params: ParamInfo[], body: arkts.BlockStatement) return gensymParamsCount; } -export function isUnmemoizedInFunction(params?: readonly arkts.Expression[]): boolean { +export function isMemoContextParamAdded(param: arkts.Expression): boolean { + return arkts.isEtsParameterExpression(param) && param.identifier.name === RuntimeNames.CONTEXT; +} + +export function isMemoIdParamAdded(param: arkts.Expression): boolean { + return arkts.isEtsParameterExpression(param) && param.identifier.name === RuntimeNames.ID; +} + +export function isUnmemoizedInFunctionParams(params?: readonly arkts.Expression[], hasReceiver?: boolean): boolean { const _params = params ?? []; - const first = _params.at(0); - const isContextAdded = - !!first && arkts.isEtsParameterExpression(first) && first.identifier.name === RuntimeNames.CONTEXT; - const second = _params.at(1); - const isIdAdded = !!second && arkts.isEtsParameterExpression(second) && second.identifier.name === RuntimeNames.ID; + const startIndex = hasReceiver ? 1 : 0; + const isContextAdded = !!_params.at(startIndex) && isMemoContextParamAdded(_params.at(startIndex)!); + const isIdAdded = !!_params.at(startIndex + 1) && isMemoIdParamAdded(_params.at(startIndex + 1)!); return isContextAdded && isIdAdded; } +export function getFunctionParamsBeforeUnmemoized( + params?: readonly arkts.Expression[], + hasReceiver?: boolean +): readonly arkts.Expression[] { + const _params = params ?? []; + if (isUnmemoizedInFunctionParams(_params, hasReceiver)) { + if (!!hasReceiver) { + return [_params.at(0)!, ..._params.slice(3)]; + } + return _params.slice(2); + } + return _params; +} + +export function findUnmemoizedScopeInFunctionBody(body: arkts.BlockStatement, gensymCount: number = 0): boolean { + const startIndex = gensymCount; + if (body.statements.length < startIndex + 1) { + return false; + } + const statement = body.statements.at(startIndex)!; + if (!arkts.isVariableDeclaration(statement)) { + return false; + } + const declarator = statement.declarators.at(0)!; + return declarator.name.name === RuntimeNames.SCOPE; +} + export function buildReturnTypeInfo( returnType: arkts.TypeNode | undefined, isMemo?: boolean, @@ -587,3 +652,7 @@ function isThisParam(node: arkts.Expression | undefined): boolean { } return node.identifier?.isReceiver ?? false; } + +export function filterMemoSkipParams(params: readonly arkts.Expression[]): readonly arkts.Expression[] { + return params.filter((p) => !hasMemoSkipAnnotation(p as arkts.ETSParameterExpression)); +} diff --git a/arkui-plugins/package.json b/arkui-plugins/package.json index 75420af81..929c12377 100644 --- a/arkui-plugins/package.json +++ b/arkui-plugins/package.json @@ -12,7 +12,7 @@ "compile:clean": "rm -rf lib", "clean:test": "rm -rf dist && rm -rf coverage", "prepare:test": "cp -rf $INIT_CWD/../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/koala-wrapper/build/native/ ../koala-wrapper/build/", - "test": "npm run clean:test && npm run prepare:test && LD_LIBRARY_PATH=$INIT_CWD/../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib jest --coverage --logHeapUsage --config ./jest-test.config.js --maxWorkers=25% --silent", + "test": "npm run clean:test && npm run prepare:test && LD_LIBRARY_PATH=$INIT_CWD/../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib jest --coverage --logHeapUsage --config ./jest-test.config.js --maxWorkers=50% --silent", "test:gdb": "LD_LIBRARY_PATH=$INIT_CWD/../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ets2panda/lib gdb --args node ./node_modules/.bin/jest --config ./jest-test.config.js", "compile": "npm run compile:clean && npm run compile:plugins && cp -rf ./lib $INIT_CWD/../../../../out/sdk/ohos-sdk/linux/ets/ets1.2/build-tools/ui-plugins/" }, diff --git a/arkui-plugins/test/demo/localtest/build_config_template.json b/arkui-plugins/test/demo/localtest/build_config_template.json index 798e9e377..1229a36b4 100755 --- a/arkui-plugins/test/demo/localtest/build_config_template.json +++ b/arkui-plugins/test/demo/localtest/build_config_template.json @@ -44,5 +44,6 @@ "colour": "green" }, "isBuildConfigModified": false, - "aceModuleJsonPath": "./demo/localtest/entry/src/main/module.json5" + "aceModuleJsonPath": "./demo/localtest/entry/src/main/module.json5", + "es2pandaMode": 2 } diff --git a/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets b/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets index b767256cc..556aba2b5 100644 --- a/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets +++ b/arkui-plugins/test/demo/mock/memo/lambdas/with-receiver.ets @@ -19,16 +19,23 @@ class Person { constructor() {} } +@memo function fullName(this: Person, @memo arg?: () => void): void { return; } class A {} +@memo type F1 = (this: A, @memo arg?: () => void) => void; + +@memo type F2 = (a: A, @memo arg?: () => void) => void; +@memo function foo(this: A, @memo arg?: () => void): void {} + +@memo function goo(a: A, @memo arg?: () => void): void {} @memo @@ -37,11 +44,7 @@ function goo(a: A, @memo arg?: () => void): void {} x.fullName(() => {}); let f1: F1 = foo; - f1 = goo; - let f2: F2 = goo; - f2 = foo; - f1 = f2; let a = new A(); a.f1(() => {}); diff --git a/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets b/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets index fb3868ecc..4e8053d50 100644 --- a/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets +++ b/arkui-plugins/test/demo/mock/memo/methods/non-void-method.ets @@ -17,6 +17,7 @@ import { memo, __memo_context_type, __memo_id_type } from "arkui.stateManagement @Retention({policy:"SOURCE"}) @interface memo_intrinsic {} @Retention({policy:"SOURCE"}) @interface memo_entry {} +@Retention({policy:"SOURCE"}) @interface memo_skip {} export declare function __context(): __memo_context_type export declare function __id(): __memo_id_type @@ -55,6 +56,12 @@ class Test { return entry() } } + + @memo memo_skip_args(arg1: number, @memo_skip arg2: string, @memo_skip arg3: @memo () => void): string { + let a = arg1; + arg3(); + return arg2; + } } class Use { diff --git a/arkui-plugins/test/ut/common/annotation.test.ts b/arkui-plugins/test/ut/common/annotation.test.ts index d7640d7af..663454255 100644 --- a/arkui-plugins/test/ut/common/annotation.test.ts +++ b/arkui-plugins/test/ut/common/annotation.test.ts @@ -17,13 +17,14 @@ import * as arkts from '@koalaui/libarkts'; import path from 'path'; import { PluginTester } from '../../utils/plugin-tester'; import { BuildConfig, PluginTestContext } from '../../utils/shared-types'; -import { recheck } from '../../utils/plugins'; import { mockBuildConfig } from '../../utils/artkts-config'; import { parseDumpSrc } from '../../utils/parse-string'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../utils/path-config'; import { annotation } from '../../../common/arkts-utils'; import { PluginContext, Plugins } from '../../../common/plugin-context'; import { AbstractVisitor } from '../../../common/abstract-visitor'; +import { ProgramVisitor } from '../../../common/program-visitor'; +import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../../../common/predefines'; const COMMON_UTILS_DIR_PATH: string = 'common-utils'; @@ -122,7 +123,14 @@ function addAnnotationTransform(this: PluginContext): arkts.EtsScript | undefine if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; const annotationAdder = new AnnotationVisitor(); - annotationAdder.visitor(program.astNode); + const programVisitor = new ProgramVisitor({ + pluginName: addAnnotationTransform.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_ERROR, // ignored + visitors: [annotationAdder], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this, + }); + program = programVisitor.programVisitor(program); script = program.astNode; return script; } @@ -135,7 +143,14 @@ function removeAnnotationTransform(this: PluginContext): arkts.EtsScript | undef if (!!contextPtr) { let program = arkts.getOrUpdateGlobalContext(contextPtr).program; const annotationAdder = new AnnotationVisitor(true); - annotationAdder.visitor(program.astNode); + const programVisitor = new ProgramVisitor({ + pluginName: addAnnotationTransform.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_ERROR, // ignored + visitors: [annotationAdder], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this, + }); + program = programVisitor.programVisitor(program); script = program.astNode; return script; } @@ -198,7 +213,7 @@ function testCheckAnnotation(this: PluginTestContext): void { pluginTester.run( 'annotation', - [addAnnotation, removeAnnotation, recheck], + [addAnnotation, removeAnnotation], { 'parsed:add-annotation': [testParseAnnotation], 'checked:add-annotation': [testCheckAnnotation], diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts index c24e48d0a..1506b9ca1 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/argument-call.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -32,7 +32,7 @@ const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -function memo_arg_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, arg2: ((x: number)=> number), @memo() arg3: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number), arg4?: ((x: number)=> number), @memo() arg5?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number)): void { +@memo() function memo_arg_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, arg2: ((x: number)=> number), @memo() arg3: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number), arg4?: ((x: number)=> number), @memo() arg5?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 5); const __memo_parameter_arg1 = __memo_scope.param(0, arg1), __memo_parameter_arg2 = __memo_scope.param(1, arg2), __memo_parameter_arg3 = __memo_scope.param(2, arg3), __memo_parameter_arg4 = __memo_scope.param(3, arg4), __memo_parameter_arg5 = __memo_scope.param(4, arg5); if (__memo_scope.unchanged) { @@ -50,7 +50,7 @@ function memo_arg_call(__memo_context: __memo_context_type, __memo_id: __memo_id return; } } -function memo_arg_call_with_lowering(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, arg4?: ((x: number)=> number), @memo() arg5?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number)): void { +@memo() function memo_arg_call_with_lowering(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, arg4?: ((x: number)=> number), @memo() arg5?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, x: number)=> number)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 3); const __memo_parameter_arg1 = __memo_scope.param(0, arg1), __memo_parameter_arg4 = __memo_scope.param(1, arg4), __memo_parameter_arg5 = __memo_scope.param(2, arg5); if (__memo_scope.unchanged) { @@ -70,9 +70,9 @@ function memo_arg_call_with_lowering(__memo_context: __memo_context_type, __memo return; } } -function args_with_default_values(__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: int, @memo() gensym%%_?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int), gensym%%_?: int, arg4?: int): void { +@memo() function args_with_default_values(__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: int, @memo() gensym%%_?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int), gensym%%_?: int, arg4?: int): void { let arg1: int = (((gensym%%_) !== (undefined)) ? gensym%%_ : (10 as int)); - let arg2: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type): int => { + let arg2: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> int) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -101,7 +101,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform argument calls in functions', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/complex-memo-intrinsic.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/complex-memo-intrinsic.test.ts index f5175c19a..a9f12ad2c 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/complex-memo-intrinsic.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/complex-memo-intrinsic.test.ts @@ -15,11 +15,12 @@ import * as path from 'path'; import { PluginTester } from '../../../utils/plugin-tester'; -import { mockBuildConfig } from '../../../utils/artkts-config'; +import { mockBuildConfig, mockProjectConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { ProjectConfig } from '../../../../common/plugin-context'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -28,7 +29,10 @@ buildConfig.compileFiles = [ path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'complex-memo-intrinsic.ets'), ]; -const pluginTester = new PluginTester('test complex memo intrinsic function', buildConfig); +const projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test complex memo intrinsic function', buildConfig, projectConfig); const expectedScript: string = ` @@ -104,13 +108,13 @@ class A implements IA { export type SimpleArray = Array | ReadonlyArray | Readonly>; class Use { - public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + (228150357)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - const style = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, attributes: IA): void => { + const style = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, attributes: IA) => { const __memo_scope = __memo_context.scope(((__memo_id) + (237001330)), 1); const __memo_parameter_attributes = __memo_scope.param(0, attributes); if (__memo_scope.unchanged) { @@ -141,7 +145,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform complex @memo_intrinsic calls in functions', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.ts index 23d779dfb..a1eb3091e 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/declare-and-call.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -34,7 +34,7 @@ const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -47,7 +47,7 @@ function main() {} } }); @memo() function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void -function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { +@memo() function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -60,7 +60,7 @@ function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): } } class A { - public foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -82,7 +82,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform declare functions and calls', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts index a921f408a..a73b950b6 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/inner-functions.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -34,7 +34,7 @@ const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -function foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { +@memo() function foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -45,13 +45,13 @@ function foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type): vo return; } } -function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { +@memo() function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - const qux = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + const qux = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -70,14 +70,14 @@ function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type): vo } } class A { - public goo(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public goo(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { - __memo_scope.cached; - return; + __memo_scope.cached; + return; } let func = (() => {}); - let func2 = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + let func2 = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -104,7 +104,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform inner functions', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/internal-memo-arg.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/internal-memo-arg.test.ts index dd7df949f..02fc30648 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/internal-memo-arg.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/internal-memo-arg.test.ts @@ -15,11 +15,12 @@ import * as path from 'path'; import { PluginTester } from '../../../utils/plugin-tester'; -import { mockBuildConfig } from '../../../utils/artkts-config'; +import { mockBuildConfig, mockProjectConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { ProjectConfig } from '../../../../common/plugin-context'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -28,7 +29,10 @@ buildConfig.compileFiles = [ path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, FUNCTION_DIR_PATH, 'internal-memo-arg.ets'), ]; -const pluginTester = new PluginTester('test internal memo argument calls', buildConfig); +const projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test internal memo argument calls', buildConfig, projectConfig); const expectedScript: string = ` @@ -63,7 +67,7 @@ export function __id(): __memo_id_type return __memo_context.valueBy(name); } -@memo_intrinsic() export function contextLocalScope(__memo_context: __memo_context_type, __memo_id: __memo_id_type, name: string, value: Value, @memo() content: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { +@memo_intrinsic() export function contextLocalScope(__memo_context: __memo_context_type, __memo_id: __memo_id_type, name: string, value: Value, @memo() content: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { const scope = __memo_context.scope(__memo_id, 1); scope.param(0, value, undefined, name, true); if (scope.unchanged) { @@ -83,7 +87,7 @@ export function __id(): __memo_id_type if (!reuseKey) { update(__memo_context, ((__memo_id) + (6025780)), (__memo_context.node as Node)); } else { - memoEntry(__memo_context, 0, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + memoEntry(__memo_context, 0, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + (31840240)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -106,7 +110,7 @@ export function __id(): __memo_id_type return __memo_context.controlledScope(__memo_id, invalidate); } -export function Repeat(__memo_context: __memo_context_type, __memo_id: __memo_id_type, count: int, @memo() action: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, index: int)=> void)): void { +@memo() export function Repeat(__memo_context: __memo_context_type, __memo_id: __memo_id_type, count: int, @memo() action: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, index: int)=> void)) { const __memo_scope = __memo_context.scope(((__memo_id) + (200707415)), 2); const __memo_parameter_count = __memo_scope.param(0, count), __memo_parameter_action = __memo_scope.param(1, action); if (__memo_scope.unchanged) { @@ -137,7 +141,7 @@ export class MemoCallbackContext { this.id = id; } - public static Make(__memo_context: __memo_context_type, __memo_id: __memo_id_type): MemoCallbackContext { + @memo() public static Make(__memo_context: __memo_context_type, __memo_id: __memo_id_type): MemoCallbackContext { const __memo_scope = __memo_context.scope(((__memo_id) + (41727473)), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -148,7 +152,7 @@ export class MemoCallbackContext { } export class CustomComponent { - public static _instantiateImpl(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public static _instantiateImpl(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + (214802466)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -172,7 +176,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform internal __context() and __id() calls in functions', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts index 8d94ec992..c59453f5f 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/non-void-return-type.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -34,28 +34,28 @@ const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -function funcNum(__memo_context: __memo_context_type, __memo_id: __memo_id_type): number { +@memo() function funcNum(__memo_context: __memo_context_type, __memo_id: __memo_id_type): number { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(1); } -function funcStr(__memo_context: __memo_context_type, __memo_id: __memo_id_type): string { +@memo() function funcStr(__memo_context: __memo_context_type, __memo_id: __memo_id_type): string { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(\"1\"); } -function funcBool(__memo_context: __memo_context_type, __memo_id: __memo_id_type): boolean { +@memo() function funcBool(__memo_context: __memo_context_type, __memo_id: __memo_id_type): boolean { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(false); } -function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): A { +@memo() function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): A { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -64,21 +64,21 @@ function funcA(__memo_context: __memo_context_type, __memo_id: __memo_id_type): str: \"1\", }); } -function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): B { +@memo() function funcB(__memo_context: __memo_context_type, __memo_id: __memo_id_type): B { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(((str: string) => {})); } -function funcC(__memo_context: __memo_context_type, __memo_id: __memo_id_type): C { +@memo() function funcC(__memo_context: __memo_context_type, __memo_id: __memo_id_type): C { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } return __memo_scope.recache(new C(\"1\")); } -function funcD(__memo_context: __memo_context_type, __memo_id: __memo_id_type): (()=> void) { +@memo() function funcD(__memo_context: __memo_context_type, __memo_id: __memo_id_type): (()=> void) { const __memo_scope = __memo_context.scope<(()=> void)>(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -104,7 +104,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform functions with non-void return type', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.ts index 0490eef81..eca62211f 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/type-reference.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -35,12 +35,23 @@ import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} @memo() export function A(__memo_context: __memo_context_type, __memo_id: __memo_id_type): Attribute -function func(__memo_context: __memo_context_type, __memo_id: __memo_id_type): ItemBuilder { +@memo() function func(__memo_context: __memo_context_type, __memo_id: __memo_id_type): ItemBuilder { const __memo_scope = __memo_context.scope>(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; } - return __memo_scope.recache(((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: Item): void => {})); + return __memo_scope.recache(((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: Item): void => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_item = __memo_scope.param(0, item); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + })); } @memo() type ItemBuilder = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, item: Item)=> void); interface Item { @@ -51,13 +62,13 @@ interface Attribute { @memo() each(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() itemGenerator: ItemBuilder): Attribute } class B { - public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - A(__memo_context, ((__memo_id) + ())).each(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, ri: Item): void => { + A(__memo_context, ((__memo_id) + ())).each(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, ri: Item) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_ri = __memo_scope.param(0, ri); if (__memo_scope.unchanged) { @@ -84,7 +95,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform functions with type reference', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.ts b/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.ts index b3e729785..fd2f3cb88 100644 --- a/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/function-declarations/void-return-type.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const FUNCTION_DIR_PATH: string = 'memo/functions'; @@ -34,7 +34,7 @@ const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -function func(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { +@memo() function func(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -53,7 +53,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform functions with void return type', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts index c94fe1dd9..18b3a26e3 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/argument-call.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const LAMBDA_DIR_PATH: string = 'memo/lambdas'; @@ -34,10 +34,20 @@ import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} ((arg: (()=> void)) => {})((() => {})); -((arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => {})(@memo() (() => {})); +((arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => {})(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +})); ((gensym%%_1?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => { - let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_1) !== (undefined)) ? gensym%%_1 : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_1) !== (undefined)) ? gensym%%_1 : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + (201676739)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -48,7 +58,7 @@ function main() {} return; } }) as @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))); -})(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +})(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + (209782503)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -60,14 +70,14 @@ function main() {} } })); -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void => { - let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => { + let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -88,7 +98,7 @@ function main() {} __memo_scope.recache(); return; } - })(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + })(__memo_context, ((__memo_id) + ()), @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -105,13 +115,13 @@ function main() {} } }); -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - let goo = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: string): void => { + let goo = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_?: string) => { let name: string = (((gensym%%_) !== (undefined)) ? gensym%%_ : (\"old\" as string)); const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_name = __memo_scope.param(0, name); @@ -133,7 +143,7 @@ function main() {} (() => { let foo = ((gensym%%_?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) => { - let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + let arg: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) = (((gensym%%_) !== (undefined)) ? gensym%%_ : (((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -145,7 +155,7 @@ function main() {} } }) as @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))); }); - foo(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + foo(@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -165,7 +175,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform argument calls in lambdas', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.ts index 415423f6e..60d5c5985 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/function-with-receiver.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; import { Plugins } from '../../../../common/plugin-context'; import { uiTransform } from '../../../../ui-plugins'; @@ -43,7 +43,7 @@ import { memo as memo } from "arkui.stateManagement.runtime"; function main() {} -function foo1(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string): void { +@memo() function foo1(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string): void { const __memo_scope = __memo_context.scope(((__memo_id) + (38567515)), 2); const __memo_parameter_this = __memo_scope.param(0, this), __memo_parameter_str = __memo_scope.param(1, str); if (__memo_scope.unchanged) { @@ -57,7 +57,7 @@ function foo1(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id } } -function foo2(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string): B { +@memo() function foo2(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id_type, str: string): B { const __memo_scope = __memo_context.scope(((__memo_id) + (167482260)), 2); const __memo_parameter_this = __memo_scope.param(0, this), __memo_parameter_str = __memo_scope.param(1, str); if (__memo_scope.unchanged) { @@ -68,7 +68,7 @@ function foo2(this: B, __memo_context: __memo_context_type, __memo_id: __memo_id } class B { - public internal_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type): B { + @memo() public internal_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type): B { const __memo_scope = __memo_context.scope(((__memo_id) + (146437675)), 0); if (__memo_scope.unchanged) { return __memo_scope.cached; @@ -87,7 +87,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform lambdas about function with receiver feature', - [parsedTransform, memoNoRecheck, recheck], + [parsedTransform, beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts index ffb1c2cfa..b2465acc7 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/trailing-lambdas.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const LAMBDA_DIR_PATH: string = 'memo/lambdas'; @@ -34,7 +34,7 @@ const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -44,7 +44,7 @@ function main() {} a.foo(__memo_context, ((__memo_id) + ()), (() => { console.log(); })); - a.goo(((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + a.goo(((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -56,7 +56,7 @@ function main() {} return; } })); - a.koo(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + a.koo(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -71,7 +71,7 @@ function main() {} bar(__memo_context, ((__memo_id) + ()), (() => { console.log(); })); - par(((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + par(((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -83,7 +83,7 @@ function main() {} return; } })); - kar(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + kar(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -100,7 +100,7 @@ function main() {} return; } }); -function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, f?: (()=> void)): void { +@memo() function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, f?: (()=> void)): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_f = __memo_scope.param(0, f); if (__memo_scope.unchanged) { @@ -113,7 +113,7 @@ function bar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, f?: } } function par(f?: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} -function kar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() f?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { +@memo() function kar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() f?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_f = __memo_scope.param(0, f); if (__memo_scope.unchanged) { @@ -126,7 +126,7 @@ function kar(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @me } } class A { - public foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, p?: (()=> void)): void { + @memo() public foo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, p?: (()=> void)): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_p = __memo_scope.param(0, p); if (__memo_scope.unchanged) { @@ -139,7 +139,7 @@ class A { } } public goo(@memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} - public koo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + @memo() public koo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() p?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_p = __memo_scope.param(0, p); if (__memo_scope.unchanged) { @@ -161,7 +161,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform trailing lambdas', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.ts index f49946e13..37b1c1365 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/void-lambda.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const LAMBDA_DIR_PATH: string = 'memo/lambdas'; @@ -65,7 +65,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform lambdas with void return type', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.test.ts b/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.test.ts index ce9477f75..748b1dd4b 100644 --- a/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/lambda-literals/with-receiver.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const LAMBDA_DIR_PATH: string = 'memo/lambdas'; @@ -33,15 +33,17 @@ const pluginTester = new PluginTester('test memo lambda', buildConfig); const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; import { memo as memo } from \"arkui.stateManagement.runtime\"; + function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } let x = new Person(); - fullName(x, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + fullName(x, __memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -53,12 +55,9 @@ function main() {} } })); let f1: F1 = foo; - f1 = goo; let f2: F2 = goo; - f2 = foo; - f1 = f2; let a = new A(); - f1(a, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + f1(a, __memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -69,7 +68,7 @@ function main() {} return; } })); - f1(a, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + f1(a, __memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -80,7 +79,7 @@ function main() {} return; } })); - f2(a, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + f2(__memo_context, ((__memo_id) + ()), a, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -96,19 +95,56 @@ function main() {} return; } }); -function fullName(this: Person, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { - return; + +@memo() function fullName(this: Person, __memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_this = __memo_scope.param(0, this), __memo_parameter_arg = __memo_scope.param(1, arg); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} + +@memo() function foo(this: A, __memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_this = __memo_scope.param(0, this), __memo_parameter_arg = __memo_scope.param(1, arg); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } } -function foo(this: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} -function goo(a: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void {} + +@memo() function goo(__memo_context: __memo_context_type, __memo_id: __memo_id_type, a: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); + const __memo_parameter_a = __memo_scope.param(0, a), __memo_parameter_arg = __memo_scope.param(1, arg); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } +} + class Person { public constructor() {} } + class A { public constructor() {} } -type F1 = ((this: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void); -type F2 = ((a: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void); + +@memo() type F1 = ((this: A, __memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void); +@memo() type F2 = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, a: A, @memo() arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void))=> void); `; function testMemoTransformer(this: PluginTestContext): void { @@ -117,7 +153,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform lambdas with receiver', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.ts index a4326ac1e..dfbc5547d 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/argument-call.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const METHOD_DIR_PATH: string = 'memo/methods'; @@ -33,7 +33,7 @@ import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} class Test { - public lambda_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + @memo() public lambda_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -45,7 +45,7 @@ class Test { return; } } - public lambda_arg_with_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string)=> string)): void { + @memo() public lambda_arg_with_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type, value: string)=> string)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -57,7 +57,7 @@ class Test { return; } } - public memo_content(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() content: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void { + @memo() public memo_content(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() content: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_content = __memo_scope.param(0, content); if (__memo_scope.unchanged) { @@ -70,7 +70,7 @@ class Test { return; } } - public compute_test(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg1: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined, arg2: (()=> void) | undefined, content: (()=> void) | undefined): void { + @memo() public compute_test(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo() arg1: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined, arg2: (()=> void) | undefined, content: (()=> void) | undefined): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 3); const __memo_parameter_arg1 = __memo_scope.param(0, arg1), __memo_parameter_arg2 = __memo_scope.param(1, arg2), __memo_parameter_content = __memo_scope.param(2, content); if (__memo_scope.unchanged) { @@ -85,7 +85,7 @@ class Test { public constructor() {} } class Use { - public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -111,7 +111,7 @@ class Use { } return __memo_scope.recache(__memo_parameter_value.value); })); - test.compute_test(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + test.compute_test(__memo_context, ((__memo_id) + ()), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -137,7 +137,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform argument calls in methods', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts index 77b786332..8852d486e 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/callable.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const METHOD_DIR_PATH: string = 'memo/methods'; @@ -32,14 +32,14 @@ const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } A.$_invoke(__memo_context, ((__memo_id) + ())); - B.$_invoke(((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + B.$_invoke(((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -62,7 +62,7 @@ function main() {} } }); class A { - public static $_invoke(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public static $_invoke(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -80,7 +80,7 @@ class B { public constructor() {} } class C { - public static $_instantiate(__memo_context: __memo_context_type, __memo_id: __memo_id_type, factory: (()=> C)): C { + @memo() public static $_instantiate(__memo_context: __memo_context_type, __memo_id: __memo_id_type, factory: (()=> C)): C { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_factory = __memo_scope.param(0, factory); if (__memo_scope.unchanged) { @@ -104,7 +104,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform callable class', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts index 5a23c9b83..309f637f8 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/declare-and-call.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const METHOD_DIR_PATH: string = 'memo/methods'; @@ -34,7 +34,7 @@ const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -54,7 +54,7 @@ declare abstract class A { public constructor() {} } class AA extends A { - public x(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public x(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -75,7 +75,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform declare methods and calls', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts index 1c870cf48..caf2b1b9f 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/internal-calls.test.ts @@ -15,18 +15,22 @@ import * as path from 'path'; import { PluginTester } from '../../../utils/plugin-tester'; -import { mockBuildConfig } from '../../../utils/artkts-config'; +import { mockBuildConfig, mockProjectConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { ProjectConfig } from '../../../../common/plugin-context'; const METHOD_DIR_PATH: string = 'memo/methods'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, METHOD_DIR_PATH, 'internal-calls.ets')]; -const pluginTester = new PluginTester('test memo method', buildConfig); +const projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test memo method', buildConfig, projectConfig); const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; @@ -36,7 +40,7 @@ export function __context(): __memo_context_type export function __id(): __memo_id_type type MemoType = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); class Test { - public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -47,7 +51,7 @@ class Test { return; } } - public internal_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public internal_call(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -59,7 +63,7 @@ class Test { return; } } - public method_with_internals(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public method_with_internals(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -73,7 +77,7 @@ class Test { } } public memo_lambda() { - @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -85,7 +89,7 @@ class Test { } }); } - public memo_variables(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public memo_variables(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -120,7 +124,7 @@ class Test { return; } } - public args_with_default_values(__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_1?: int, gensym%%_2?: (()=> int), gensym%%_3?: int, arg4?: int): void { + @memo() public args_with_default_values(__memo_context: __memo_context_type, __memo_id: __memo_id_type, gensym%%_1?: int, gensym%%_2?: (()=> int), gensym%%_3?: int, arg4?: int): void { let arg1: int = (((gensym%%_1) !== (undefined)) ? gensym%%_1 : (10 as int)); let arg2: (()=> int) = (((gensym%%_2) !== (undefined)) ? gensym%%_2 : ((() => { return 20; @@ -139,7 +143,7 @@ class Test { return; } } - public optional_args(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1?: int, arg2?: (()=> int)): void { + @memo() public optional_args(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1?: int, arg2?: (()=> int)) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 2); const __memo_parameter_arg1 = __memo_scope.param(0, arg1), __memo_parameter_arg2 = __memo_scope.param(1, arg2); if (__memo_scope.unchanged) { @@ -155,7 +159,7 @@ class Test { return; } } - public type_alias(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: MemoType): void { + @memo() public type_alias(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: MemoType) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -178,7 +182,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform inner calls in methods', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.test.ts index 70a12bc70..8957ca871 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/non-void-method.test.ts @@ -15,18 +15,22 @@ import * as path from 'path'; import { PluginTester } from '../../../utils/plugin-tester'; -import { mockBuildConfig } from '../../../utils/artkts-config'; +import { mockBuildConfig, mockProjectConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; +import { ProjectConfig } from '../../../../common/plugin-context'; const METHOD_DIR_PATH: string = 'memo/methods'; const buildConfig: BuildConfig = mockBuildConfig(); buildConfig.compileFiles = [path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH, METHOD_DIR_PATH, 'non-void-method.ets')]; -const pluginTester = new PluginTester('test memo method', buildConfig); +const projectConfig: ProjectConfig = mockProjectConfig(); +projectConfig.frameworkMode = 'frameworkMode'; + +const pluginTester = new PluginTester('test memo method', buildConfig, projectConfig); const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; @@ -36,8 +40,9 @@ export function __context(): __memo_context_type export function __id(): __memo_id_type @Retention({policy:"SOURCE"}) @interface memo_intrinsic {} @Retention({policy:"SOURCE"}) @interface memo_entry {} +@Retention({policy:"SOURCE"}) @interface memo_skip {} class Test { - public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -48,7 +53,7 @@ class Test { return; } } - public string_method_with_return(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string): string { + @memo() public string_method_with_return(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string): string { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -56,7 +61,7 @@ class Test { } return __memo_scope.recache(__memo_parameter_arg.value); } - public method_with_type_parameter(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T): T { + @memo() public method_with_type_parameter(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T): T { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -84,10 +89,20 @@ class Test { return entry(__memo_context, ((__memo_id) + ())); } } + @memo() public memo_skip_args(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg1: number, @memo_skip() arg2: string, @memo_skip() arg3: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): string { + const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); + const __memo_parameter_arg1 = __memo_scope.param(0, arg1); + if (__memo_scope.unchanged) { + return __memo_scope.cached; + } + let a = __memo_parameter_arg1.value; + arg3(__memo_context, ((__memo_id) + ())); + return __memo_scope.recache(arg2); + } public constructor() {} } class Use { - public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -111,7 +126,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform methods with non-void return type', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts b/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts index e29eb8cc1..41e73d5bb 100644 --- a/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/method-declarations/void-method.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const METHOD_DIR_PATH: string = 'memo/methods'; @@ -40,7 +40,7 @@ class A { public constructor() {} } class Test { - public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public void_method(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -51,7 +51,7 @@ class Test { return; } } - public a_method_with_implicit_return_type(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public a_method_with_implicit_return_type(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -62,7 +62,7 @@ class Test { return; } } - public void_method_with_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string): void { + @memo() public void_method_with_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -74,7 +74,7 @@ class Test { return; } } - public void_method_with_return(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string): void { + @memo() public void_method_with_return(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: string) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -86,7 +86,7 @@ class Test { return; } } - public static static_method_with_type_parameter(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T): void { + @memo() public static static_method_with_type_parameter(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: T): void { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -98,7 +98,7 @@ class Test { return; } } - public obj_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: A): void { + @memo() public obj_arg(__memo_context: __memo_context_type, __memo_id: __memo_id_type, arg: A) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 1); const __memo_parameter_arg = __memo_scope.param(0, arg); if (__memo_scope.unchanged) { @@ -113,7 +113,7 @@ class Test { public constructor() {} } class Use { - public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public test(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -143,7 +143,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform methods with void return type', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts index 49a170e20..af8cde937 100644 --- a/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-constructor.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const PROPERTY_DIR_PATH: string = 'memo/properties'; @@ -34,14 +34,14 @@ const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } let a = new AA({ - a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -59,7 +59,7 @@ function main() {} } }); interface A { - @memo() set a(a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void + @memo() set a(a: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) @memo() get a(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) } class AA { @@ -71,7 +71,7 @@ class AA { this.a = ({let gensym%%_ = arg; (((gensym%%_) == (null)) ? undefined : gensym%%_.a)}); } - public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -93,7 +93,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform properties in class constructor', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.ts b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.ts index 31092a975..b0c65607e 100644 --- a/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/property-declarations/class-properties.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const PROPERTY_DIR_PATH: string = 'memo/properties'; @@ -38,7 +38,7 @@ class A { public arg: (()=> void); @memo() public memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); @memo() public memo_optional_arg?: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined; - @memo() public memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + @memo() public memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -52,7 +52,7 @@ class A { public arg_memo_type: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); public constructor() { this.arg = (() => {}); - this.memo_arg = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + this.memo_arg = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -63,7 +63,7 @@ class A { return; } }); - this.arg_memo_type = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + this.arg_memo_type = ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -75,7 +75,7 @@ class A { } }); } - public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -98,7 +98,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform properties in class', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts b/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts index d58f3d7bc..3164742a0 100644 --- a/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts +++ b/arkui-plugins/test/ut/memo-plugins/property-declarations/interfaces.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const PROPERTY_DIR_PATH: string = 'memo/properties'; @@ -32,7 +32,7 @@ const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; import { memo as memo } from \"arkui.stateManagement.runtime\"; function main() {} -@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { +@memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -40,7 +40,7 @@ function main() {} } let a: A = { arg: (() => {}), - memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -51,7 +51,7 @@ function main() {} return; } }), - memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -62,7 +62,7 @@ function main() {} return; } }), - arg_memo_type: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + arg_memo_type: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + ()), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -82,11 +82,11 @@ function main() {} interface A { set arg(arg: (()=> void)) get arg(): (()=> void) - @memo() set memo_arg(memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)): void + @memo() set memo_arg(memo_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) @memo() get memo_arg(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) - @memo() set memo_optional_arg(memo_optional_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined): void + @memo() set memo_optional_arg(memo_optional_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) @memo() get memo_optional_arg(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined - @memo() set memo_union_arg(memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined): void + @memo() set memo_union_arg(memo_union_arg: ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined) @memo() get memo_union_arg(): ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) | undefined set arg_memo_type(arg_memo_type: @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void)) get arg_memo_type(): @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void) @@ -99,7 +99,7 @@ function testMemoTransformer(this: PluginTestContext): void { pluginTester.run( 'transform interface properties', - [memoNoRecheck, recheck], + [beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:memo-no-recheck': [testMemoTransformer], }, diff --git a/arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.ts index a7266aa96..e34d6cc9d 100644 --- a/arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/animation/animatable-extend-basic.test.ts @@ -76,8 +76,8 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ public __updateStruct(initializers: __Options_AnimatablePropertyExample | undefined): void {} @memo() public build() { - Column(undefined, (() => { - Text(((instance: TextAttribute): void => { + Column(undefined, undefined, (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.animationStart({ duration: 2000, curve: Curve.Ease, @@ -92,7 +92,7 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ curve: Curve.Ease, }).width("100%"); return; - }), "AnimatableProperty"); + }), "AnimatableProperty", undefined, undefined); })); } @@ -108,7 +108,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { AnimatablePropertyExample._instantiateImpl(undefined, (() => { return new AnimatablePropertyExample(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts index 599720488..b7f9b64dc 100644 --- a/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/animation/animation-basic.test.ts @@ -75,8 +75,8 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ public __updateStruct(initializers: __Options_AnimatablePropertyExample | undefined): void {} @memo() public build() { - Column(undefined, (() => { - Text(((instance: TextAttribute): void => { + Column(undefined, undefined, (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.animationStart({ duration: 2000, curve: Curve.Ease, @@ -91,7 +91,7 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ curve: Curve.Ease, }).width("100%"); return; - }), "AnimatableProperty"); + }), "AnimatableProperty", undefined, undefined); })); } @@ -107,7 +107,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { AnimatablePropertyExample._instantiateImpl(undefined, (() => { return new AnimatablePropertyExample(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts index d848437c7..9cb90f2eb 100644 --- a/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/custom-component/custom-component-call.test.ts @@ -129,7 +129,7 @@ function main() {} return this.__backing_closer!; } - public set closer(@memo() value: (()=> void)) { + public set closer(value: @memo() (()=> void)) { this.__backing_closer = value; } @@ -147,25 +147,25 @@ function main() {} public __updateStruct(initializers: __Options_CustomContainerUser | undefined): void {} @memo() public build() { - Column(undefined, (() => { + Column(undefined, undefined, @memo() (() => { CustomContainer._instantiateImpl(undefined, (() => { return new CustomContainer(); - }), (() => { - Column(undefined, (() => { - Text(undefined, "hello"); + }), undefined, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => { + Text(undefined, "hello", undefined, undefined); })); })); CustomContainer._instantiateImpl(undefined, (() => { return new CustomContainer(); - }), {}, (() => { - Column(undefined, (() => {})); + }), {}, undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => {})); })); CustomContainer._instantiateImpl(undefined, (() => { return new CustomContainer(); - }), undefined, (() => {})); + }), undefined, undefined, @memo() (() => {})); CustomContainer._instantiateImpl(undefined, (() => { return new CustomContainer(); - })); + }), undefined, undefined, undefined); })); } @@ -174,7 +174,7 @@ function main() {} } @Component() export interface __Options_CustomContainer { - set closer(@memo() closer: (()=> void) | undefined) + set closer(closer: @memo() (()=> void) | undefined) get closer(): @memo() (()=> void) | undefined diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.test.ts index 52b5c7aa5..e8e1ace16 100644 --- a/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/simple-component.test.ts @@ -18,7 +18,7 @@ import { PluginTester } from '../../../utils/plugin-tester'; import { mockBuildConfig } from '../../../utils/artkts-config'; import { getRootPath, MOCK_ENTRY_DIR_PATH } from '../../../utils/path-config'; import { parseDumpSrc } from '../../../utils/parse-string'; -import { builderLambdaNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; +import { beforeMemoNoRecheck, builderLambdaNoRecheck, memoNoRecheck, recheck } from '../../../utils/plugins'; import { BuildConfig, PluginTestContext } from '../../../utils/shared-types'; const BUILDER_LAMBDA_DIR_PATH: string = 'builder-lambda'; @@ -32,12 +32,13 @@ const pluginTester = new PluginTester('test builder-lambda simple component', bu function testBuilderLambdaTransformer(this: PluginTestContext): void { const expectedScript: string = ` +import { memo as memo } from "arkui.stateManagement.runtime"; import { memo as memo } from \"@ohos.arkui.stateManagement\"; import { Column as Column, ColumnAttribute as ColumnAttribute } from \"arkui.component.column\"; function main() {} class MyStateSample { @memo() public build() { - Column(undefined, undefined, (() => {})); + Column(undefined, undefined, @memo() (() => {})); } public constructor() {} } @@ -48,17 +49,18 @@ class MyStateSample { function testMemoTransformer(this: PluginTestContext): void { const expectedScript: string = ` import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type } from \"arkui.stateManagement.runtime\"; +import { memo as memo } from "arkui.stateManagement.runtime"; import { memo as memo } from \"@ohos.arkui.stateManagement\"; import { Column as Column, ColumnAttribute as ColumnAttribute } from \"arkui.component.column\"; function main() {} class MyStateSample { - public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + @memo() public build(__memo_context: __memo_context_type, __memo_id: __memo_id_type) { const __memo_scope = __memo_context.scope(((__memo_id) + (263357132)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; return; } - Column(__memo_context, ((__memo_id) + (65509320)), undefined, undefined, ((__memo_context: __memo_context_type, __memo_id: __memo_id_type): void => { + Column(__memo_context, ((__memo_id) + (65509320)), undefined, undefined, @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { const __memo_scope = __memo_context.scope(((__memo_id) + (147296800)), 0); if (__memo_scope.unchanged) { __memo_scope.cached; @@ -82,7 +84,7 @@ class MyStateSample { pluginTester.run( 'transform simple component', - [builderLambdaNoRecheck, recheck, memoNoRecheck, recheck], + [builderLambdaNoRecheck, beforeMemoNoRecheck, memoNoRecheck, recheck], { 'checked:builder-lambda-no-recheck': [testBuilderLambdaTransformer], 'checked:memo-no-recheck': [testMemoTransformer], diff --git a/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts b/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts index 09d7420cd..897a4d828 100644 --- a/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/builder-lambda/style-with-receiver.test.ts @@ -76,15 +76,15 @@ function main() {} public __updateStruct(initializers: __Options_MM | undefined): void {} @memo() public build() { - Column(undefined, (() => { - Text(((instance: TextAttribute): void => { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { style22(cardStyle(instance.height(200).fontColor("#000000"), 600, "#eeeeee").fontSize(60).fontWeight(400)).width(900); return; - }), "hello world"); + }), "hello world", undefined, undefined); Text(@memo() ((instance: TextAttribute): void => { cardStyle(instance, 600, "#eeeeee"); return; - }), "hello world"); + }), "hello world", undefined, undefined); })); } diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts index ffdf38238..69c198125 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/builder-param-passing.test.ts @@ -67,7 +67,7 @@ function main() {} return this.__backing_customBuilderParam!; } - public set customBuilderParam(@memo() value: (()=> void)) { + public set customBuilderParam(value: @memo() (()=> void)) { this.__backing_customBuilderParam = value; } @@ -87,27 +87,27 @@ function main() {} public __updateStruct(initializers: __Options_Parent | undefined): void {} @memo() public componentBuilder() { - Text(undefined, "Parent builder"); + Text(undefined, "Parent builder", undefined, undefined); } @memo() public build() { - Column(undefined, (() => { + Column(undefined, undefined, @memo() (() => { Child._instantiateImpl(undefined, (() => { return new Child(); }), { customBuilderParam: this.componentBuilder, - }); + }, undefined, undefined); Child._instantiateImpl(undefined, (() => { return new Child(); }), { customBuilderParam: @memo() (() => { this.componentBuilder(); }), - }); + }, undefined, undefined); Child._instantiateImpl(undefined, (() => { return new Child(); - }), (() => { - Text(undefined, "Parent builder"); + }), undefined, undefined, @memo() (() => { + Text(undefined, "Parent builder", undefined, undefined); })); })); } @@ -117,7 +117,7 @@ function main() {} } @Component() export interface __Options_Child { - set customBuilderParam(@memo() customBuilderParam: (()=> void) | undefined) + set customBuilderParam(customBuilderParam: @memo() (()=> void) | undefined) get customBuilderParam(): @memo() (()=> void) | undefined diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts index 2eff07da7..e999c104c 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder-param/init-with-local-builder.test.ts @@ -69,7 +69,7 @@ function main() {} return this.__backing_customBuilderParam!; } - public set customBuilderParam(@memo() value: (()=> void)) { + public set customBuilderParam(value: @memo() (()=> void)) { this.__backing_customBuilderParam = value; } @@ -79,7 +79,7 @@ function main() {} return this.__backing_customBuilderParam2!; } - public set customBuilderParam2(@memo() value: ((str: string)=> void)) { + public set customBuilderParam2(value: @memo() ((str: string)=> void)) { this.__backing_customBuilderParam2 = value; } @@ -97,10 +97,10 @@ function main() {} } @Component() export interface __Options_Child { - set customBuilderParam(@memo() customBuilderParam: (()=> void) | undefined) + set customBuilderParam(customBuilderParam: @memo() (()=> void) | undefined) get customBuilderParam(): @memo() (()=> void) | undefined - set customBuilderParam2(@memo() customBuilderParam2: ((str: string)=> void) | undefined) + set customBuilderParam2(customBuilderParam2: @memo() ((str: string)=> void) | undefined) get customBuilderParam2(): @memo() ((str: string)=> void) | undefined diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts index af94a3af4..b5a052fc6 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder/global-builder.test.ts @@ -51,12 +51,12 @@ function main() {} @memo() function showTextBuilder() { - Text(undefined, "Hello World"); + Text(undefined, "Hello World", undefined, undefined); } @memo() function overBuilder(params: Tmp) { - Row(undefined, (() => { - Text(undefined, (("UseStateVarByReference: ") + (params.paramA1))); + Row(undefined, undefined, @memo() (() => { + Text(undefined, (("UseStateVarByReference: ") + (params.paramA1)), undefined, undefined); })); } @@ -74,7 +74,7 @@ class Tmp { public __updateStruct(initializers: __Options_BuilderDemo | undefined): void {} @memo() public build() { - Row(undefined, @memo() (() => { + Row(undefined, undefined, @memo() (() => { showTextBuilder(); overBuilder({ paramA1: "Hello", diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts index 84218864c..976fd7033 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/builder/local-builder.test.ts @@ -59,21 +59,21 @@ function main() {} public __updateStruct(initializers: __Options_BuilderDemo | undefined): void {} @memo() public showTextBuilder() { - Text(((instance: TextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(30); return; - }), "Hello World"); + }), "Hello World", undefined, undefined); } @memo() public showTextValueBuilder(param: string) { - Text(((instance: TextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(30); return; - }), param); + }), param, undefined, undefined); } @memo() public build() { - Column(undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => { this.showTextBuilder(); this.showTextValueBuilder("Hello @Builder"); })); diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts index b8434470e..17d458fc9 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/link-to-link-prop-state.test.ts @@ -43,14 +43,14 @@ import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateMan import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; -import { memo as memo } from "arkui.stateManagement.runtime"; - import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; +import { memo as memo } from "arkui.stateManagement.runtime"; + import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; @@ -86,10 +86,10 @@ function main() {} } @memo() public build() { - Column(undefined, (() => { + Column(undefined, undefined, @memo() (() => { TextInput(undefined, { text: this.text1, - }); + }, undefined); Child._instantiateImpl(undefined, (() => { return new Child(); }), { @@ -97,7 +97,7 @@ function main() {} childText2: this.text1, childText3: this.text1, childText4: this.text1, - }); + }, undefined, undefined); })); } @@ -172,7 +172,7 @@ function main() {} @memo() public build() { TextInput(undefined, { text: this.childText, - }); + }, undefined); } private constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts index f7e39c261..7a3862871 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/link/state-to-link.test.ts @@ -41,14 +41,14 @@ const expectedScript: string = ` import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; -import { memo as memo } from "arkui.stateManagement.runtime"; - import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { LinkSourceType as LinkSourceType } from "arkui.stateManagement.decorator"; import { ILinkDecoratedVariable as ILinkDecoratedVariable } from "arkui.stateManagement.decorator"; +import { memo as memo } from "arkui.stateManagement.runtime"; + import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; import { NavInterface as NavInterface } from "arkui.UserView"; @@ -98,24 +98,24 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ } @memo() public build() { - Column(undefined, (() => { - Button(((instance: ButtonAttribute): void => { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.selectedDate.setFullYear(((this.selectedDate.getFullYear()) + (1))); })); return; - }), "child increase the year by 1"); - Button(((instance: ButtonAttribute): void => { + }), "child increase the year by 1", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { instance.margin(10).onClick(((e: ClickEvent) => { this.selectedDate = new Date("2023-09-09"); })); return; - }), "child update the new date"); + }), "child update the new date", undefined, undefined); DatePicker(undefined, { start: new Date("1970-1-1"), end: new Date("2100-1-1"), selected: this.selectedDate, - }); + }, undefined); })); } @@ -142,29 +142,29 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ } @memo() public build() { - Column(undefined, (() => { - Button(((instance: ButtonAttribute): void => { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.margin(10).onClick(((e: ClickEvent) => { this.parentSelectedDate.setMonth(((this.parentSelectedDate.getMonth()) + (1))); })); return; - }), "parent increase the month by 1"); - Button(((instance: ButtonAttribute): void => { + }), "parent increase the month by 1", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { instance.margin(10).onClick(((e: ClickEvent) => { this.parentSelectedDate = new Date("2023-07-07"); })); return; - }), "parent update the new date"); + }), "parent update the new date", undefined, undefined); DatePicker(undefined, { start: new Date("1970-1-1"), end: new Date("2100-1-1"), selected: this.parentSelectedDate, - }); + }, undefined); DateComponent._instantiateImpl(undefined, (() => { return new DateComponent(); }), { __backing_selectedDate: this.__backing_parentSelectedDate, - }); + }, undefined, undefined); })); } @@ -198,7 +198,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { ParentComponent._instantiateImpl(undefined, (() => { return new ParentComponent(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-observed.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-observed.test.ts index 5bc96038c..cdf26099c 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-observed.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/objectlink/objectlink-observed.test.ts @@ -41,10 +41,10 @@ const expectedScript: string = ` import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; -import { memo as memo } from "arkui.stateManagement.runtime"; - import { IObjectLinkDecoratedVariable as IObjectLinkDecoratedVariable } from "arkui.stateManagement.decorator"; +import { memo as memo } from "arkui.stateManagement.runtime"; + import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; import { IObservedObject as IObservedObject } from "arkui.stateManagement.decorator"; @@ -204,13 +204,13 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ } @memo() public build() { - Column(undefined, (() => { - Button(((instance: ButtonAttribute): void => { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.data.setDate(((this.data.getDate()) + (1))); })); return; - }), "child increase the day by 1"); + }), "child increase the day by 1", undefined, undefined); })); } @@ -237,25 +237,25 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ } @memo() public build() { - Column(undefined, (() => { + Column(undefined, undefined, @memo() (() => { Child._instantiateImpl(undefined, (() => { return new Child(); }), { label: "date", data: this.newData.data, - }); - Button(((instance: ButtonAttribute): void => { + }, undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.newData.data = new DateClass("2023-07-07"); })); return; - }), "parent update the new date"); - Button(((instance: ButtonAttribute): void => { + }), "parent update the new date", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.newData = new NewDate(new DateClass("2023-08-20")); })); return; - }), "ViewB: this.newData = new NewDate(new DateClass('2023-08-20'))"); + }), "ViewB: this.newData = new NewDate(new DateClass('2023-08-20'))", undefined, undefined); })); } @@ -290,7 +290,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { Parent._instantiateImpl(undefined, (() => { return new Parent(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts index f6568dd60..ffff4a57a 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/observed-track/observed-track-complex-type.test.ts @@ -829,7 +829,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { MyStateSample._instantiateImpl(undefined, (() => { return new MyStateSample(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts index 411080b4b..cd62e2906 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/prop/state-to-prop.test.ts @@ -41,12 +41,12 @@ const expectedScript: string = ` import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; -import { memo as memo } from "arkui.stateManagement.runtime"; - import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; +import { memo as memo } from "arkui.stateManagement.runtime"; + import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; @@ -99,18 +99,18 @@ function main() {} } @memo() public build() { - Column(undefined, (() => { + Column(undefined, undefined, @memo() (() => { if (((this.count) > (0))) { - Text(undefined, (((("You have") + (this.count))) + ("Nuggets left"))); + Text(undefined, (((("You have") + (this.count))) + ("Nuggets left")), undefined, undefined); } else { - Text(undefined, "Game over!"); + Text(undefined, "Game over!", undefined, undefined); } - Button(((instance: ButtonAttribute): void => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.count -= this.costOfOneAttempt; })); return; - }), "Try again"); + }), "Try again", undefined, undefined); })); } @@ -137,26 +137,26 @@ function main() {} } @memo() public build() { - Column(undefined, (() => { - Text(undefined, (((("Grant") + (this.countDownStartValue))) + ("nuggets to play."))); - Button(((instance: ButtonAttribute): void => { + Column(undefined, undefined, @memo() (() => { + Text(undefined, (((("Grant") + (this.countDownStartValue))) + ("nuggets to play.")), undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.countDownStartValue += 1; })); return; - }), "+1 - Nuggets in New Game"); - Button(((instance: ButtonAttribute): void => { + }), "+1 - Nuggets in New Game", undefined, undefined); + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => { this.countDownStartValue -= 1; })); return; - }), "-1 - Nuggets in New Game"); + }), "-1 - Nuggets in New Game", undefined, undefined); CountDownComponent._instantiateImpl(undefined, (() => { return new CountDownComponent(); }), { count: this.countDownStartValue, costOfOneAttempt: 2, - }); + }, undefined, undefined); })); } diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts index ab9a379ca..15657c474 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-build.test.ts @@ -38,15 +38,24 @@ const parsedTransform: Plugins = { }; const expectedScript: string = ` -import { memo as memo } from "arkui.stateManagement.runtime"; import { _rawfile as _rawfile } from "arkui.component.resources"; + import { ImageAnimatorAttribute as ImageAnimatorAttribute } from "arkui.component.imageAnimator"; + +import { memo as memo } from "arkui.stateManagement.runtime"; + import { ImageAttribute as ImageAttribute } from "arkui.component.image"; + import { _r as _r } from "arkui.component.resources"; + import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; + import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; + import { CustomComponent as CustomComponent } from "arkui.component.customComponent"; + import { Component as Component, $r as $r, $rawfile as $rawfile, Column as Column, Text as Text, Image as Image, TextInput as TextInput, Select as Select, SelectOption as SelectOption, Margin as Margin, ImageAnimator as ImageAnimator, Resource as Resource } from "@ohos.arkui.component"; + function main() {} @Component() final struct ResourceComponent extends CustomComponent { @@ -100,14 +109,14 @@ function main() {} } @memo() public build() { - Column(undefined, (() => { - Text(undefined, _r(16777216, 10003, "com.example.mock", "entry")); - Image(undefined, _rawfile(0, 30000, "com.example.mock", "entry", "app.mock.txt")); + Column(undefined, undefined, @memo() (() => { + Text(undefined, _r(16777216, 10003, "com.example.mock", "entry"), undefined, undefined); + Image(undefined, _rawfile(0, 30000, "com.example.mock", "entry", "app.mock.txt"), undefined, undefined); TextInput(undefined, { text: _r(16777220, 10003, "com.example.mock", "entry"), - }); - Text(undefined, _r(-1, -1, "com.example.mock", "entry", this.str1)); - Text(undefined, _r(-1, -1, "com.example.mock", "entry", this.str2)); + }, undefined); + Text(undefined, _r(-1, -1, "com.example.mock", "entry", this.str1), undefined, undefined); + Text(undefined, _r(-1, -1, "com.example.mock", "entry", this.str2), undefined, undefined); Select(undefined, new Array({ value: "aaa", icon: _r(16777223, 20000, "com.example.mock", "entry"), @@ -120,15 +129,15 @@ function main() {} }, { value: "ddd", icon: _r(16777223, 20000, "com.example.mock", "entry"), - })); - Image(((instance: ImageAttribute): void => { + }), undefined); + Image(@memo() ((instance: ImageAttribute): void => { instance.margin(({ top: _r(16777222, 10002, "com.example.mock", "entry"), bottom: _r(16777222, 10002, "com.example.mock", "entry"), } as Margin)); return; - }), _r(16777217, 20000, "com.example.mock", "entry")); - ImageAnimator(((instance: ImageAnimatorAttribute): void => { + }), _r(16777217, 20000, "com.example.mock", "entry"), undefined, undefined); + ImageAnimator(@memo() ((instance: ImageAnimatorAttribute): void => { instance.images([{ src: _r(16777217, 20000, "com.example.mock", "entry"), }, { diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts index 6d28f0fb6..99498521e 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/resource/resource-in-property.test.ts @@ -73,11 +73,12 @@ i = _r(16777216, 10003, "com.example.mock", "entry"); public set icon(value: Resource) { this.__backing_icon = value; } + @memo() public build() { - Column(undefined, (() => { - Text(undefined, this.str); - Text(undefined, i); - Image(undefined, this.icon); + Column(undefined, undefined, @memo() (() => { + Text(undefined, this.str, undefined, undefined); + Text(undefined, i, undefined, undefined); + Image(undefined, this.icon, undefined, undefined); })); } private constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts index df76281bd..ec5f742fe 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/reusable/reusable-complex.test.ts @@ -41,14 +41,14 @@ const expectedScript: string = ` import { TextAttribute as TextAttribute } from "arkui.component.text"; -import { memo as memo } from "arkui.stateManagement.runtime"; - import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; +import { memo as memo } from "arkui.stateManagement.runtime"; + import { ColumnAttribute as ColumnAttribute } from "arkui.component.column"; import { NavInterface as NavInterface } from "arkui.UserView"; @@ -105,16 +105,16 @@ class Message { } @memo() public build() { - Column(((instance: ColumnAttribute): void => { + Column(@memo() ((instance: ColumnAttribute): void => { instance.height("100%").width("100%"); return; - }), (() => { - Button(((instance: ButtonAttribute): void => { + }), undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.fontSize(30).fontWeight(FontWeight.Bold).onClick(((e: ClickEvent) => { this.display = !(this.display); })); return; - }), "Hello"); + }), "Hello", undefined, undefined); if (this.display) { Child._instantiateImpl(undefined, (() => { return new Child(); @@ -159,14 +159,14 @@ class Message { } @memo() public build() { - Column(((instance: ColumnAttribute): void => { + Column(@memo() ((instance: ColumnAttribute): void => { instance.borderWidth(1).height(100); return; - }), (() => { - Text(((instance: TextAttribute): void => { + }), undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(30); return; - }), this.message.value); + }), this.message.value, undefined, undefined); })); } @@ -198,7 +198,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { Index._instantiateImpl(undefined, (() => { return new Index(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.test.ts index 9db3a8db6..927dc81ce 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-basic-type.test.ts @@ -167,6 +167,10 @@ function main() {} `; function testParsedAndCheckedTransformer(this: PluginTestContext): void { + console.log("[testParsedAndCheckedTransformer] this: ", this); + console.log("[testParsedAndCheckedTransformer] parseDumpSrc(this.scriptSnapshot ?? ''): ", parseDumpSrc(this.scriptSnapshot ?? '')); + console.log("[testParsedAndCheckedTransformer] parseDumpSrc(expectedScript): ", parseDumpSrc(expectedScript)); + expect(parseDumpSrc(this.scriptSnapshot ?? '')).toBe(parseDumpSrc(expectedScript)); } diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.test.ts index dd53e3c78..c27847e9e 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/state/state-to-state.test.ts @@ -39,12 +39,12 @@ const parsedTransform: Plugins = { const expectedScript: string = ` -import { memo as memo } from "arkui.stateManagement.runtime"; - import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; +import { memo as memo } from "arkui.stateManagement.runtime"; + import { LayoutCallback as LayoutCallback } from "arkui.component.customComponent"; import { CustomComponentV2 as CustomComponentV2 } from "arkui.component.customComponent"; @@ -87,12 +87,12 @@ class Per { } @memo() public build() { - Column(undefined, (() => { + Column(undefined, undefined, @memo() (() => { Child._instantiateImpl(undefined, (() => { return new Child(); }), { childVar1: this.parentVar1, - }); + }, undefined, undefined); })); } @@ -119,7 +119,7 @@ class Per { } @memo() public build() { - Text(undefined, this.childVar1.str); + Text(undefined, this.childVar1.str, undefined, undefined); } private constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.test.ts index 474e6776a..11ba358f5 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-appstorage.test.ts @@ -39,12 +39,12 @@ const pluginTester = new PluginTester('test storagelink with appstorage', buildC const expectedScript: string = ` -import { memo as memo } from "arkui.stateManagement.runtime"; - import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { IStorageLinkDecoratedVariable as IStorageLinkDecoratedVariable } from "arkui.stateManagement.decorator"; +import { memo as memo } from "arkui.stateManagement.runtime"; + import { TextAttribute as TextAttribute } from "arkui.component.text"; import { NavInterface as NavInterface } from "arkui.UserView"; @@ -114,19 +114,19 @@ class Data { } @memo() public build() { - Column(undefined, (() => { - Text(((instance: TextAttribute): void => { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.onClick(((e: ClickEvent) => { this.storageLink += 1; })); return; - }), \`From AppStorage \${this.storageLink}\`); - Text(((instance: TextAttribute): void => { + }), \`From AppStorage \${this.storageLink}\`, undefined, undefined); + Text(@memo() ((instance: TextAttribute): void => { instance.onClick(((e: ClickEvent) => { this.storageLinkObject.code += 1; })); return; - }), \`From AppStorage \${this.storageLinkObject.code}\`); + }), \`From AppStorage \${this.storageLinkObject.code}\`, undefined, undefined); })); } @@ -154,7 +154,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { Index._instantiateImpl(undefined, (() => { return new Index(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts index 7f256cbe8..15e0885a7 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-complex-type.test.ts @@ -302,7 +302,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { MyStateSample._instantiateImpl(undefined, (() => { return new MyStateSample(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.test.ts index f613eea39..3840dd6d4 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storagelink/storagelink-primitive-type.test.ts @@ -142,7 +142,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { MyStateSample._instantiateImpl(undefined, (() => { return new MyStateSample(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts index de222f2cc..ca7b515a9 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-appstorage.test.ts @@ -39,12 +39,12 @@ const pluginTester = new PluginTester('test storageprop with appstorage', buildC const expectedScript: string = ` -import { memo as memo } from "arkui.stateManagement.runtime"; - import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { IStoragePropDecoratedVariable as IStoragePropDecoratedVariable } from "arkui.stateManagement.decorator"; +import { memo as memo } from "arkui.stateManagement.runtime"; + import { TextAttribute as TextAttribute } from "arkui.component.text"; import { NavInterface as NavInterface } from "arkui.UserView"; @@ -114,19 +114,19 @@ class Data { } @memo() public build() { - Column(undefined, (() => { - Text(((instance: TextAttribute): void => { + Column(undefined, undefined, @memo() (() => { + Text(@memo() ((instance: TextAttribute): void => { instance.onClick(((e: ClickEvent) => { this.storageProp += 1; })); return; - }), \`From AppStorage \${this.storageProp}\`); - Text(((instance: TextAttribute): void => { + }), \`From AppStorage \${this.storageProp}\`, undefined, undefined); + Text(@memo() ((instance: TextAttribute): void => { instance.onClick(((e: ClickEvent) => { this.storagePropObject.code += 1; })); return; - }), \`From AppStorage \${this.storagePropObject.code}\`); + }), \`From AppStorage \${this.storagePropObject.code}\`, undefined, undefined); })); } @@ -154,7 +154,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { Index._instantiateImpl(undefined, (() => { return new Index(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts index 57c5dbb37..86ed9aa96 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-complex-type.test.ts @@ -302,7 +302,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { MyStateSample._instantiateImpl(undefined, (() => { return new MyStateSample(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts index 14122a95b..f9863bd47 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/storageprop/storageprop-primitive-type.test.ts @@ -142,7 +142,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { MyStateSample._instantiateImpl(undefined, (() => { return new MyStateSample(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts b/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts index aa6c5f3d6..16d2535ff 100644 --- a/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/decorators/watch/watch-basic.test.ts @@ -275,7 +275,7 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ @memo() public build() { Child._instantiateImpl(undefined, (() => { return new Child(); - })); + }), undefined, undefined, undefined); } private constructor() {} @@ -371,7 +371,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { MyStateSample._instantiateImpl(undefined, (() => { return new MyStateSample(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-griditem.test.ts b/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-griditem.test.ts index b244f916b..0eb8ae712 100644 --- a/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-griditem.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/double-dollar/double-dollar-griditem.test.ts @@ -39,12 +39,12 @@ const parsedTransform: Plugins = { const expectedScript: string = ` -import { memo as memo } from "arkui.stateManagement.runtime"; - import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateManagement.decorator"; +import { memo as memo } from "arkui.stateManagement.runtime"; + import { GridItemAttribute as GridItemAttribute } from "arkui.component.gridItem"; import { Bindable as Bindable } from "arkui.component.common"; @@ -98,9 +98,9 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ } @memo() public build() { - Column(undefined, (() => { - Grid(undefined, (() => { - GridItem(((instance: GridItemAttribute): void => { + Column(undefined, undefined, @memo() (() => { + Grid(undefined, undefined, undefined, @memo() (() => { + GridItem(@memo() ((instance: GridItemAttribute): void => { instance.selected(({ value: this.boo, onChange: ((value: boolean) => { @@ -108,10 +108,10 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ }), } as Bindable)); return; - }), (() => { - Text(undefined, "nihao"); + }), undefined, @memo() (() => { + Text(undefined, "nihao", undefined, undefined); })); - GridItem(((instance: GridItemAttribute): void => { + GridItem(@memo() ((instance: GridItemAttribute): void => { instance.selected(({ value: c, onChange: ((value: boolean) => { @@ -119,8 +119,8 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ }), } as Bindable)); return; - }), (() => { - Text(undefined, "nihao"); + }), undefined, @memo() (() => { + Text(undefined, "nihao", undefined, undefined); })); })); })); @@ -144,7 +144,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { MyStateSample._instantiateImpl(undefined, (() => { return new MyStateSample(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/imports/import-struct.test.ts b/arkui-plugins/test/ut/ui-plugins/imports/import-struct.test.ts index 3a5763d11..f0c0c3187 100644 --- a/arkui-plugins/test/ut/ui-plugins/imports/import-struct.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/imports/import-struct.test.ts @@ -97,16 +97,16 @@ function main() {} @memo() public build() { SimpleStruct._instantiateImpl(undefined, (() => { return new SimpleStruct(); - })); + }), undefined, undefined, undefined); SimpleStruct._instantiateImpl(undefined, (() => { return new SimpleStruct(); }), { message: "str1", - }); + }, undefined, undefined); SimpleStruct._instantiateImpl(undefined, (() => { return new SimpleStruct(); - }), (() => { - Text(undefined, "a"); + }), undefined, undefined, @memo() (() => { + Text(undefined, "a", undefined, undefined); })); } diff --git a/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts b/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts index 577234f30..4ef6bf65e 100644 --- a/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/imports/kit-import.test.ts @@ -103,8 +103,6 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ const expectedCheckedScript: string = ` -import { memo as memo } from "arkui.stateManagement.runtime"; - import { IPropDecoratedVariable as IPropDecoratedVariable } from "arkui.stateManagement.decorator"; import { STATE_MGMT_FACTORY as STATE_MGMT_FACTORY } from "arkui.stateManagement.decorator"; @@ -113,6 +111,8 @@ import { IStateDecoratedVariable as IStateDecoratedVariable } from "arkui.stateM import { TextAttribute as TextAttribute } from "arkui.component.text"; +import { memo as memo } from "arkui.stateManagement.runtime"; + import { ButtonAttribute as ButtonAttribute } from "arkui.component.button"; import { NavInterface as NavInterface } from "arkui.UserView"; @@ -182,15 +182,15 @@ __EntryWrapper.RegisterNamedRouter("", new __EntryWrapper(), ({ } @memo() public build() { - Column(undefined, (() => { - Button(((instance: ButtonAttribute): void => { + Column(undefined, undefined, @memo() (() => { + Button(@memo() ((instance: ButtonAttribute): void => { instance.onClick(((e: ClickEvent) => {})); return; - }), "button"); - Text(((instance: TextAttribute): void => { + }), "button", undefined, undefined); + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(20); return; - }), "text"); + }), "text", undefined, undefined); })); } @@ -218,7 +218,7 @@ class __EntryWrapper extends EntryPoint { @memo() public entry(): void { A._instantiateImpl(undefined, (() => { return new A(); - })); + }), undefined, undefined, undefined); } public constructor() {} diff --git a/arkui-plugins/test/ut/ui-plugins/wrap-builder/init-with-builder.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/init-with-builder.test.ts index 946db1fa1..396a03122 100644 --- a/arkui-plugins/test/ut/ui-plugins/wrap-builder/init-with-builder.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/init-with-builder.test.ts @@ -51,14 +51,14 @@ function main() {} globalBuilder = wrapBuilder(myBuilder); @memo() function myBuilder(value: string, size: number) { - Text(((instance: TextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(size); return; - }), value); + }), value, undefined, undefined); } -type MyBuilderFuncType = @memo() ((value: string, size: number)=> void); +@memo() type MyBuilderFuncType = @Builder() ((value: string, size: number)=> void); @Component() final struct ImportStruct extends CustomComponent { public __initializeStruct(initializers: __Options_ImportStruct | undefined, @memo() content: (()=> void) | undefined): void {} @@ -66,7 +66,7 @@ type MyBuilderFuncType = @memo() ((value: string, size: number)=> void); public __updateStruct(initializers: __Options_ImportStruct | undefined): void {} @memo() public build() { - Column(undefined, (() => { + Column(undefined, undefined, @memo() (() => { globalBuilder.builder("hello", 50); })); } diff --git a/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.test.ts b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.test.ts index 33bd5355c..c5d73e216 100644 --- a/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.test.ts +++ b/arkui-plugins/test/ut/ui-plugins/wrap-builder/wrap-builder-in-ui.test.ts @@ -56,21 +56,21 @@ function main() {} @memo() function myBuilder(value: string, size: number) { - Text(((instance: TextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(size); return; - }), value); + }), value, undefined, undefined); } @memo() function yourBuilder(value: string, size: number) { - Text(((instance: TextAttribute): void => { + Text(@memo() ((instance: TextAttribute): void => { instance.fontSize(size); return; - }), value); + }), value, undefined, undefined); } -type MyBuilderFuncType = @memo() ((value: string, size: number)=> void); +@memo() type MyBuilderFuncType = @Builder() ((value: string, size: number)=> void); @Component() final struct ImportStruct extends CustomComponent { public __initializeStruct(initializers: __Options_ImportStruct | undefined, @memo() content: (()=> void) | undefined): void {} @@ -84,7 +84,7 @@ type MyBuilderFuncType = @memo() ((value: string, size: number)=> void); } @memo() public build() { - Column(undefined, @memo() (() => { + Column(undefined, undefined, @memo() (() => { this.testBuilder(); })); } diff --git a/arkui-plugins/test/utils/artkts-config.ts b/arkui-plugins/test/utils/artkts-config.ts index 037eb2e9d..d47091bd7 100644 --- a/arkui-plugins/test/utils/artkts-config.ts +++ b/arkui-plugins/test/utils/artkts-config.ts @@ -174,27 +174,28 @@ function mockBuildConfig(): BuildConfig { function mockProjectConfig(): ProjectConfig { return { - bundleName: MOCK_BUNDLE_NAME, - moduleName: 'entry', - cachePath: path.resolve(getRootPath(), MOCK_OUTPUT_CACHE_PATH), - dependentModuleList: [], - appResource: path.resolve(getResourcePath(), MOCK_RESOURCE_TABLE_FILE_NAME), - rawFileResource: path.resolve(getResourcePath(), MOCK_RAWFILE_DIR_PATH), - buildLoaderJson: '', - hspResourcesMap: false, - compileHar: false, - byteCodeHar: false, - uiTransformOptimization: false, - resetBundleName: false, - allowEmptyBundleName: false, - moduleType: 'entry', - moduleRootPath: path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH), - aceModuleJsonPath: '', - ignoreError: true, - projectPath: '', - projectRootPath: path.resolve(getRootPath(), MOCK_PROJECT_ROOT_PATH), - integratedHsp: false -}; + bundleName: MOCK_BUNDLE_NAME, + moduleName: 'entry', + cachePath: path.resolve(getRootPath(), MOCK_OUTPUT_CACHE_PATH), + dependentModuleList: [], + appResource: path.resolve(getResourcePath(), MOCK_RESOURCE_TABLE_FILE_NAME), + rawFileResource: path.resolve(getResourcePath(), MOCK_RAWFILE_DIR_PATH), + buildLoaderJson: '', + hspResourcesMap: false, + compileHar: false, + byteCodeHar: false, + uiTransformOptimization: false, + resetBundleName: false, + allowEmptyBundleName: false, + moduleType: 'entry', + moduleRootPath: path.resolve(getRootPath(), MOCK_ENTRY_DIR_PATH), + aceModuleJsonPath: '', + ignoreError: true, + projectPath: '', + projectRootPath: path.resolve(getRootPath(), MOCK_PROJECT_ROOT_PATH), + integratedHsp: false, + frameworkMode: undefined + }; } class MockArktsConfigBuilder implements ArktsConfigBuilder { diff --git a/arkui-plugins/test/utils/compile.ts b/arkui-plugins/test/utils/compile.ts index 9dd537855..afa55219a 100644 --- a/arkui-plugins/test/utils/compile.ts +++ b/arkui-plugins/test/utils/compile.ts @@ -25,7 +25,7 @@ import { } from './shared-types'; import { MockPluginDriver, stateName } from './plugin-driver'; import { - createContextFromString, + createContextGenerateAbcForExternalSourceFiles, createCacheContextFromFile, destroyContext, resetConfig, @@ -202,7 +202,7 @@ function createContextForExternalCompilation(jobInfo: JobInfo): arkts.Context { function compileAbcWithExternal(emitter: EventEmitter, jobInfo: JobInfo, tracing: TraceOptions): void { MockPluginDriver.getInstance().initPlugins(jobInfo.plugins ?? []); MockPluginDriver.getInstance().getPluginContext().setProjectConfig(jobInfo.projectConfig!); - const context = createContextFromString(jobInfo.filePaths!); + const context = createContextGenerateAbcForExternalSourceFiles(jobInfo.filePaths!); MockPluginDriver.getInstance().getPluginContext().setContextPtr(context.peer); const stopAfter = jobInfo.stopAfter!; let shouldStop = false; diff --git a/arkui-plugins/test/utils/global.ts b/arkui-plugins/test/utils/global.ts index 28f7a868c..424d4512e 100644 --- a/arkui-plugins/test/utils/global.ts +++ b/arkui-plugins/test/utils/global.ts @@ -35,6 +35,9 @@ function createGlobalConfig( if (isDebug) { config.push('--debug-info'); } + if (!isUseCache) { + config.push('--simultaneous'); + } config.push(fileInfo.filePath); if (isUseCache) { @@ -68,10 +71,8 @@ function createCacheContextFromFile( return arkts.Context.createCacheContextFromFile(config.peer, filePath, globalContextPtr, isExternal); } -function createContextFromString(filePaths: string[]): arkts.Context { - const source = fs.readFileSync(filePaths.at(0)!).toString(); - arkts.arktsGlobal.filePath = filePaths.at(0)!; - return arkts.Context.createFromString(source); +function createContextGenerateAbcForExternalSourceFiles(filePaths: string[]): arkts.Context { + return arkts.Context.createContextGenerateAbcForExternalSourceFiles(filePaths); } function resetContext(source: string): void { @@ -124,7 +125,7 @@ export { createGlobalContextPtr, destroyGlobalContextPtr, createCacheContextFromFile, - createContextFromString, + createContextGenerateAbcForExternalSourceFiles, resetContext, resetConfig, destroyContext, diff --git a/arkui-plugins/test/utils/plugin-tester.ts b/arkui-plugins/test/utils/plugin-tester.ts index 1fe094237..1f1a6a59d 100644 --- a/arkui-plugins/test/utils/plugin-tester.ts +++ b/arkui-plugins/test/utils/plugin-tester.ts @@ -62,7 +62,7 @@ class PluginTester { constructor(describe: string, buildConfig?: BuildConfig, projectConfig?: ProjectConfig) { this.describe = describe; this.hashId = HashGenerator.getInstance().dynamicSha1Id(describe, 13); - this.configBuilder = new MockArktsConfigBuilder(this.hashId, buildConfig); + this.configBuilder = new MockArktsConfigBuilder(this.hashId, buildConfig, projectConfig); } private clear(): void { diff --git a/arkui-plugins/test/utils/plugins/before-memo-no-recheck.ts b/arkui-plugins/test/utils/plugins/before-memo-no-recheck.ts new file mode 100644 index 000000000..f6a2c0c13 --- /dev/null +++ b/arkui-plugins/test/utils/plugins/before-memo-no-recheck.ts @@ -0,0 +1,47 @@ +/* + * 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. + */ + +import * as arkts from '@koalaui/libarkts'; +import { PluginContext, Plugins } from '../../../common/plugin-context'; +import { ProgramVisitor } from '../../../common/program-visitor'; +import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../../../common/predefines'; +import { MemoVisitor } from '../../../collectors/memo-collectors/memo-visitor'; + +/** + * AfterCheck before-memo-visit and cache any node that should be unmemoized with no recheck AST. + */ +export const beforeMemoNoRecheck: Plugins = { + name: 'before-memo-no-recheck', + checked(this: PluginContext): arkts.EtsScript | undefined { + let script: arkts.EtsScript | undefined; + const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; + if (!!contextPtr) { + let program = arkts.getOrUpdateGlobalContext(contextPtr).program; + script = program.astNode; + const builderLambdaTransformer = new MemoVisitor(); + const programVisitor = new ProgramVisitor({ + pluginName: beforeMemoNoRecheck.name, + state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, + visitors: [builderLambdaTransformer], + skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + pluginContext: this, + }); + program = programVisitor.programVisitor(program); + script = program.astNode; + return script; + } + return script; + }, +}; \ No newline at end of file diff --git a/arkui-plugins/test/utils/plugins/index.ts b/arkui-plugins/test/utils/plugins/index.ts index 4858b84b2..ab232c217 100644 --- a/arkui-plugins/test/utils/plugins/index.ts +++ b/arkui-plugins/test/utils/plugins/index.ts @@ -17,6 +17,7 @@ export * from './struct-to-component'; // AfterCheck +export * from './before-memo-no-recheck'; export * from './builder-lambda-no-recheck'; export * from './memo-no-recheck'; export * from './struct-no-recheck'; diff --git a/arkui-plugins/test/utils/plugins/memo-no-recheck.ts b/arkui-plugins/test/utils/plugins/memo-no-recheck.ts index f4bc38681..9396b34e5 100644 --- a/arkui-plugins/test/utils/plugins/memo-no-recheck.ts +++ b/arkui-plugins/test/utils/plugins/memo-no-recheck.ts @@ -16,7 +16,7 @@ import * as arkts from '@koalaui/libarkts'; import { PluginContext, Plugins } from '../../../common/plugin-context'; import { ProgramVisitor } from '../../../common/program-visitor'; -import { EXTERNAL_SOURCE_PREFIX_NAMES } from '../../../common/predefines'; +import { EXTERNAL_SOURCE_PREFIX_NAMES, EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK } from '../../../common/predefines'; import { PositionalIdTracker } from '../../../memo-plugins/utils'; import { ParameterTransformer } from '../../../memo-plugins/parameter-transformer'; import { ReturnTransformer } from '../../../memo-plugins/return-transformer'; @@ -32,6 +32,7 @@ export const memoNoRecheck: Plugins = { checked(this: PluginContext): arkts.EtsScript | undefined { const contextPtr = this.getContextPtr() ?? arkts.arktsGlobal.compilerContext?.peer; if (!!contextPtr) { + const isFrameworkMode = !!this.getProjectConfig()?.frameworkMode; let program = arkts.getOrUpdateGlobalContext(contextPtr).program; let script = program.astNode; const positionalIdTracker = new PositionalIdTracker(arkts.getFileName(), false); @@ -40,22 +41,30 @@ export const memoNoRecheck: Plugins = { }); const returnTransformer = new ReturnTransformer(); const signatureTransformer = new SignatureTransformer(); - const internalsTransformer = new InternalsTransformer({ positionalIdTracker }); + let internalsTransformer: InternalsTransformer | undefined; + if (isFrameworkMode) { + internalsTransformer = new InternalsTransformer({ positionalIdTracker }); + } const functionTransformer = new FunctionTransformer({ positionalIdTracker, parameterTransformer, returnTransformer, signatureTransformer, internalsTransformer, + useCache: arkts.NodeCache.getInstance().isCollected() }); + const skipPrefixNames = isFrameworkMode + ? EXTERNAL_SOURCE_PREFIX_NAMES_FOR_FRAMEWORK + : EXTERNAL_SOURCE_PREFIX_NAMES; const programVisitor = new ProgramVisitor({ pluginName: memoNoRecheck.name, state: arkts.Es2pandaContextState.ES2PANDA_STATE_CHECKED, visitors: [functionTransformer], - skipPrefixNames: EXTERNAL_SOURCE_PREFIX_NAMES, + skipPrefixNames, pluginContext: this, }); program = programVisitor.programVisitor(program); + arkts.NodeCache.getInstance().clear(); script = program.astNode; return script; } diff --git a/arkui-plugins/tsconfig.build.json b/arkui-plugins/tsconfig.build.json index 02074ad93..a7e077cb8 100644 --- a/arkui-plugins/tsconfig.build.json +++ b/arkui-plugins/tsconfig.build.json @@ -17,6 +17,7 @@ "removeComments": false }, "include": [ + "./collectors/**/*.ts", "./common/**/*.ts", "./memo-plugins/**/*.ts", "./ui-plugins/**/*.ts", diff --git a/arkui-plugins/tsconfig.json b/arkui-plugins/tsconfig.json index c29a9c01d..5ea0297ec 100644 --- a/arkui-plugins/tsconfig.json +++ b/arkui-plugins/tsconfig.json @@ -20,6 +20,7 @@ "esModuleInterop": true }, "include": [ + "./collectors/**/*.ts", "./common/**/*.ts", "./memo-plugins/**/*.ts", "./ui-plugins/**/*.ts", diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts index d4fdf5269..aba68967c 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/builder-lambda-transformer.ts @@ -34,7 +34,7 @@ export class BuilderLambdaTransformer extends AbstractVisitor { visitor(beforeChildren: arkts.AstNode): arkts.AstNode { if (arkts.isCallExpression(beforeChildren) && isBuilderLambda(beforeChildren)) { - const lambda = factory.transformBuilderLambda(beforeChildren, this.projectConfig); + const lambda = factory.transformBuilderLambda(beforeChildren); return this.visitEachChild(lambda); } if (arkts.isMethodDefinition(beforeChildren) && isBuilderLambdaMethodDecl(beforeChildren)) { diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts index 6b6b650ff..55997c0c3 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts @@ -14,15 +14,14 @@ */ import * as arkts from '@koalaui/libarkts'; +import { BuilderLambdaNames } from '../utils'; import { - addMemoAnnotation, - BuilderLambdaNames, - CustomComponentNames, - findCanAddMemoFromParamExpression, - isCustomComponentAnnotation, - hasMemoAnnotation, -} from '../utils'; -import { annotation, backingField, filterDefined, removeAnnotationByName } from '../../common/arkts-utils'; + backingField, + filterDefined, + isDecoratorAnnotation, + removeAnnotationByName, + forEachArgWithParam, +} from '../../common/arkts-utils'; import { BuilderLambdaDeclInfo, builderLambdaFunctionName, @@ -42,12 +41,14 @@ import { isStyleChainedCall, isStyleWithReceiverCall, builderLambdaType, + BuilderLambdaSecondLastArgInfo, + buildSecondLastArgInfo, } from './utils'; -import { hasDecorator, isDecoratorIntrinsicAnnotation } from '../property-translators/utils'; +import { isDecoratorIntrinsicAnnotation } from '../property-translators/utils'; import { factory as PropertyFactory } from '../property-translators/factory'; -import { ProjectConfig } from '../../common/plugin-context'; -import { AnimationNames, BindableDecl, DecoratorIntrinsicNames, StructDecoratorNames, DecoratorNames } from '../../common/predefines'; +import { AnimationNames, BindableDecl, DecoratorIntrinsicNames, DecoratorNames } from '../../common/predefines'; import { ImportCollector } from '../../common/import-collector'; +import { addMemoAnnotation, collectMemoableInfoInParameter } from '../../collectors/memo-collectors/utils'; export class factory { /** @@ -55,16 +56,15 @@ export class factory { */ static updateBuilderLambdaMethodDecl( node: arkts.MethodDefinition, - styleArg: arkts.ETSParameterExpression, + prefixArgs: arkts.ETSParameterExpression[], newAnno: arkts.AnnotationUsage[], newName: string | undefined, externalSourceName?: string ): arkts.MethodDefinition { const func: arkts.ScriptFunction = node.scriptFunction; - let newParams: arkts.Expression[] = [styleArg]; + let newParams: arkts.Expression[] = []; if (func.params.length > 0) { - newParams.push(...func.params.slice(0, func.params.length - 1)); - newParams.push(func.params.at(func.params.length - 1)!); + newParams.push(...prefixArgs, ...func.params); } const updateFunc = arkts.factory .updateScriptFunction( @@ -130,11 +130,7 @@ export class factory { /* * create style instance call, e.g. `instance.margin(10)`. */ - static createStyleLambdaBody( - lambdaBody: arkts.AstNode, - callInfo: InstanceCallInfo, - projectConfig: ProjectConfig | undefined - ): arkts.CallExpression { + static createStyleLambdaBody(lambdaBody: arkts.AstNode, callInfo: InstanceCallInfo): arkts.CallExpression { if (!callInfo.isReceiver) { const newArgs: arkts.Expression[] = factory.getTransformedStyle(callInfo.call); return arkts.factory.createCallExpression( @@ -148,7 +144,7 @@ export class factory { undefined, newArgs.map((arg) => { if (arkts.isArrowFunctionExpression(arg)) { - return this.processArgArrowFunction(arg, projectConfig); + return this.processArgArrowFunction(arg); } return arg; }) @@ -194,9 +190,11 @@ export class factory { undefined ); + const returnStatement = arkts.factory.createReturnStatement(); + arkts.NodeCache.getInstance().collect(returnStatement); const body: arkts.BlockStatement = arkts.factory.createBlock([ arkts.factory.createExpressionStatement(lambdaBody), - arkts.factory.createReturnStatement(), + returnStatement, ]); const func = arkts.factory.createScriptFunction( @@ -211,7 +209,7 @@ export class factory { arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC ); - return arkts.factory.createArrowFunction(func); + return addMemoAnnotation(arkts.factory.createArrowFunction(func)); } /* @@ -234,25 +232,15 @@ export class factory { ), arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW ); + addMemoAnnotation(funcType); let parameter: arkts.ETSParameterExpression; - if (isFunctionCall) { - parameter = arkts.factory - .createParameterDeclaration( - arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_PARAM_NAME, funcType), - undefined - ) - .setOptional(true); - } else { - const optionalFuncType = arkts.factory.createUnionType([funcType, arkts.factory.createETSUndefinedType()]); - parameter = arkts.factory.createParameterDeclaration( - arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_PARAM_NAME, optionalFuncType), - undefined - ); - } - if (findCanAddMemoFromParamExpression(parameter)) { - addMemoAnnotation(parameter); - } + const optionalFuncType = arkts.factory.createUnionType([funcType, arkts.factory.createETSUndefinedType()]); + parameter = arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_PARAM_NAME, optionalFuncType), + undefined + ); + arkts.NodeCache.getInstance().collect(parameter); return parameter; } @@ -260,17 +248,14 @@ export class factory { * If a builder lambda's argument is an arrow function, * then transform any builder lambda in the function body. */ - static processArgArrowFunction( - arg: arkts.ArrowFunctionExpression, - projectConfig: ProjectConfig | undefined - ): arkts.ArrowFunctionExpression { + static processArgArrowFunction(arg: arkts.ArrowFunctionExpression): arkts.ArrowFunctionExpression { const func: arkts.ScriptFunction = arg.scriptFunction; const updateFunc = arkts.factory.updateScriptFunction( func, !!func.body && arkts.isBlockStatement(func.body) ? arkts.factory.updateBlock( func.body, - func.body.statements.map((st) => this.updateContentBodyInBuilderLambda(st, projectConfig)) + func.body.statements.map((st) => this.updateContentBodyInBuilderLambda(st)) ) : undefined, arkts.FunctionSignature.createFunctionSignature( @@ -318,11 +303,18 @@ export class factory { } const returnType: arkts.TypeNode | undefined = decl.scriptFunction.returnTypeAnnotation; const isBindable: boolean = !!returnType && hasBindableProperty(returnType, BindableDecl.BINDABLE); - const isLinkIntrinsic: boolean = decl.scriptFunction.annotations.some((anno) => - isDecoratorIntrinsicAnnotation(anno, DecoratorIntrinsicNames.LINK) - ); + let isBuilderParam: boolean = false; + let isLinkIntrinsic: boolean = false; + decl.scriptFunction.annotations.forEach((anno) => { + isBuilderParam ||= isDecoratorAnnotation(anno, DecoratorNames.BUILDER_PARAM); + isLinkIntrinsic ||= isDecoratorIntrinsicAnnotation(anno, DecoratorIntrinsicNames.LINK); + }); + if (isBindable && isDoubleDollarCall(prop.value)) { return factory.updateBindableProperty(prop); + } else if (isBuilderParam && arkts.isArrowFunctionExpression(prop.value)) { + addMemoAnnotation(prop.value); + return prop; } else if ( isLinkIntrinsic && arkts.isIdentifier(prop.key) && @@ -344,16 +336,20 @@ export class factory { * If the corresponding argument is not provided, fill-in an `undefined` to it. */ static createOrUpdateArgInBuilderLambda( + fallback: arkts.AstNode | undefined, arg: arkts.Expression | undefined, - projectConfig: ProjectConfig | undefined, typeName?: string, - fallback?: arkts.AstNode + canAddMemo?: boolean ): arkts.AstNode | undefined { if (!arg) { return fallback; } if (arkts.isArrowFunctionExpression(arg)) { - return this.processArgArrowFunction(arg, projectConfig); + const newNode = this.processArgArrowFunction(arg); + if (canAddMemo) { + addMemoAnnotation(newNode); + } + return newNode; } // this is too optimistic to check if this is an options argument... if (arkts.isTSAsExpression(arg) || arkts.isObjectExpression(arg)) { @@ -362,70 +358,72 @@ export class factory { return arg; } + static createSecondLastArgInBuilderLambda(argInfo: BuilderLambdaSecondLastArgInfo): arkts.AstNode | undefined { + if (!!argInfo.isReusable && !!argInfo.reuseId) { + const reuseIdNode = arkts.factory.createStringLiteral(argInfo.reuseId); + return this.createOrUpdateArgInBuilderLambda(reuseIdNode, undefined, undefined); + } else if (!argInfo.isFunctionCall) { + return this.createOrUpdateArgInBuilderLambda(arkts.factory.createUndefinedLiteral(), undefined, undefined); + } + return undefined; + } + /** * transform arguments in a builder lambda call. */ static generateArgsInBuilderLambda( leaf: arkts.CallExpression, lambdaBody: arkts.Identifier | arkts.CallExpression, - declInfo: BuilderLambdaDeclInfo, - projectConfig: ProjectConfig | undefined + declInfo: BuilderLambdaDeclInfo ): (arkts.AstNode | undefined)[] { const { isFunctionCall, params, returnType, moduleName } = declInfo; const type: arkts.Identifier | undefined = builderLambdaType(leaf); - let isReusable: boolean | undefined; - let reuseId: arkts.StringLiteral | undefined; - if (!isFunctionCall && !!type) { - const customComponentDecl = arkts.getDecl(type); - isReusable = - !!customComponentDecl && - arkts.isClassDefinition(customComponentDecl) && - customComponentDecl.annotations.some((anno) => - isCustomComponentAnnotation(anno, StructDecoratorNames.RESUABLE) - ); - reuseId = isReusable ? arkts.factory.createStringLiteral(type.name) : undefined; - } const args: (arkts.AstNode | undefined)[] = [ this.createStyleArgInBuilderLambda(lambdaBody, returnType, moduleName), ]; - let index = 0; - while (index < params.length) { - if (isReusable && index === params.length - 1) { - args.push(this.createOrUpdateArgInBuilderLambda(undefined, undefined, undefined, reuseId)); - args.push( - this.createOrUpdateArgInBuilderLambda( - leaf.arguments.at(index), - projectConfig, + const secondLastArgInfo = buildSecondLastArgInfo(type, isFunctionCall); + const isTrailingCall = leaf.isTrailingCall; + forEachArgWithParam( + leaf.arguments, + params, + (arg, param, index) => { + let modifiedArg: arkts.AstNode | undefined; + if (index === params.length - 2 && !arg) { + modifiedArg = this.createSecondLastArgInBuilderLambda(secondLastArgInfo); + } + if (!modifiedArg) { + const memoableInfo = collectMemoableInfoInParameter(param); + const canAddMemo = + (!!memoableInfo.hasBuilder || !!memoableInfo.hasMemo) && !!memoableInfo.hasProperType; + modifiedArg = this.createOrUpdateArgInBuilderLambda( + arkts.factory.createUndefinedLiteral(), + arg, type?.name, - arkts.factory.createUndefinedLiteral() - ) - ); - } else { - args.push(this.createOrUpdateArgInBuilderLambda(leaf.arguments.at(index), projectConfig, type?.name)); - } - index++; - } + canAddMemo + ); + } + args.push(modifiedArg); + }, + { isTrailingCall } + ); return filterDefined(args); } /** * update if-else in trailing lambda contents in a builder lambda call. */ - static updateIfElseContentBodyInBuilderLambda( - statement: arkts.AstNode, - projectConfig: ProjectConfig | undefined - ): arkts.AstNode { + static updateIfElseContentBodyInBuilderLambda(statement: arkts.AstNode): arkts.AstNode { if (arkts.isIfStatement(statement)) { const alternate = !!statement.alternate - ? this.updateIfElseContentBodyInBuilderLambda(statement.alternate, projectConfig) + ? this.updateIfElseContentBodyInBuilderLambda(statement.alternate) : statement.alternate; - const consequence = this.updateIfElseContentBodyInBuilderLambda(statement.consequent, projectConfig); + const consequence = this.updateIfElseContentBodyInBuilderLambda(statement.consequent); return arkts.factory.updateIfStatement(statement, statement.test, consequence!, alternate); } if (arkts.isBlockStatement(statement)) { return arkts.factory.updateBlock( statement, - statement.statements.map((st) => this.updateContentBodyInBuilderLambda(st, projectConfig)) + statement.statements.map((st) => this.updateContentBodyInBuilderLambda(st)) ); } return statement; @@ -434,10 +432,7 @@ export class factory { /** * update trailing lambda contents in a builder lambda call. */ - static updateContentBodyInBuilderLambda( - statement: arkts.Statement, - projectConfig: ProjectConfig | undefined - ): arkts.Statement { + static updateContentBodyInBuilderLambda(statement: arkts.Statement): arkts.Statement { if ( arkts.isExpressionStatement(statement) && arkts.isCallExpression(statement.expression) && @@ -445,11 +440,11 @@ export class factory { ) { return arkts.factory.updateExpressionStatement( statement, - this.transformBuilderLambda(statement.expression, projectConfig) + this.transformBuilderLambda(statement.expression) ); } if (arkts.isIfStatement(statement)) { - return this.updateIfElseContentBodyInBuilderLambda(statement, projectConfig); + return this.updateIfElseContentBodyInBuilderLambda(statement); } return statement; @@ -496,13 +491,15 @@ export class factory { factory.transformBuilderLambdaMethodDecl(method) ); - return this.updateBuilderLambdaMethodDecl( + const newNode = this.updateBuilderLambdaMethodDecl( node, - this.createStyleArgInBuilderLambdaDecl(typeNode, isFunctionCall), + [this.createStyleArgInBuilderLambdaDecl(typeNode, isFunctionCall)], removeAnnotationByName(func.annotations, BuilderLambdaNames.ANNOTATION_NAME), replaceBuilderLambdaDeclMethodName(node.name.name), externalSourceName ).setOverloads(newOverloads); + arkts.NodeCache.getInstance().collect(newNode); + return newNode; } /** @@ -542,7 +539,7 @@ export class factory { /** * transform `@ComponentBuilder` in non-declared calls. */ - static transformBuilderLambda(node: arkts.CallExpression, projectConfig: ProjectConfig | undefined): arkts.AstNode { + static transformBuilderLambda(node: arkts.CallExpression): arkts.AstNode { let instanceCalls: InstanceCallInfo[] = []; let leaf: arkts.CallExpression = node; @@ -584,18 +581,16 @@ export class factory { instanceCalls = instanceCalls.reverse(); this.updateAnimation(instanceCalls); lambdaBody = arkts.factory.createIdentifier(BuilderLambdaNames.STYLE_ARROW_PARAM_NAME); + arkts.NodeCache.getInstance().collect(lambdaBody); instanceCalls.forEach((callInfo) => { - lambdaBody = this.createStyleLambdaBody(lambdaBody!, callInfo, projectConfig); + lambdaBody = this.createStyleLambdaBody(lambdaBody!, callInfo); }); } - const args: (arkts.AstNode | undefined)[] = this.generateArgsInBuilderLambda( - leaf, - lambdaBody!, - declInfo, - projectConfig - ); - return arkts.factory.updateCallExpression(node, replace, leaf.typeArguments, filterDefined(args)); + const args: (arkts.AstNode | undefined)[] = this.generateArgsInBuilderLambda(leaf, lambdaBody!, declInfo); + const newNode = arkts.factory.updateCallExpression(node, replace, leaf.typeArguments, filterDefined(args)); + arkts.NodeCache.getInstance().collect(newNode); + return newNode; } /* diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts index 274ae7fd2..e0118e037 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts @@ -15,9 +15,9 @@ import * as arkts from '@koalaui/libarkts'; import { isAnnotation, matchPrefix } from '../../common/arkts-utils'; -import { BuilderLambdaNames } from '../utils'; +import { BuilderLambdaNames, isCustomComponentAnnotation } from '../utils'; import { DeclarationCollector } from '../../common/declaration-collector'; -import { ARKUI_IMPORT_PREFIX_NAMES, BindableDecl, Dollars } from '../../common/predefines'; +import { ARKUI_IMPORT_PREFIX_NAMES, BindableDecl, Dollars, StructDecoratorNames } from '../../common/predefines'; import { ImportCollector } from '../../common/import-collector'; export type BuilderLambdaDeclInfo = { @@ -34,6 +34,37 @@ export type InstanceCallInfo = { call: arkts.CallExpression; }; +export type BuilderLambdaArgInfo = { + isFunctionCall: boolean; +}; + +export type BuilderLambdaReusableArgInfo = { + isReusable?: boolean; + reuseId?: string; +}; + +export type BuilderLambdaSecondLastArgInfo = BuilderLambdaArgInfo & + BuilderLambdaReusableArgInfo; + +export function buildSecondLastArgInfo( + type: arkts.Identifier | undefined, + isFunctionCall: boolean +): BuilderLambdaSecondLastArgInfo { + let isReusable: boolean | undefined; + let reuseId: string | undefined; + if (!isFunctionCall && !!type) { + const customComponentDecl = arkts.getDecl(type); + isReusable = + !!customComponentDecl && + arkts.isClassDefinition(customComponentDecl) && + customComponentDecl.annotations.some((anno) => + isCustomComponentAnnotation(anno, StructDecoratorNames.RESUABLE) + ); + reuseId = isReusable ? type.name : undefined; + } + return { isFunctionCall, isReusable, reuseId }; +} + /** * Used in finding "XXX" in BuilderLambda("XXX") * @deprecated diff --git a/arkui-plugins/ui-plugins/checked-transformer.ts b/arkui-plugins/ui-plugins/checked-transformer.ts index 31ec5a48e..682c9b43a 100644 --- a/arkui-plugins/ui-plugins/checked-transformer.ts +++ b/arkui-plugins/ui-plugins/checked-transformer.ts @@ -19,22 +19,6 @@ import { factory as structFactory } from './struct-translators/factory'; import { factory as builderLambdaFactory } from './builder-lambda-translators/factory'; import { factory as entryFactory } from './entry-translators/factory'; import { AbstractVisitor } from '../common/abstract-visitor'; -import { - addMemoAnnotation, - collectCustomComponentScopeInfo, - CustomComponentNames, - isCustomComponentClass, -} from './utils'; -import { - CustomComponentScopeInfo, - ScopeInfoCollection, - findCanAddMemoFromArrowFunction, - isResourceNode, - LoaderJson, - ResourceInfo, - loadBuildJson, - initResourceInfo -} from './struct-translators/utils'; import { isBuilderLambda, isBuilderLambdaMethodDecl } from './builder-lambda-translators/utils'; import { isEntryWrapperClass } from './entry-translators/utils'; import { ImportCollector } from '../common/import-collector'; @@ -43,6 +27,17 @@ import { PropertyCache } from './property-translators/utils'; import { isArkUICompatible, generateArkUICompatible } from './interop/interop'; import { checkCustomDialogController, insertImportDeclaration, transformDeclaration } from './customdialog'; import { LogCollector } from '../common/log-collector'; +import { + CustomComponentScopeInfo, + initResourceInfo, + isResourceNode, + loadBuildJson, + LoaderJson, + ResourceInfo, + ScopeInfoCollection, +} from './struct-translators/utils'; +import { collectCustomComponentScopeInfo, CustomComponentNames, isCustomComponentClass } from './utils'; +import { findAndCollectMemoableNode } from '../collectors/memo-collectors/factory'; export class CheckedTransformer extends AbstractVisitor { private scope: ScopeInfoCollection; @@ -114,13 +109,17 @@ export class CheckedTransformer extends AbstractVisitor { visitor(beforeChildren: arkts.AstNode): arkts.AstNode { this.enter(beforeChildren); if (arkts.isCallExpression(beforeChildren) && isBuilderLambda(beforeChildren)) { - const lambda = builderLambdaFactory.transformBuilderLambda(beforeChildren, this.projectConfig); + const lambda = builderLambdaFactory.transformBuilderLambda(beforeChildren); return this.visitEachChild(lambda); } else if (arkts.isMethodDefinition(beforeChildren) && isBuilderLambdaMethodDecl(beforeChildren)) { - const lambda = builderLambdaFactory.transformBuilderLambdaMethodDecl(beforeChildren, this.externalSourceName); + const lambda = builderLambdaFactory.transformBuilderLambdaMethodDecl( + beforeChildren, + this.externalSourceName + ); return this.visitEachChild(lambda); } const node = this.visitEachChild(beforeChildren); + findAndCollectMemoableNode(node); if (this.isCustomDialogController()) { return this.processCustomDialogController(node); } else if ( @@ -143,16 +142,13 @@ export class CheckedTransformer extends AbstractVisitor { return generateArkUICompatible(node as arkts.CallExpression); } else if (arkts.isTSInterfaceDeclaration(node)) { return structFactory.tranformInterfaceMembers(node, this.externalSourceName); - } else if (findCanAddMemoFromArrowFunction(node)) { - return addMemoAnnotation(node); - } else if (arkts.isEtsScript(node)) { + } else if (arkts.isBlockStatement(node)) { + return checkCustomDialogController(node); + } + if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { ImportCollector.getInstance().insertCurrentImports(this.program); LogCollector.getInstance().shouldIgnoreError(this.projectConfig?.ignoreError); LogCollector.getInstance().emitLogInfo(); - } else if (arkts.isTSTypeAliasDeclaration(node)) { - return structFactory.transformTSTypeAlias(node); - } else if (arkts.isBlockStatement(node)) { - return checkCustomDialogController(node); } return node; } diff --git a/arkui-plugins/ui-plugins/component-transformer.ts b/arkui-plugins/ui-plugins/component-transformer.ts index fa3067b00..6c9642d8e 100644 --- a/arkui-plugins/ui-plugins/component-transformer.ts +++ b/arkui-plugins/ui-plugins/component-transformer.ts @@ -33,11 +33,12 @@ import { filterDefined, collect, createAndInsertImportDeclaration, + isDecoratorAnnotation, } from '../common/arkts-utils'; import { ProjectConfig } from '../common/plugin-context'; import { getEntryParams } from './entry-translators/utils'; import { factory as entryFactory } from './entry-translators/factory'; -import { hasDecoratorName, findDecoratorInfos, isDecoratorAnnotation } from './property-translators/utils'; +import { hasDecoratorName, findDecoratorInfos } from './property-translators/utils'; import { factory } from './ui-factory'; import { factory as propertyFactory } from './property-translators/factory'; import { StructMap } from '../common/program-visitor'; @@ -173,7 +174,7 @@ export class ComponentTransformer extends AbstractVisitor { const scopeInfo: ScopeInfo = this.scopeInfos[this.scopeInfos.length - 1]; return !!scopeInfo.annotations.customdialog; } - + hasController(node: arkts.ETSTypeReference, key_name: string): void { const ident = node.part?.name; if (ident && arkts.isIdentifier(ident) && ident.name === 'CustomDialogController') { @@ -329,7 +330,6 @@ export class ComponentTransformer extends AbstractVisitor { if (!className || scopeInfo?.name !== className) { return node; } - arkts.insertGlobalStructInfo(className); if (arkts.isStructDeclaration(node)) { this.collectComponentMembers(node, className); } diff --git a/arkui-plugins/ui-plugins/entry-translators/factory.ts b/arkui-plugins/ui-plugins/entry-translators/factory.ts index b2a09d567..e4dd29d22 100644 --- a/arkui-plugins/ui-plugins/entry-translators/factory.ts +++ b/arkui-plugins/ui-plugins/entry-translators/factory.ts @@ -20,6 +20,7 @@ import { ENTRY_POINT_IMPORT_SOURCE_NAME, EntryWrapperNames, NavigationNames } fr import { ProjectConfig } from '../../common/plugin-context'; import { factory as uiFactory } from '../ui-factory'; import { getRelativePagePath } from './utils'; +import { addMemoAnnotation } from '../../collectors/memo-collectors/utils'; export class factory { /** @@ -218,7 +219,8 @@ export class factory { !!member.scriptFunction.id && member.scriptFunction.id.name === EntryWrapperNames.ENTRY_FUNC ) { - member.scriptFunction.setAnnotations([annotation('memo')]); + addMemoAnnotation(member.scriptFunction); + arkts.NodeCache.getInstance().collect(member); } }); } diff --git a/arkui-plugins/ui-plugins/index.ts b/arkui-plugins/ui-plugins/index.ts index 792ca0de8..3014b24c0 100644 --- a/arkui-plugins/ui-plugins/index.ts +++ b/arkui-plugins/ui-plugins/index.ts @@ -134,9 +134,6 @@ function checkedTransform(this: PluginContext): arkts.EtsScript | undefined { cachePath, program.fileNameWithExtension ); - arkts.Performance.getInstance().createEvent('ui-recheck'); - arkts.recheckSubtree(script); - arkts.Performance.getInstance().stopEvent('ui-recheck', true); this.setArkTSAst(script); arkts.Performance.getInstance().memoryTrackerGetDelta('UIPlugin:UI-AfterCheck'); arkts.Performance.getInstance().stopMemRecord('Node:UIPlugin:UI-AfterCheck'); diff --git a/arkui-plugins/ui-plugins/property-translators/builderParam.ts b/arkui-plugins/ui-plugins/property-translators/builderParam.ts index 44266f3d1..b18f9d239 100644 --- a/arkui-plugins/ui-plugins/property-translators/builderParam.ts +++ b/arkui-plugins/ui-plugins/property-translators/builderParam.ts @@ -17,18 +17,14 @@ import * as arkts from '@koalaui/libarkts'; import { backingField, expectName } from '../../common/arkts-utils'; import { DecoratorNames } from '../../common/predefines'; -import { - createGetter, - createSetter, - generateThisBacking, - hasDecorator, - removeDecorator, - PropertyCache, -} from './utils'; +import { createGetter, createSetter, generateThisBacking, hasDecorator, removeDecorator, PropertyCache } from './utils'; import { InterfacePropertyTranslator, InterfacePropertyTypes, PropertyTranslator } from './base'; import { GetterSetter, InitializerConstructor } from './types'; -import { addMemoAnnotation, findCanAddMemoFromParamExpression, findCanAddMemoFromTypeAnnotation } from '../utils'; import { factory } from './factory'; +import { + addMemoAnnotation, + findCanAddMemoFromTypeAnnotation, +} from '../../collectors/memo-collectors/utils'; export class BuilderParamTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { @@ -45,6 +41,10 @@ export class BuilderParamTranslator extends PropertyTranslator implements Initia } translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { + const propertyType = this.property.typeAnnotation; + if (!!propertyType && (arkts.isETSFunctionType(propertyType) || arkts.isETSUnionType(propertyType))) { + addMemoAnnotation(propertyType); + } const field: arkts.ClassProperty = factory.createOptionalClassProperty( newName, this.property, @@ -52,18 +52,13 @@ export class BuilderParamTranslator extends PropertyTranslator implements Initia arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE, true ); + arkts.NodeCache.getInstance().collect(field); const thisGetValue: arkts.Expression = generateThisBacking(newName, false, true); const thisSetValue: arkts.Expression = generateThisBacking(newName, false, false); - const getter: arkts.MethodDefinition = this.translateGetter( - originalName, - this.property.typeAnnotation, - thisGetValue - ); - const setter: arkts.MethodDefinition = this.translateSetter( - originalName, - this.property.typeAnnotation, - thisSetValue - ); + const getter: arkts.MethodDefinition = this.translateGetter(originalName, propertyType?.clone(), thisGetValue); + arkts.NodeCache.getInstance().collect(getter); + const setter: arkts.MethodDefinition = this.translateSetter(originalName, propertyType?.clone(), thisSetValue); + arkts.NodeCache.getInstance().collect(setter); return [field, getter, setter]; } @@ -134,7 +129,7 @@ export class BuilderParamInterfaceTranslator e private updateBuilderParamMethodInInterface(method: arkts.MethodDefinition): arkts.MethodDefinition { if (method.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET) { const type: arkts.TypeNode | undefined = method.scriptFunction.returnTypeAnnotation; - if (findCanAddMemoFromTypeAnnotation(type)) { + if (!!type && (arkts.isETSFunctionType(type) || arkts.isETSUnionType(type))) { addMemoAnnotation(type); } const newOverLoads = method.overloads.map((overload) => { @@ -145,12 +140,15 @@ export class BuilderParamInterfaceTranslator e }); method.setOverloads(newOverLoads); removeDecorator(method, DecoratorNames.BUILDER_PARAM); + arkts.NodeCache.getInstance().collect(method, { isGetter: true }); } else if (method.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET) { - const param: arkts.Expression | undefined = method.scriptFunction.params.at(0); - if (findCanAddMemoFromParamExpression(param)) { - addMemoAnnotation(param); + const param = method.scriptFunction.params.at(0)! as arkts.ETSParameterExpression; + const type = param.type; + if (!!type && (arkts.isETSFunctionType(type) || arkts.isETSUnionType(type))) { + addMemoAnnotation(type); } removeDecorator(method, DecoratorNames.BUILDER_PARAM); + arkts.NodeCache.getInstance().collect(method, { isSetter: true }); } return method; } diff --git a/arkui-plugins/ui-plugins/property-translators/factory.ts b/arkui-plugins/ui-plugins/property-translators/factory.ts index 882a7b4ba..18907cf50 100644 --- a/arkui-plugins/ui-plugins/property-translators/factory.ts +++ b/arkui-plugins/ui-plugins/property-translators/factory.ts @@ -18,7 +18,8 @@ import { GenSymGenerator } from '../../common/gensym-generator'; import { DecoratorNames, DECORATOR_TYPE_MAP, StateManagementTypes } from '../../common/predefines'; import { factory as UIFactory } from '../ui-factory'; import { collectStateManagementTypeImport, getValueInAnnotation, hasDecorator, removeDecorator } from './utils'; -import { addMemoAnnotation, findCanAddMemoFromTypeAnnotation, CustomComponentNames } from '../utils'; +import { CustomComponentNames } from '../utils'; +import { addMemoAnnotation, findCanAddMemoFromTypeAnnotation } from '../../collectors/memo-collectors/utils'; import { annotation } from '../../common/arkts-utils'; export class factory { diff --git a/arkui-plugins/ui-plugins/property-translators/utils.ts b/arkui-plugins/ui-plugins/property-translators/utils.ts index 08c0fb11f..56d189490 100644 --- a/arkui-plugins/ui-plugins/property-translators/utils.ts +++ b/arkui-plugins/ui-plugins/property-translators/utils.ts @@ -14,18 +14,20 @@ */ import * as arkts from '@koalaui/libarkts'; -import { DeclarationCollector } from '../../common/declaration-collector'; import { ImportCollector } from '../../common/import-collector'; -import { matchPrefix } from '../../common/arkts-utils'; +import { isDecoratorAnnotation } from '../../common/arkts-utils'; import { - ARKUI_IMPORT_PREFIX_NAMES, DecoratorIntrinsicNames, DecoratorNames, DECORATOR_TYPE_MAP, StateManagementTypes, GetSetTypes, } from '../../common/predefines'; -import { addMemoAnnotation, findCanAddMemoFromParamExpression, findCanAddMemoFromTypeAnnotation } from '../utils'; +import { + addMemoAnnotation, + findCanAddMemoFromParameter, + findCanAddMemoFromTypeAnnotation, +} from '../../collectors/memo-collectors/utils'; export interface DecoratorInfo { annotation: arkts.AnnotationUsage; @@ -39,28 +41,6 @@ export function isDecoratorIntrinsicAnnotation( return !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === decoratorName; } -export function isDecoratorAnnotation( - anno: arkts.AnnotationUsage, - decoratorName: DecoratorNames, - ignoreDecl?: boolean -): boolean { - if (!(!!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === decoratorName)) { - return false; - } - if (!ignoreDecl) { - const decl = arkts.getDecl(anno.expr); - if (!decl) { - return false; - } - const moduleName: string = arkts.getProgramFromAstNode(decl).moduleName; - if (!moduleName || !matchPrefix(ARKUI_IMPORT_PREFIX_NAMES, moduleName)) { - return false; - } - DeclarationCollector.getInstance().collect(decl); - } - return true; -} - export function removeDecorator( property: arkts.ClassProperty | arkts.ClassDefinition | arkts.MethodDefinition, decoratorName: DecoratorNames, @@ -216,7 +196,7 @@ export function createSetter( arkts.factory.createIdentifier('value', type?.clone()), undefined ); - if (needMemo && findCanAddMemoFromParamExpression(param)) { + if (needMemo && findCanAddMemoFromParameter(param)) { addMemoAnnotation(param); } const scriptFunction = arkts.factory.createScriptFunction( diff --git a/arkui-plugins/ui-plugins/struct-translators/factory.ts b/arkui-plugins/ui-plugins/struct-translators/factory.ts index eae984fb7..129e43f02 100644 --- a/arkui-plugins/ui-plugins/struct-translators/factory.ts +++ b/arkui-plugins/ui-plugins/struct-translators/factory.ts @@ -15,19 +15,17 @@ import * as arkts from '@koalaui/libarkts'; import { - addMemoAnnotation, CustomComponentNames, getCustomComponentOptionsName, getGettersFromClassDecl, getTypeNameFromTypeParameter, getTypeParamsFromClassDecl, isCustomComponentInterface, - MemoNames, - isKnownMethodDefinition, + isKnownMethodDefinition } from '../utils'; import { factory as uiFactory } from '../ui-factory'; import { factory as propertyFactory } from '../property-translators/factory'; -import { collect, filterDefined, annotation } from '../../common/arkts-utils'; +import { collect, filterDefined } from '../../common/arkts-utils'; import { classifyObservedTrack, classifyProperty, @@ -50,7 +48,6 @@ import { } from './utils'; import { collectStateManagementTypeImport, hasDecorator, PropertyCache } from '../property-translators/utils'; import { ProjectConfig } from '../../common/plugin-context'; -import { DeclarationCollector } from '../../common/declaration-collector'; import { ImportCollector } from '../../common/import-collector'; import { AnimationNames, @@ -62,9 +59,10 @@ import { RESOURCE_TYPE, } from '../../common/predefines'; import { ObservedTrackTranslator } from '../property-translators/observedTrack'; +import { addMemoAnnotation } from '../../collectors/memo-collectors/utils'; export class factory { - /* + /** * update class `constructor` to private. */ static setStructConstructorToPrivate(member: arkts.MethodDefinition): arkts.MethodDefinition { @@ -74,7 +72,7 @@ export class factory { return member; } - /* + /** * create __initializeStruct method. */ static createInitializeStruct(optionsTypeName: string, scope: CustomComponentScopeInfo): arkts.MethodDefinition { @@ -111,7 +109,7 @@ export class factory { ); } - /* + /** * create __updateStruct method. */ static createUpdateStruct(optionsTypeName: string, scope: CustomComponentScopeInfo): arkts.MethodDefinition { @@ -149,7 +147,7 @@ export class factory { ); } - /* + /** * create __toRecord method when the component is decorated with @Reusable. */ static createToRecord(optionsTypeName: string, scope: CustomComponentScopeInfo): arkts.MethodDefinition { @@ -184,7 +182,7 @@ export class factory { ); } - /* + /** * generate `const paramsCasted = (params as )`. */ static generateParamsCasted(optionsTypeName: string): arkts.VariableDeclaration { @@ -205,7 +203,7 @@ export class factory { ); } - /* + /** * generate Record type. */ static generateTypeRecord(): arkts.ETSTypeReference { @@ -220,7 +218,7 @@ export class factory { ); } - /* + /** * create type reference with type name, e.g. number. */ static generateTypeReferenceWithTypeName(typeName: string): arkts.ETSTypeReference { @@ -229,7 +227,7 @@ export class factory { ); } - /* + /** * create type reference with type name, e.g. number. */ static updateCustomComponentClass( @@ -250,7 +248,7 @@ export class factory { ); } - /* + /** * add headers for animation & @AnimatableExtend in CommonMethod */ static modifyExternalComponentCommon(node: arkts.TSInterfaceDeclaration): arkts.TSInterfaceDeclaration { @@ -274,7 +272,7 @@ export class factory { ); } - /* + /** * helper to create value parameter for AnimatableExtend methods */ static createAniExtendValueParam(): arkts.ETSParameterExpression { @@ -294,7 +292,7 @@ export class factory { ); } - /* + /** * generate __createOrSetAnimatableProperty(...) for AnimatableExtend */ static createOrSetAniProperty(): arkts.MethodDefinition { @@ -337,7 +335,7 @@ export class factory { ); } - /* + /** * generate animationStart(...) and animationStop(...) */ static createAnimationMethod(key: string): arkts.MethodDefinition { @@ -699,17 +697,6 @@ export class factory { return arkts.factory.updateClassDeclaration(node, newClassDef); } - static transformTSTypeAlias(node: arkts.TSTypeAliasDeclaration): arkts.TSTypeAliasDeclaration { - if ( - node.typeAnnotation && - arkts.isETSFunctionType(node.typeAnnotation) && - hasDecorator(node.typeAnnotation, DecoratorNames.BUILDER) - ) { - node.typeAnnotation.setAnnotations([annotation(MemoNames.MEMO)]); - } - return node; - } - static updateObservedTrackClassDef(node: arkts.ClassDefinition): arkts.ClassDefinition { const isObserved: boolean = hasDecorator(node, DecoratorNames.OBSERVED); const classHasTrack: boolean = node.body.some( diff --git a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts index 1a9c88261..3101184cd 100644 --- a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts +++ b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts @@ -16,15 +16,9 @@ import * as arkts from '@koalaui/libarkts'; import { AbstractVisitor } from '../../common/abstract-visitor'; import { ProjectConfig } from '../../common/plugin-context'; -import { - addMemoAnnotation, - collectCustomComponentScopeInfo, - CustomComponentNames, - isCustomComponentClass, -} from '../utils'; +import { collectCustomComponentScopeInfo, CustomComponentNames, isCustomComponentClass } from '../utils'; import { CustomComponentScopeInfo, - findCanAddMemoFromArrowFunction, isResourceNode, ScopeInfoCollection, LoaderJson, @@ -38,6 +32,7 @@ import { factory as entryFactory } from '../entry-translators/factory'; import { ImportCollector } from '../../common/import-collector'; import { DeclarationCollector } from '../../common/declaration-collector'; import { PropertyCache } from '../property-translators/utils'; +import { generateArkUICompatible, isArkUICompatible } from '../interop/interop'; export class StructTransformer extends AbstractVisitor { private scope: ScopeInfoCollection; @@ -105,11 +100,12 @@ export class StructTransformer extends AbstractVisitor { return factory.transformNormalClass(node); } else if (arkts.isCallExpression(node) && isResourceNode(node)) { return factory.transformResource(node, this.projectConfig, this.resourceInfo); + } else if (isArkUICompatible(node)) { + return generateArkUICompatible(node as arkts.CallExpression); } else if (arkts.isTSInterfaceDeclaration(node)) { return factory.tranformInterfaceMembers(node, this.externalSourceName); - } else if (findCanAddMemoFromArrowFunction(node)) { - return addMemoAnnotation(node); - } else if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { + } + if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { ImportCollector.getInstance().insertCurrentImports(this.program); } return node; diff --git a/arkui-plugins/ui-plugins/struct-translators/utils.ts b/arkui-plugins/ui-plugins/struct-translators/utils.ts index eba0ce6a4..9be1a309c 100644 --- a/arkui-plugins/ui-plugins/struct-translators/utils.ts +++ b/arkui-plugins/ui-plugins/struct-translators/utils.ts @@ -16,8 +16,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as arkts from '@koalaui/libarkts'; -import { CustomComponentInfo, isMemoAnnotation, MemoNames } from '../utils'; -import { isDecoratorAnnotation } from '../property-translators/utils'; +import { CustomComponentInfo } from '../utils'; import { matchPrefix } from '../../common/arkts-utils'; import { ARKUI_IMPORT_PREFIX_NAMES, @@ -107,38 +106,6 @@ export function isResourceNode(node: arkts.CallExpression, ignoreDecl: boolean = return true; } -export function isMemoCall(node: arkts.AstNode): node is arkts.CallExpression { - if (!arkts.isCallExpression(node)) { - return false; - } - const expr: arkts.AstNode = node.expression; - const decl: arkts.AstNode | undefined = arkts.getDecl(expr); - - if (!decl) { - return false; - } - - if (arkts.isMethodDefinition(decl)) { - return decl.scriptFunction.annotations.some( - (anno) => isDecoratorAnnotation(anno, DecoratorNames.BUILDER) || isMemoAnnotation(anno, MemoNames.MEMO) - ); - } - return false; -} - -export function findCanAddMemoFromArrowFunction(node: arkts.AstNode): node is arkts.ArrowFunctionExpression { - if (!arkts.isArrowFunctionExpression(node)) { - return false; - } - const hasMemo: boolean = node.annotations.some((anno) => isMemoAnnotation(anno, MemoNames.MEMO)); - if (!hasMemo && !!node.scriptFunction.body && arkts.isBlockStatement(node.scriptFunction.body)) { - return node.scriptFunction.body.statements.some( - (st) => arkts.isExpressionStatement(st) && isMemoCall(st.expression) - ); - } - return false; -} - /** * Read the content of file 'loader.json'. * diff --git a/arkui-plugins/ui-plugins/ui-factory.ts b/arkui-plugins/ui-plugins/ui-factory.ts index 465a572b4..9e7d5fa3b 100644 --- a/arkui-plugins/ui-plugins/ui-factory.ts +++ b/arkui-plugins/ui-plugins/ui-factory.ts @@ -15,17 +15,16 @@ import * as arkts from '@koalaui/libarkts'; import { - addMemoAnnotation, BuilderLambdaNames, CustomComponentAnontations, CustomComponentNames, - findCanAddMemoFromParamExpression, hasNullOrUndefinedType, hasPropertyInAnnotation, } from './utils'; import { PartialExcept, PartialNested, PartialNestedExcept } from '../common/safe-types'; import { DecoratorNames } from '../common/predefines'; import { needDefiniteOrOptionalModifier } from './property-translators/utils'; +import { addMemoAnnotation } from '../collectors/memo-collectors/utils'; export interface ScriptFunctionConfiguration { key: arkts.Identifier | undefined; @@ -138,9 +137,7 @@ export class factory { static createContentParameter(): arkts.ETSParameterExpression { const contentParam: arkts.Identifier = factory.createContentIdentifier(); const param: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration(contentParam, undefined); - if (findCanAddMemoFromParamExpression(param)) { - addMemoAnnotation(param); - } + addMemoAnnotation(param); return param; } @@ -305,7 +302,7 @@ export class factory { return newAnnotationDecl; } - /* + /** * add alias: to @Provide annotation when no alias in @Provide({...}). */ static processNoAliasProvideVariable(property: arkts.ClassProperty): void { @@ -333,7 +330,7 @@ export class factory { property.setAnnotations(newAnnos); } - /* + /** * create class property : `alias: `. */ static createAliasClassProperty(value: arkts.Identifier): arkts.ClassProperty { @@ -346,7 +343,7 @@ export class factory { ); } - /* + /** * add optional or definite modifier for class property needs initializing without assignment. */ static PreprocessClassPropertyModifier(st: arkts.AstNode): arkts.AstNode { @@ -360,7 +357,7 @@ export class factory { return st; } - /* + /** * create class implements : `implements `. */ static createClassImplements( diff --git a/arkui-plugins/ui-plugins/utils.ts b/arkui-plugins/ui-plugins/utils.ts index 0c8ca988d..562c74f4e 100644 --- a/arkui-plugins/ui-plugins/utils.ts +++ b/arkui-plugins/ui-plugins/utils.ts @@ -14,10 +14,9 @@ */ import * as arkts from '@koalaui/libarkts'; -import { annotation, matchPrefix } from '../common/arkts-utils'; -import { ARKUI_IMPORT_PREFIX_NAMES, MEMO_IMPORT_SOURCE_NAME, StructDecoratorNames } from '../common/predefines'; +import { matchPrefix } from '../common/arkts-utils'; +import { ARKUI_IMPORT_PREFIX_NAMES, StructDecoratorNames } from '../common/predefines'; import { DeclarationCollector } from '../common/declaration-collector'; -import { ImportCollector } from '../common/import-collector'; export enum CustomComponentNames { COMPONENT_BUILD_ORI = 'build', @@ -47,10 +46,6 @@ export enum BuilderLambdaNames { CONTENT_PARAM_NAME = 'content' } -export enum MemoNames { - MEMO = 'memo', -} - // IMPORT export function findImportSourceByName(importName: string): string { const source = DeclarationCollector.getInstance().findExternalSourceFromName(importName); @@ -278,84 +273,6 @@ export function getCustomComponentOptionsName(className: string): string { return `${CustomComponentNames.COMPONENT_INTERFACE_PREFIX}${className}`; } -// MEMO -export type MemoAstNode = - | arkts.ScriptFunction - | arkts.ETSParameterExpression - | arkts.ClassProperty - | arkts.TSTypeAliasDeclaration - | arkts.ETSFunctionType - | arkts.ArrowFunctionExpression - | arkts.ETSUnionType; - -export function hasMemoAnnotation(node: T): boolean { - return node.annotations.some((it) => isMemoAnnotation(it, MemoNames.MEMO)); -} - -export function collectMemoAnnotationImport(memoName: MemoNames = MemoNames.MEMO): void { - ImportCollector.getInstance().collectImport(memoName); -} - -export function collectMemoAnnotationSource(memoName: MemoNames = MemoNames.MEMO): void { - ImportCollector.getInstance().collectSource(memoName, MEMO_IMPORT_SOURCE_NAME); -} - -export function findCanAddMemoFromTypeAnnotation( - typeAnnotation: arkts.AstNode | undefined -): typeAnnotation is arkts.ETSFunctionType { - if (!typeAnnotation) { - return false; - } - if (arkts.isETSFunctionType(typeAnnotation)) { - return true; - } else if (arkts.isETSUnionType(typeAnnotation)) { - return typeAnnotation.types.some((type) => arkts.isETSFunctionType(type)); - } - return false; -} - -export function findCanAddMemoFromParamExpression( - param: arkts.AstNode | undefined -): param is arkts.ETSParameterExpression { - if (!param) { - return false; - } - if (!arkts.isEtsParameterExpression(param)) { - return false; - } - const type = param.type; - return findCanAddMemoFromTypeAnnotation(type); -} - -export function isMemoAnnotation(node: arkts.AnnotationUsage, memoName: MemoNames): boolean { - if (!(node.expr !== undefined && arkts.isIdentifier(node.expr) && node.expr.name === memoName)) { - return false; - } - return true; -} - -export function addMemoAnnotation(node: T, memoName: MemoNames = MemoNames.MEMO): T { - collectMemoAnnotationSource(memoName); - if (arkts.isETSUnionType(node)) { - const functionType = node.types.find((type) => arkts.isETSFunctionType(type)); - if (!functionType) { - return node; - } - addMemoAnnotation(functionType, memoName); - return node; - } - const newAnnotations: arkts.AnnotationUsage[] = [ - ...node.annotations.filter((it) => !isMemoAnnotation(it, memoName)), - annotation(memoName), - ]; - collectMemoAnnotationImport(memoName); - if (arkts.isEtsParameterExpression(node)) { - node.annotations = newAnnotations; - return node; - } - return node.setAnnotations(newAnnotations) as T; -} - /** * Determine whether it is method with specified name. * diff --git a/koala-wrapper/native/src/bridges.cc b/koala-wrapper/native/src/bridges.cc index b6c2593a6..f06308b60 100644 --- a/koala-wrapper/native/src/bridges.cc +++ b/koala-wrapper/native/src/bridges.cc @@ -20,24 +20,6 @@ #include #include "memoryTracker.h" -std::set globalStructInfo; -std::mutex g_structMutex; - -void impl_InsertGlobalStructInfo(KNativePointer contextPtr, KStringPtr& instancePtr) -{ - std::lock_guard lock(g_structMutex); - globalStructInfo.insert(getStringCopy(instancePtr)); - return; -} -KOALA_INTEROP_V2(InsertGlobalStructInfo, KNativePointer, KStringPtr); - -KBoolean impl_HasGlobalStructInfo(KNativePointer contextPtr, KStringPtr& instancePtr) -{ - std::lock_guard lock(g_structMutex); - return globalStructInfo.count(getStringCopy(instancePtr)); -} -KOALA_INTEROP_2(HasGlobalStructInfo, KBoolean, KNativePointer, KStringPtr); - KBoolean impl_ClassDefinitionIsFromStructConst(KNativePointer contextPtr, KNativePointer instancePtr) { auto context = reinterpret_cast(contextPtr); @@ -661,6 +643,7 @@ KNativePointer impl_CreateSuggestionInfo(KNativePointer context, KNativePointer } KOALA_INTEROP_5(CreateSuggestionInfo, KNativePointer, KNativePointer, KNativePointer, KStringArray, KInt, KStringPtr); + void impl_LogDiagnostic(KNativePointer context, KNativePointer kind, KStringArray argvPtr, KInt argc, KNativePointer pos) { @@ -680,6 +663,7 @@ void impl_LogDiagnostic(KNativePointer context, KNativePointer kind, KStringArra GetImpl()->LogDiagnostic(_context_, _kind_, argv, argc, _pos_); } KOALA_INTEROP_V5(LogDiagnostic, KNativePointer, KNativePointer, KStringArray, KInt, KNativePointer); + void impl_LogDiagnosticWithSuggestion(KNativePointer context, KNativePointer diagnosticInfo, KNativePointer suggestionInfo, KNativePointer range) { @@ -691,6 +675,14 @@ void impl_LogDiagnosticWithSuggestion(KNativePointer context, KNativePointer dia } KOALA_INTEROP_V4(LogDiagnosticWithSuggestion, KNativePointer, KNativePointer, KNativePointer, KNativePointer); +KBoolean impl_CallExpressionIsTrailingCallConst(KNativePointer context, KNativePointer receiver) +{ + const auto _context = reinterpret_cast(context); + const auto _receiver = reinterpret_cast(receiver); + return GetImpl()->CallExpressionIsTrailingCallConst(_context, _receiver); +} +KOALA_INTEROP_2(CallExpressionIsTrailingCallConst, KBoolean, KNativePointer, KNativePointer); + MemoryTracker tracker; void impl_MemoryTrackerReset(KNativePointer context) { diff --git a/koala-wrapper/src/Es2pandaNativeModule.ts b/koala-wrapper/src/Es2pandaNativeModule.ts index 1a9ff787c..a707b498d 100644 --- a/koala-wrapper/src/Es2pandaNativeModule.ts +++ b/koala-wrapper/src/Es2pandaNativeModule.ts @@ -910,13 +910,6 @@ export class Es2pandaNativeModule { throw new Error('CreateCacheContextFromFile was not overloaded by native module initialization'); } - _InsertGlobalStructInfo(context: KNativePointer, str: String): void { - throw new Error('InsertGlobalStructInfo was not overloaded by native module initialization'); - } - - _HasGlobalStructInfo(context: KNativePointer, str: String): KBoolean { - throw new Error('HasGlobalStructInfo was not overloaded by native module initialization'); - } _CreateDiagnosticKind(context: KNativePointer, message: string, type: PluginDiagnosticType): KNativePointer { throw new Error('Not implemented'); } @@ -944,14 +937,18 @@ export class Es2pandaNativeModule { } _MemoryTrackerReset(context: KNativePointer): void { - throw new Error('CallExpressionIsTrailingCallConst was not overloaded by native module initialization'); + throw new Error('MemoryTrackerReset was not overloaded by native module initialization'); } _MemoryTrackerGetDelta(context: KNativePointer): void { - throw new Error('CallExpressionIsTrailingCallConst was not overloaded by native module initialization'); + throw new Error('MemoryTrackerGetDelta was not overloaded by native module initialization'); } _MemoryTrackerPrintCurrent(context: KNativePointer): void { + throw new Error('MemoryTrackerPrintCurrent was not overloaded by native module initialization'); + } + + _CallExpressionIsTrailingCallConst(context: KNativePointer, node: KNativePointer): boolean { throw new Error('CallExpressionIsTrailingCallConst was not overloaded by native module initialization'); } } diff --git a/koala-wrapper/src/arkts-api/class-by-peer.ts b/koala-wrapper/src/arkts-api/class-by-peer.ts index f8d79996c..a12afcba8 100644 --- a/koala-wrapper/src/arkts-api/class-by-peer.ts +++ b/koala-wrapper/src/arkts-api/class-by-peer.ts @@ -26,7 +26,7 @@ export function clearNodeCache(): void { cache.clear(); } -function getOrPut(peer: KNativePointer, create: (peer: KNativePointer) => AstNode): AstNode { +export function getOrPut(peer: KNativePointer, create: (peer: KNativePointer) => AstNode): AstNode { if (cache.has(peer)) { return cache.get(peer)!; } diff --git a/koala-wrapper/src/arkts-api/index.ts b/koala-wrapper/src/arkts-api/index.ts index f0a9a91d1..eef8304a5 100644 --- a/koala-wrapper/src/arkts-api/index.ts +++ b/koala-wrapper/src/arkts-api/index.ts @@ -13,77 +13,78 @@ * limitations under the License. */ -export * from "../Es2pandaEnums" -export * from "../generated/Es2pandaEnums" -export * from "../generated/peers/AnnotationDeclaration" -export * from "../generated/peers/AnnotationUsage" -export * from "../generated/peers/BlockStatement" -export * from "../generated/peers/ETSPrimitiveType" -export * from "../generated/peers/TSInterfaceBody" -export * from "../generated/peers/TSInterfaceDeclaration" -export * from "../generated/peers/TSTypeParameterInstantiation" -export * from "../generated/peers/UndefinedLiteral" -export * from "../generated/peers/ETSUnionType" -export * from "../generated/peers/FunctionSignature" -export * from "../generated/peers/ETSFunctionType" -export * from "../generated/peers/StringLiteral" -export * from "../generated/peers/ThrowStatement" -export * from "../generated/peers/TypeNode" -export * from "../generated/peers/ClassDeclaration" -export * from "../generated/peers/ClassDefinition" -export * from "../generated/peers/Identifier" -export * from "../generated/peers/ETSTypeReference" -export * from "../generated/peers/ETSTypeReferencePart" -export * from "../generated/peers/ClassProperty" -export * from "../generated/peers/ReturnStatement" -export * from "../generated/peers/Expression" -export * from "../generated/peers/Statement" -export * from "../generated/peers/ImportSpecifier" -export * from "../generated/peers/TSAsExpression" -export * from "../generated/peers/ThisExpression" -export * from "../generated/peers/TSThisType" -export * from "../generated/peers/ETSImportDeclaration" -export * from "../generated/peers/ImportSource" -export * from "../generated/peers/ScriptFunction" -export * from "../generated/peers/TSTypeParameterDeclaration" -export * from "../generated/peers/TSTypeParameter" -export * from "../generated/peers/TSNonNullExpression" -export * from "../generated/peers/ChainExpression" -export * from "../generated/peers/ConditionalExpression" -export * from "../generated/peers/NullLiteral" -export * from "../generated/peers/TSTypeAliasDeclaration" -export * from "../generated/peers/ETSUndefinedType" -export * from "../generated/peers/ETSNewClassInstanceExpression" -export * from "../generated/peers/ObjectExpression" -export * from "../generated/peers/Property" -export * from "../generated/peers/BlockExpression" -export * from "../generated/peers/TSClassImplements" -export * from "../generated/peers/BooleanLiteral" -export * from "../generated/peers/TSArrayType" -export * from "../generated/peers/ArrayExpression" -export * from "../generated/peers/TryStatement" -export * from "../generated/peers/ETSNullType" +export * from '../Es2pandaEnums'; +export * from '../generated/Es2pandaEnums'; +export * from '../generated/peers/AnnotationDeclaration'; +export * from '../generated/peers/AnnotationUsage'; +export * from '../generated/peers/BlockStatement'; +export * from '../generated/peers/ETSPrimitiveType'; +export * from '../generated/peers/TSInterfaceBody'; +export * from '../generated/peers/TSInterfaceDeclaration'; +export * from '../generated/peers/TSTypeParameterInstantiation'; +export * from '../generated/peers/UndefinedLiteral'; +export * from '../generated/peers/ETSUnionType'; +export * from '../generated/peers/FunctionSignature'; +export * from '../generated/peers/ETSFunctionType'; +export * from '../generated/peers/StringLiteral'; +export * from '../generated/peers/ThrowStatement'; +export * from '../generated/peers/TypeNode'; +export * from '../generated/peers/ClassDeclaration'; +export * from '../generated/peers/ClassDefinition'; +export * from '../generated/peers/Identifier'; +export * from '../generated/peers/ETSTypeReference'; +export * from '../generated/peers/ETSTypeReferencePart'; +export * from '../generated/peers/ClassProperty'; +export * from '../generated/peers/ReturnStatement'; +export * from '../generated/peers/Expression'; +export * from '../generated/peers/Statement'; +export * from '../generated/peers/ImportSpecifier'; +export * from '../generated/peers/TSAsExpression'; +export * from '../generated/peers/ThisExpression'; +export * from '../generated/peers/TSThisType'; +export * from '../generated/peers/ETSImportDeclaration'; +export * from '../generated/peers/ImportSource'; +export * from '../generated/peers/ScriptFunction'; +export * from '../generated/peers/TSTypeParameterDeclaration'; +export * from '../generated/peers/TSTypeParameter'; +export * from '../generated/peers/TSNonNullExpression'; +export * from '../generated/peers/ChainExpression'; +export * from '../generated/peers/ConditionalExpression'; +export * from '../generated/peers/NullLiteral'; +export * from '../generated/peers/TSTypeAliasDeclaration'; +export * from '../generated/peers/ETSUndefinedType'; +export * from '../generated/peers/ETSNewClassInstanceExpression'; +export * from '../generated/peers/ObjectExpression'; +export * from '../generated/peers/Property'; +export * from '../generated/peers/BlockExpression'; +export * from '../generated/peers/TSClassImplements'; +export * from '../generated/peers/BooleanLiteral'; +export * from '../generated/peers/TSArrayType'; +export * from '../generated/peers/ArrayExpression'; +export * from '../generated/peers/TryStatement'; +export * from '../generated/peers/ETSNullType'; -export * from "./types" -export * from "./utilities/private" -export * from "./utilities/public" -export * from "./utilities/performance" -export * from "./factory/nodeFactory" -export * from "./factory/nodeTests" -export * from "./visitor" -export * from "./peers/AstNode" -import "../generated" +export * from './types'; +export * from './utilities/private'; +export * from './utilities/public'; +export * from './utilities/performance'; +export * from './utilities/nodeCache'; +export * from './factory/nodeFactory'; +export * from './factory/nodeTests'; +export * from './visitor'; +export * from './peers/AstNode'; +import '../generated'; -export * from "./peers/Config" -export * from "./peers/Context" -export * from "./peers/Program" -export * from "./peers/ImportPathManager" -export * from "./peers/SourcePosition" -export * from "./peers/SourceRange" -export * from "./peers/Diagnostic" -export * from "./peers/DiagnosticInfo" -export * from "./peers/DiagnosticKind" -export * from "./peers/SuggestionInfo" -export * from "./to-be-generated/MemberExpression" -export * from "./static/globalUtils" -export { global as arktsGlobal } from "./static/global" +export * from './peers/Config'; +export * from './peers/Context'; +export * from './peers/Program'; +export * from './peers/ImportPathManager'; +export * from './peers/SourcePosition'; +export * from './peers/SourceRange'; +export * from './peers/Diagnostic'; +export * from './peers/DiagnosticInfo'; +export * from './peers/DiagnosticKind'; +export * from './peers/SuggestionInfo'; +export * from './to-be-generated/MemberExpression'; +export * from './static/globalUtils'; +export { global as arktsGlobal } from './static/global'; diff --git a/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts index 9ad9e11af..a787698d7 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts @@ -16,6 +16,7 @@ import { ScriptFunction } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { ArrowFunctionExpression } from '../types'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateArrowFunctionExpression( @@ -31,5 +32,9 @@ export function updateArrowFunctionExpression( attachModifiers, (node: ArrowFunctionExpression, original: ArrowFunctionExpression) => node.setAnnotations(original.annotations) ); - return update(original, func); + const newNode = update(original, func); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/CallExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/CallExpression.ts index d1489441a..843798827 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/CallExpression.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/CallExpression.ts @@ -17,6 +17,7 @@ import { TypeNode } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { AstNode } from '../peers/AstNode'; import { CallExpression } from '../types'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateCallExpression( @@ -41,5 +42,9 @@ export function updateCallExpression( (node: CallExpression, original: CallExpression) => !!original.trailingBlock ? node.setTralingBlock(original.trailingBlock) : node ); - return update(original, expression, typeArguments, args, isOptional, trailingComma); + const newNode = update(original, expression, typeArguments, args, isOptional, trailingComma); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ClassProperty.ts b/koala-wrapper/src/arkts-api/node-utilities/ClassProperty.ts index 06450c0cb..1d31c2858 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ClassProperty.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ClassProperty.ts @@ -18,6 +18,7 @@ import { isSameNativeObject } from '../peers/ArktsObject'; import { updateThenAttach } from '../utilities/private'; import { classPropertySetOptional, hasModifierFlag } from '../utilities/public'; import { Es2pandaModifierFlags } from '../../generated/Es2pandaEnums'; +import { NodeCache } from '../utilities/nodeCache'; export function updateClassProperty( original: ClassProperty, @@ -47,5 +48,9 @@ export function updateClassProperty( return node; } ); - return update(original, key, value, typeAnnotation, modifiers, isComputed); + const newNode = update(original, key, value, typeAnnotation, modifiers, isComputed); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ETSFunctionType.ts b/koala-wrapper/src/arkts-api/node-utilities/ETSFunctionType.ts index a0e522040..c4e441974 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ETSFunctionType.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ETSFunctionType.ts @@ -17,6 +17,7 @@ import { ETSFunctionType, FunctionSignature } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { attachModifiers, updateThenAttach } from '../utilities/private'; import { Es2pandaScriptFunctionFlags } from '../../generated/Es2pandaEnums'; +import { NodeCache } from '../utilities/nodeCache'; export function updateETSFunctionType( original: ETSFunctionType, @@ -38,5 +39,9 @@ export function updateETSFunctionType( attachModifiers, (node: ETSFunctionType, original: ETSFunctionType) => node.setAnnotations(original.annotations) ); - return update(original, signature, funcFlags); + const newNode = update(original, signature, funcFlags); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ETSParameterExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/ETSParameterExpression.ts index b9e8f0514..23923cd75 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ETSParameterExpression.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ETSParameterExpression.ts @@ -17,6 +17,7 @@ import { Identifier } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { AstNode } from '../peers/AstNode'; import { ETSParameterExpression } from '../types'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateETSParameterExpression( @@ -37,7 +38,11 @@ export function updateETSParameterExpression( (node: ETSParameterExpression, original: ETSParameterExpression) => { node.annotations = original.annotations; return node; - } + }, ); - return update(original, identifier, initializer); + const newNode = update(original, identifier, initializer); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ETSUnionType.ts b/koala-wrapper/src/arkts-api/node-utilities/ETSUnionType.ts index c8dc20720..07ab40282 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ETSUnionType.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ETSUnionType.ts @@ -15,6 +15,7 @@ import { ETSUnionType, TypeNode } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateETSUnionType(original: ETSUnionType, types: readonly TypeNode[]): ETSUnionType { @@ -23,5 +24,9 @@ export function updateETSUnionType(original: ETSUnionType, types: readonly TypeN } const update = updateThenAttach(ETSUnionType.updateETSUnionType, attachModifiers); - return update(original, types); + const newNode = update(original, types); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/Identifier.ts b/koala-wrapper/src/arkts-api/node-utilities/Identifier.ts index f31d25fc6..3a1fff783 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/Identifier.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/Identifier.ts @@ -15,6 +15,7 @@ import { Identifier, TypeNode } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateIdentifier(original: Identifier, name: string, typeAnnotation?: TypeNode): Identifier { @@ -23,5 +24,9 @@ export function updateIdentifier(original: Identifier, name: string, typeAnnotat } const update = updateThenAttach(Identifier.update2Identifier, attachModifiers); - return update(original, name, typeAnnotation); + const newNode = update(original, name, typeAnnotation); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts b/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts index 42006a300..6318a0c84 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/MethodDefinition.ts @@ -20,6 +20,7 @@ import { MethodDefinition } from '../types'; import { updateThenAttach } from '../utilities/private'; import { Es2pandaMethodDefinitionKind } from '../../generated/Es2pandaEnums'; import { ScriptFunction } from '../../generated'; +import { NodeCache } from '../utilities/nodeCache'; export function updateMethodDefinition( original: MethodDefinition, @@ -42,5 +43,9 @@ export function updateMethodDefinition( const update = updateThenAttach(MethodDefinition.update, (node: MethodDefinition, original: MethodDefinition) => node.setOverloads(original.overloads) ); - return update(original, kind, key, value, modifiers, isComputed); + const newNode = update(original, kind, key, value, modifiers, isComputed); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/Property.ts b/koala-wrapper/src/arkts-api/node-utilities/Property.ts index 4b0a33c5b..fdb1443c5 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/Property.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/Property.ts @@ -15,6 +15,7 @@ import { Expression, Property } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateProperty(original: Property, key?: Expression, value?: Expression): Property { @@ -23,5 +24,10 @@ export function updateProperty(original: Property, key?: Expression, value?: Exp } const update = updateThenAttach(Property.updateProperty, attachModifiers); - return update(original, key, value); + const newNode = update(original, key, value); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; + } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ReturnStatement.ts b/koala-wrapper/src/arkts-api/node-utilities/ReturnStatement.ts index 9fc05a371..984e0aa5d 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ReturnStatement.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ReturnStatement.ts @@ -15,6 +15,7 @@ import { Expression, ReturnStatement } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateReturnStatement(original: ReturnStatement, argument?: Expression): ReturnStatement { @@ -23,5 +24,9 @@ export function updateReturnStatement(original: ReturnStatement, argument?: Expr } const update = updateThenAttach(ReturnStatement.update1ReturnStatement, attachModifiers); - return update(original, argument); + const newNode = update(original, argument); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/ScriptFunction.ts b/koala-wrapper/src/arkts-api/node-utilities/ScriptFunction.ts index 745a5398c..16e30342a 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/ScriptFunction.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/ScriptFunction.ts @@ -16,6 +16,7 @@ import { FunctionSignature, ScriptFunction } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; import { AstNode } from '../peers/AstNode'; +import { NodeCache } from '../utilities/nodeCache'; import { updateThenAttach } from '../utilities/private'; export function updateScriptFunction( @@ -42,5 +43,9 @@ export function updateScriptFunction( (node: ScriptFunction, original: ScriptFunction) => (!!original.id ? node.setIdent(original.id) : node), (node: ScriptFunction, original: ScriptFunction) => node.setAnnotations(original.annotations) ); - return update(original, databody, datasignature, datafuncFlags, dataflags); + const newNode = update(original, databody, datasignature, datafuncFlags, dataflags); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/TSTypeAliasDeclaration.ts b/koala-wrapper/src/arkts-api/node-utilities/TSTypeAliasDeclaration.ts index 3531ee3a9..a1d47e0e1 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/TSTypeAliasDeclaration.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/TSTypeAliasDeclaration.ts @@ -15,6 +15,7 @@ import { Identifier, TSTypeAliasDeclaration, TSTypeParameterDeclaration, TypeNode } from '../../generated'; import { isSameNativeObject } from '../peers/ArktsObject'; +import { NodeCache } from '../utilities/nodeCache'; import { attachModifiers, updateThenAttach } from '../utilities/private'; export function updateTSTypeAliasDeclaration( @@ -36,5 +37,9 @@ export function updateTSTypeAliasDeclaration( attachModifiers, (node: TSTypeAliasDeclaration, original: TSTypeAliasDeclaration) => node.setAnnotations(original.annotations) ); - return update(original, id, typeParams, typeAnnotation); + const newNode = update(original, id, typeParams, typeAnnotation); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/node-utilities/VariableDeclarator.ts b/koala-wrapper/src/arkts-api/node-utilities/VariableDeclarator.ts index 52c002fd3..1f1739da2 100644 --- a/koala-wrapper/src/arkts-api/node-utilities/VariableDeclarator.ts +++ b/koala-wrapper/src/arkts-api/node-utilities/VariableDeclarator.ts @@ -19,6 +19,7 @@ import { VariableDeclarator } from '../types'; import { attachModifiers, updateThenAttach } from '../utilities/private'; import { Es2pandaVariableDeclaratorFlag } from '../../generated/Es2pandaEnums'; import { AstNode } from '../peers/AstNode'; +import { NodeCache } from '../utilities/nodeCache'; export function updateVariableDeclarator( original: VariableDeclarator, @@ -35,5 +36,9 @@ export function updateVariableDeclarator( } const update = updateThenAttach(VariableDeclarator.update, attachModifiers); - return update(original, flag, name, initializer); + const newNode = update(original, flag, name, initializer); + if (NodeCache.getInstance().has(original)) { + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/peers/AstNode.ts b/koala-wrapper/src/arkts-api/peers/AstNode.ts index 54b699cd2..2a8b90fa6 100644 --- a/koala-wrapper/src/arkts-api/peers/AstNode.ts +++ b/koala-wrapper/src/arkts-api/peers/AstNode.ts @@ -15,7 +15,7 @@ import { isNullPtr, KInt, KNativePointer as KPtr, KNativePointer, nullptr } from '@koalaui/interop'; import { global } from '../static/global'; -import { allFlags, nodeType, unpackNode, unpackNodeArray, unpackNonNullableNode, unpackString } from '../utilities/private'; +import { allFlags, unpackNode, unpackNodeArray, unpackNonNullableNode, unpackString } from '../utilities/private'; import { throwError } from '../../utils'; import { Es2pandaModifierFlags } from '../../generated/Es2pandaEnums'; import { ArktsObject } from './ArktsObject'; diff --git a/koala-wrapper/src/arkts-api/types.ts b/koala-wrapper/src/arkts-api/types.ts index be1c31651..d73f539d0 100644 --- a/koala-wrapper/src/arkts-api/types.ts +++ b/koala-wrapper/src/arkts-api/types.ts @@ -27,7 +27,6 @@ import { allFlags, arrayOfNullptr, assertValidPeer, - nodeType, passNode, passNodeArray, passString, @@ -241,6 +240,14 @@ export class CallExpression extends Expression { return this; } + get hasTrailingComma(): boolean { + return global.generatedEs2panda._CallExpressionHasTrailingCommaConst(global.context, this.peer); + } + + get isTrailingCall(): boolean { + return global.es2panda._CallExpressionIsTrailingCallConst(global.context, this.peer); + } + readonly expression: AstNode; // Expression readonly typeArguments: readonly TypeNode[] | undefined; readonly arguments: readonly Expression[]; diff --git a/koala-wrapper/src/arkts-api/utilities/nodeCache.ts b/koala-wrapper/src/arkts-api/utilities/nodeCache.ts new file mode 100644 index 000000000..0843ee6b6 --- /dev/null +++ b/koala-wrapper/src/arkts-api/utilities/nodeCache.ts @@ -0,0 +1,103 @@ +/* + * 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. + */ + +import { KNativePointer } from '@koalaui/interop'; +import { Es2pandaAstNodeType } from '../../Es2pandaEnums'; +import { AstNode, UnsupportedNode } from '../peers/AstNode'; +import { global } from '../static/global'; +import { getOrPut, nodeByType } from '../class-by-peer'; + +export interface AstNodeCacheValue { + peer: KNativePointer; + type: Es2pandaAstNodeType; + metadata?: AstNodeCacheValueMetadata; +} + +export interface AstNodeCacheValueMetadata { + callName?: string; + hasReceiver?: boolean; + isSetter?: boolean; + isGetter?: boolean; + hasMemoSkip?: boolean; + hasMemoIntrinsic?: boolean; + hasMemoEntry?: boolean; +} + +export class NodeCache { + private _isCollected: boolean = false; + private cacheMap: Map; + private static instance: NodeCache; + + private constructor() { + this.cacheMap = new Map(); + } + + static getInstance(): NodeCache { + if (!this.instance) { + this.instance = new NodeCache(); + } + return this.instance; + } + + collect(node: AstNode, metadata?: AstNodeCacheValueMetadata): void { + const peer = node.peer; + const type = global.generatedEs2panda._AstNodeTypeConst(global.context, node.peer); + let currMetadata: AstNodeCacheValueMetadata | undefined = metadata ?? {}; + if (this.cacheMap.has(peer)) { + const oldMetadata = this.cacheMap.get(peer)!.metadata ?? {}; + currMetadata = { ...oldMetadata, ...currMetadata }; + } + currMetadata = Object.keys(currMetadata).length === 0 ? undefined : currMetadata; + this.cacheMap.set(peer, { peer, type, metadata: currMetadata }); + this._isCollected = true; + } + + refresh(original: AstNode, node: AstNode): void { + let metadata: AstNodeCacheValueMetadata | undefined; + if (this.has(original)) { + metadata = this.get(original)?.metadata; + this.cacheMap.delete(original.peer); + } + this.collect(node, metadata); + } + + isCollected(): boolean { + return this._isCollected; + } + + has(node: AstNode): boolean { + return this.cacheMap.has(node.peer); + } + + get(node: AstNode): AstNodeCacheValue | undefined { + return this.cacheMap.get(node.peer); + } + + clear(): void { + this.cacheMap.clear(); + this._isCollected = false; + } + + visualize(): void { + Array.from(this.cacheMap.values()).forEach(({ peer, type, metadata }) => { + const node = nodeByType.get(type) ?? UnsupportedNode; + const newNode = getOrPut(peer, (peer) => new node(peer)) as AstNode; + console.log( + `[NODE CACHE] ptr ${peer}, type: ${type}, metadata: ${JSON.stringify(metadata)}, node: `, + newNode.dumpSrc() + ); + }); + } +} diff --git a/koala-wrapper/src/arkts-api/utilities/private.ts b/koala-wrapper/src/arkts-api/utilities/private.ts index 30db3e472..c343fb8e2 100644 --- a/koala-wrapper/src/arkts-api/utilities/private.ts +++ b/koala-wrapper/src/arkts-api/utilities/private.ts @@ -168,10 +168,6 @@ export function updateNodeByNode(node: T, original: AstNode): return node; } -export function nodeType(node: AstNode): Es2pandaAstNodeType { - return global.generatedEs2panda._AstNodeTypeConst(global.context, passNode(node)); -} - /** * @deprecated */ diff --git a/koala-wrapper/src/arkts-api/utilities/public.ts b/koala-wrapper/src/arkts-api/utilities/public.ts index faa8e3624..083de3b2d 100644 --- a/koala-wrapper/src/arkts-api/utilities/public.ts +++ b/koala-wrapper/src/arkts-api/utilities/public.ts @@ -39,9 +39,10 @@ import { type AnnotationUsage, } from '../../generated'; import { Program } from '../peers/Program'; -import { clearNodeCache, nodeByType } from '../class-by-peer'; +import { clearNodeCache } from '../class-by-peer'; import { SourcePosition } from '../peers/SourcePosition'; import { MemberExpression } from '../to-be-generated/MemberExpression'; +import { Es2pandaAstNodeType } from '../../Es2pandaEnums'; export function proceedToState(state: Es2pandaContextState, context: KNativePointer, forceDtsEmit = false): void { console.log('[TS WRAPPER] PROCEED TO STATE: ', getEnumName(Es2pandaContextState, state)); @@ -73,6 +74,10 @@ function processErrorState(state: Es2pandaContextState, context: KNativePointer, } } +export function nodeType(node: AstNode): Es2pandaAstNodeType { + return global.generatedEs2panda._AstNodeTypeConst(global.context, passNode(node)); +} + export function startChecker(): boolean { return global.es2panda._CheckerStartChecker(global.context); } @@ -315,11 +320,3 @@ export function CreateCacheContextFromFile( ): KNativePointer { return global.es2panda._CreateCacheContextFromFile(configPtr, passString(filename), globalContext, isExternal); } - -export function insertGlobalStructInfo(structName: string): void { - global.es2panda._InsertGlobalStructInfo(global.context, passString(structName)); -} - -export function hasGlobalStructInfo(structName: string): boolean { - return global.es2panda._HasGlobalStructInfo(global.context, passString(structName)); -} diff --git a/koala-wrapper/src/arkts-api/visitor.ts b/koala-wrapper/src/arkts-api/visitor.ts index d5f2be7d1..3a76bf840 100644 --- a/koala-wrapper/src/arkts-api/visitor.ts +++ b/koala-wrapper/src/arkts-api/visitor.ts @@ -51,6 +51,9 @@ import { isForInStatement, isForUpdateStatement, isForOfStatement, + isTSTypeAliasDeclaration, + isETSParameterExpression, + isETSFunctionType, } from '../generated'; import { isEtsScript, @@ -66,6 +69,7 @@ import { isVariableDeclarator, isArrowFunctionExpression, isAssignmentExpression, + isEtsParameterExpression, } from './factory/nodeTests'; import { classDefinitionFlags } from './utilities/public'; import { Es2pandaAstNodeType } from '../Es2pandaEnums'; @@ -224,8 +228,7 @@ function visitTrivialExpression(node: AstNode, visitor: Visitor): AstNode { function visitDeclaration(node: AstNode, visitor: Visitor): AstNode { if (updated) { return node; - } - if (isFunctionDeclaration(node)) { + } else if (isFunctionDeclaration(node)) { updated = true; return factory.updateFunctionDeclaration( node, @@ -233,16 +236,13 @@ function visitDeclaration(node: AstNode, visitor: Visitor): AstNode { node.isAnon, node.annotations ); - } - if (isClassDeclaration(node)) { + } else if (isClassDeclaration(node)) { updated = true; return factory.updateClassDeclaration(node, nodeVisitor(node.definition, visitor)); - } - if (isStructDeclaration(node)) { + } else if (isStructDeclaration(node)) { updated = true; return factory.updateStructDeclaration(node, nodeVisitor(node.definition, visitor)); - } - if (isTSInterfaceDeclaration(node)) { + } else if (isTSInterfaceDeclaration(node)) { updated = true; return factory.updateInterfaceDeclaration( node, @@ -254,8 +254,7 @@ function visitDeclaration(node: AstNode, visitor: Visitor): AstNode { // TODO: how do I get it? true ); - } - if (isVariableDeclaration(node)) { + } else if (isVariableDeclaration(node)) { updated = true; return factory.updateVariableDeclaration( node, @@ -263,6 +262,14 @@ function visitDeclaration(node: AstNode, visitor: Visitor): AstNode { node.declarationKind, nodesVisitor(node.declarators, visitor) ); + } else if (isTSTypeAliasDeclaration(node)) { + updated = true; + return factory.updateTSTypeAliasDeclaration( + node, + node.id, + nodeVisitor(node.typeParams, visitor), + nodeVisitor(node.typeAnnotation, visitor) + ); } // TODO return node; @@ -459,5 +466,11 @@ function visitWithoutUpdate(node: T, visitor: Visitor): T { if (isImportDeclaration(node)) { nodesVisitor(node.specifiers, visitor); } + if (isETSFunctionType(node)) { + nodesVisitor(node.params, visitor); + } + if (isEtsParameterExpression(node)) { + nodeVisitor(node.type, visitor); + } return node; } -- Gitee