diff --git a/ui2abc/memo-plugin/src/AnalysisVisitor.ts b/ui2abc/memo-plugin/src/AnalysisVisitor.ts index afcd12e9d9a42980a244f25cb874f273a22229e8..b5d89d06f0f0363e546f56c3c1c52de8e7d74b5e 100644 --- a/ui2abc/memo-plugin/src/AnalysisVisitor.ts +++ b/ui2abc/memo-plugin/src/AnalysisVisitor.ts @@ -14,7 +14,7 @@ */ import * as arkts from "@koalaui/libarkts" -import { getDeclResolveGensym, getMemoFunctionKind, MemoFunctionKind } from "./utils" +import { getDeclResolveGensym, getMemoFunctionKind, getMemoParameterKind, MemoFunctionKind } from "./utils" import { getParams, isMemoAnnotatable, isMemoizable, MemoAnnotatable } from "./api-utils" class AnalysisVisitorOptions { @@ -77,7 +77,7 @@ export class AnalysisVisitor extends arkts.AbstractVisitor { return arkts.factory.updateCallExpression( node, this.visitor(node.callee!, options), - node.arguments.map((arg, index) => this.visitor(arg, { applyMemo: params[index] ? getMemoFunctionKind(params[index]) : undefined })), + node.arguments.map((arg, index) => this.visitor(arg, { applyMemo: params[index] ? getMemoParameterKind(params[index]) : undefined })), node.typeParams ? this.visitor(node.typeParams, options) as arkts.TSTypeParameterInstantiation : undefined, node.isOptional, node.hasTrailingComma, diff --git a/ui2abc/memo-plugin/src/api-utils.ts b/ui2abc/memo-plugin/src/api-utils.ts index 199250aada430e7c507e73dcbf8da36f46867cfd..1b4cbd23aa0338ede3250142b885820c77fd23f8 100644 --- a/ui2abc/memo-plugin/src/api-utils.ts +++ b/ui2abc/memo-plugin/src/api-utils.ts @@ -14,7 +14,7 @@ */ import * as arkts from "@koalaui/libarkts" -import { getDeclResolveGensym, getMemoFunctionKind, MemoFunctionKind } from "./utils" +import { getMemoFunctionKind, getMemoParameterKind, MemoFunctionKind } from "./utils" /** * Type of node which can be returned by getDecl and correspond to node which can have memo annotation @@ -47,6 +47,13 @@ export type MemoAnnotatableType = arkts.ETSFunctionType | arkts.ETSUnionType +/** + * Type of type node which can correspond to functional memo type + */ +export type MemoAllowedType = + MemoAnnotatableType + | arkts.ETSTypeReference + /** * Type of node which can have functional memo annotation */ @@ -86,6 +93,10 @@ export function isMemoAnnotatable(node: arkts.AstNode | undefined): node is Memo return isMemoAnnotatableExpression(node) || isMemoAnnotatableType(node) } +export function isMemoAllowedType(node: arkts.AstNode | undefined): node is MemoAllowedType { + return isMemoAnnotatableType(node) || arkts.isETSTypeReference(node) +} + export function getName(node: Memoizable): string { if (arkts.isMethodDefinition(node)) { return node.id!.name @@ -115,32 +126,10 @@ export function isMemo(node: Memoizable) { } } if (arkts.isETSParameterExpression(node)) { - const kind = getMemoFunctionKind(node) + const kind = getMemoParameterKind(node) if (kind == MemoFunctionKind.MEMO || kind == MemoFunctionKind.INTRINSIC) { return true } - if (isMemoAnnotatableType(node.typeAnnotation)) { - const kind = getMemoFunctionKind(node.typeAnnotation) - if (kind == MemoFunctionKind.MEMO || kind == MemoFunctionKind.INTRINSIC) { - return true - } - } - if (arkts.isETSTypeReference(node.typeAnnotation)) { - const name = node.typeAnnotation.part?.name - if (name) { - const decl = getDeclResolveGensym(name) - if (arkts.isTSTypeAliasDeclaration(decl)) { - const kind = isMemoAnnotatableType(decl.typeAnnotation) && getMemoFunctionKind(decl.typeAnnotation) - if (kind == MemoFunctionKind.MEMO || kind == MemoFunctionKind.INTRINSIC) { - return true - } - const kind2 = getMemoFunctionKind(decl) - if (kind2 == MemoFunctionKind.MEMO || kind2 == MemoFunctionKind.INTRINSIC) { - return true - } - } - } - } } if (arkts.isIdentifier(node)) { if (arkts.isVariableDeclarator(node.parent) && arkts.isArrowFunctionExpression(node.parent.init)) { diff --git a/ui2abc/memo-plugin/src/utils.ts b/ui2abc/memo-plugin/src/utils.ts index 43ba7052f6252a146ef5c84222ec7eedbe53f869..1a1828cace55c53856e6216c34ed07a619273fc3 100644 --- a/ui2abc/memo-plugin/src/utils.ts +++ b/ui2abc/memo-plugin/src/utils.ts @@ -21,6 +21,9 @@ import { correctFunctionParamType, correctObjectExpression, isMemo, + isMemoAllowedType, + isMemoAnnotatableType, + MemoAllowedType, MemoAnnotatable, MemoSkipAnnotatable, MemoStableAnnotatable @@ -153,8 +156,7 @@ export function hasWrapAnnotation(node: arkts.ETSParameterExpression) { ) } -export function getMemoFunctionKind(node: MemoAnnotatable): MemoFunctionKind { - const mask = (+hasMemoEntryAnnotation(node) << 2) + (+hasMemoIntrinsicAnnotation(node) << 1) + (+hasMemoAnnotation(node)) +export function maskToKind(mask: number): MemoFunctionKind { switch (mask) { case 0: return MemoFunctionKind.NONE @@ -170,6 +172,46 @@ export function getMemoFunctionKind(node: MemoAnnotatable): MemoFunctionKind { } } +export function getMemoFunctionKind(node: MemoAnnotatable): MemoFunctionKind { + const mask = (+hasMemoEntryAnnotation(node) << 2) + (+hasMemoIntrinsicAnnotation(node) << 1) + (+hasMemoAnnotation(node)) + return maskToKind(mask) +} + +export function getMemoTypeKind(node: MemoAllowedType): MemoFunctionKind { + if (arkts.isETSFunctionType(node)) { + return getMemoFunctionKind(node) + } + if (arkts.isETSUnionType(node)) { + const directKind = getMemoFunctionKind(node) + const childKinds = node.types.map(it => isMemoAllowedType(it) ? getMemoTypeKind(it) : MemoFunctionKind.NONE) + const mask = childKinds.reduce((prev: number, cur: number) => prev | cur, directKind) + return maskToKind(mask) + } + if (arkts.isETSTypeReference(node)) { + const name = node.part?.name + if (!name) { + return MemoFunctionKind.NONE + } + const decl = getDeclResolveGensym(name) + if (!arkts.isTSTypeAliasDeclaration(decl)) { + return MemoFunctionKind.NONE + } + const directKind = getMemoFunctionKind(decl) + const typeKind = isMemoAllowedType(decl.typeAnnotation) ? getMemoTypeKind(decl.typeAnnotation) : MemoFunctionKind.NONE + return maskToKind(directKind | typeKind) + } + return MemoFunctionKind.NONE +} + +export function getMemoParameterKind(node: arkts.ETSParameterExpression): MemoFunctionKind { + const directKind = getMemoFunctionKind(node) + const typeAnnotation = node.ident?.typeAnnotation + const typeKind = isMemoAllowedType(typeAnnotation) + ? getMemoTypeKind(typeAnnotation) + : MemoFunctionKind.NONE + return maskToKind(directKind | typeKind) +} + export function isWrappable(type: arkts.TypeNode | undefined, arg: arkts.Expression) { return (type && correctFunctionParamType(arg)) || correctObjectExpression(arg) } diff --git a/ui2abc/memo-plugin/test/golden/param-usage/union-with-non-memo/primitive.ets b/ui2abc/memo-plugin/test/golden/param-usage/union-with-non-memo/primitive.ets new file mode 100644 index 0000000000000000000000000000000000000000..5f9ab43af3e0f85ee739fd8ad00bb9d5d8153c99 --- /dev/null +++ b/ui2abc/memo-plugin/test/golden/param-usage/union-with-non-memo/primitive.ets @@ -0,0 +1,52 @@ + +import { __memo_context_type as __memo_context_type, __memo_id_type as __memo_id_type, __hash as __hash } from "@koalaui/runtime"; + +import { memo as memo, memo_skip as memo_skip } from "@koalaui/runtime/annotations"; + +function main() {} + + +@memo() function memo_function(__memo_context: __memo_context_type, __memo_id: __memo_id_type, @memo_skip() param: (Builder | string)): void { + const __memo_scope = __memo_context.scope(((__memo_id) + (__hash("id_memo_function_primitive.ets"))), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + if (((param) instanceof (Builder))) { + param(__memo_context, ((__memo_id) + (__hash("id_param_primitive.ets")))); + } else { + param; + } + { + __memo_scope.recache(); + return; + } +} + +@memo() function callsite(__memo_context: __memo_context_type, __memo_id: __memo_id_type): void { + const __memo_scope = __memo_context.scope(((__memo_id) + (__hash("id_callsite_primitive.ets"))), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + memo_function(__memo_context, ((__memo_id) + (__hash("id_memo_function_primitive.ets"))), ((__memo_context: __memo_context_type, __memo_id: __memo_id_type) => { + const __memo_scope = __memo_context.scope(((__memo_id) + (__hash("id__primitive.ets"))), 0); + if (__memo_scope.unchanged) { + __memo_scope.cached; + return; + } + { + __memo_scope.recache(); + return; + } + })); + memo_function(__memo_context, ((__memo_id) + (__hash("id_memo_function_primitive.ets"))), "hello"); + { + __memo_scope.recache(); + return; + } +} + + +type Builder = @memo() ((__memo_context: __memo_context_type, __memo_id: __memo_id_type)=> void); + diff --git a/ui2abc/memo-plugin/test/input/param-usage/union-with-non-memo/primitive.ets b/ui2abc/memo-plugin/test/input/param-usage/union-with-non-memo/primitive.ets new file mode 100644 index 0000000000000000000000000000000000000000..9de85bc28d58b5b1916f3575b7325614bb921309 --- /dev/null +++ b/ui2abc/memo-plugin/test/input/param-usage/union-with-non-memo/primitive.ets @@ -0,0 +1,16 @@ +import { memo, memo_skip } from "@koalaui/runtime/annotations" + +type Builder = @memo () => void + +@memo function memo_function(@memo_skip param: Builder | string): void { + if (param instanceof Builder) { + param() + } else { + param + } +} + +@memo function callsite(): void { + memo_function(() => {}) + memo_function("hello") +} diff --git a/ui2abc/memo-plugin/test/rewrite.test.ts b/ui2abc/memo-plugin/test/rewrite.test.ts index 964d7a61e4969c51b1404b50fa434266f60fdadd..097a5d00b050d9304935b3486d077b24b2a5eb1c 100644 --- a/ui2abc/memo-plugin/test/rewrite.test.ts +++ b/ui2abc/memo-plugin/test/rewrite.test.ts @@ -130,6 +130,12 @@ suite("golden tests", () => { testBody("param-usage/on-param-and-type/memo-on-optional-possibly-undefined-param") }) }) + + suite("union-with-non-memo", () => { + test("memo on (Builder | string)", () => { + testBody("param-usage/union-with-non-memo/primitive") + }) + }) }) test("implicit return type", () => {