diff --git a/arkoala-arkts/libarkts/src/arkts-api/factory/nodeFactory.ts b/arkoala-arkts/libarkts/src/arkts-api/factory/nodeFactory.ts index eb99b8552f94b38f3023d8e0a8ad20f103678ec6..da8b4a386e6e1bb1b83b55381d613ee7a0a6007a 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/factory/nodeFactory.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/factory/nodeFactory.ts @@ -33,6 +33,7 @@ import { ExpressionStatement, FunctionDeclaration, FunctionExpression, + FunctionSignature, Identifier, IfStatement, ImportSpecifier, @@ -52,7 +53,7 @@ import { } from "../types" import { MemberExpression } from "../to-be-generated/MemberExpression" import { AstNode } from "../peers/AstNode" -import { AnnotationUsage, TSInterfaceBody, TSInterfaceDeclaration } from "../../generated" +import { AnnotationUsage, ConditionalExpression, TSInterfaceBody, TSInterfaceDeclaration } from "../../generated" function compose( create: (...args: ARGS) => T, @@ -300,4 +301,13 @@ export const factory = { get updateAnnotationUsageIr() { return compose(UndefinedLiteral.create) }, + get createFunctionSignature() { + return FunctionSignature.create + }, + get createConditionalExpression() { + return ConditionalExpression.createConditionalExpression + }, + get updateConditionalExpression() { + return compose(ConditionalExpression.createConditionalExpression) + }, } diff --git a/arkoala-arkts/libarkts/src/arkts-api/types.ts b/arkoala-arkts/libarkts/src/arkts-api/types.ts index b04cc52b5562182b01ffa4c8b6dfa6ac8ccae7b4..6342c515119707b2361dbeec7b6063c1bec1b54c 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/types.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/types.ts @@ -364,6 +364,14 @@ export class ETSFunctionType extends AstNode { ) ) } + + get params() { + return unpackNodeArray(global.generatedEs2panda._ETSFunctionTypeIrParamsConst(global.context, this.peer)) + } + + get returnType() { + return unpackNode(global.generatedEs2panda._ETSFunctionTypeIrReturnType(global.context, this.peer)) + } } export class Identifier extends Expression { @@ -757,6 +765,16 @@ export class ETSParameterExpression extends AstNode { ); } + get type(): AstNode | undefined { + return unpackNode(global.generatedEs2panda._ETSParameterExpressionTypeAnnotation(global.context, this.peer)) + } + + set type(t: AstNode | undefined) { + if (t === undefined) + return + global.generatedEs2panda._ETSParameterExpressionSetTypeAnnotation(global.context, this.peer, t.peer) + } + identifier: Identifier } @@ -1135,6 +1153,10 @@ export class VariableDeclarator extends AstNode { return new VariableDeclarator(peer) } + get initializer(): AstNode | undefined { + return unpackNode(global.generatedEs2panda._VariableDeclaratorInit(global.context, this.peer)) + } + readonly name: Identifier } @@ -1149,7 +1171,7 @@ export class SuperExpression extends AstNode { constructor(peer: KPtr) { assertValidPeer(peer, Es2pandaAstNodeType.AST_NODE_TYPE_SUPER_EXPRESSION) super(peer) - this.id = unpackNonNullableNode(global.generatedEs2panda._TSInterfaceDeclarationId(global.context, this.peer)); + this.id = unpackNode(global.generatedEs2panda._TSInterfaceDeclarationId(global.context, this.peer)); } static create( diff --git a/arkoala-arkts/libarkts/src/arkts-api/visitor.ts b/arkoala-arkts/libarkts/src/arkts-api/visitor.ts index 6d518991d66a3edd957ab64f158200859ff030ea..976fc53af90c97f99960309647562606077e7b73 100644 --- a/arkoala-arkts/libarkts/src/arkts-api/visitor.ts +++ b/arkoala-arkts/libarkts/src/arkts-api/visitor.ts @@ -15,6 +15,7 @@ import { factory } from "./factory/nodeFactory" import { + ArrowFunctionExpression, BlockStatement, CallExpression, ClassDeclaration, @@ -29,12 +30,14 @@ import { ScriptFunction, StructDeclaration, TSTypeParameterInstantiation, + VariableDeclaration, + VariableDeclarator, } from "./types" -import { Es2pandaClassDefinitionModifiers, Es2pandaModifierFlags } from "../generated/Es2pandaEnums" +import { Es2pandaClassDefinitionModifiers, Es2pandaModifierFlags, Es2pandaVariableDeclarationKind, Es2pandaVariableDeclaratorFlag } from "../generated/Es2pandaEnums" import { nullptr } from "@koalaui/interop" import { AstNode } from "./peers/AstNode" import { MemberExpression } from "./to-be-generated/MemberExpression" -import { TSInterfaceBody, TSInterfaceDeclaration } from "../generated" +import { ConditionalExpression, TSInterfaceBody, TSInterfaceDeclaration } from "../generated" type Visitor = (node: AstNode) => AstNode @@ -250,6 +253,36 @@ export function visitEachChild( nodeVisitor(node.alternate, visitor), ) } + if (node instanceof ConditionalExpression) { + return factory.updateConditionalExpression( + node, + nodeVisitor(node.test, visitor), + nodeVisitor(node.consequent, visitor), + nodeVisitor(node.alternate, visitor), + ) + } + if (node instanceof VariableDeclaration) { + return factory.updateVariableDeclaration( + node, + 0, + node.declarationKind, + nodesVisitor(node.declarators, visitor), + ) + } + if (node instanceof VariableDeclarator) { + return factory.updateVariableDeclarator( + node, + Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_UNKNOWN, + nodeVisitor(node.name, visitor), + nodeVisitor(node.initializer, visitor), + ) + } + if (node instanceof ArrowFunctionExpression) { + return factory.updateArrowFunction( + node, + nodeVisitor(node.scriptFunction, visitor), + ) + } // TODO return node } diff --git a/arkoala-arkts/memo-plugin/demo/demo.sts b/arkoala-arkts/memo-plugin/demo/demo.sts index 8e49383b95c5b31af7ff9109c978ce932711b6bc..74cc5fd7dffc87b96f901fc074b235840ffd1a60 100644 --- a/arkoala-arkts/memo-plugin/demo/demo.sts +++ b/arkoala-arkts/memo-plugin/demo/demo.sts @@ -1,4 +1,7 @@ -import { GlobalStateManager, memoEntry, StateContext, memo } from "@koalaui/runtime" +import { GlobalStateManager, memoEntry, StateContext, memo, IncrementalNode, MutableState, + NodeAttach, mutableState, contextLocalValue, ReadonlyTreeNode, CONTEXT_ROOT_SCOPE, + memoRoot, updateStateManager, +} from "@koalaui/runtime" import { memo_foo } from "./stub" @memo @@ -13,17 +16,89 @@ function f(s: string) { y("she") } +@memo +function bar( + arg1: number, + @memo arg2: (x: number) => number, +) { + console.log(arg1, arg2(arg1)) +} + @memo function foo_wrapper() { ETSGLOBAL.f("he") memo_foo("hello") + ETSGLOBAL.bar(1, (x: number): number => { return 3 + x; }) +} + +class StringerNode extends IncrementalNode { + constructor(kind: int = 1) { + super(kind) + } + data: string | undefined = undefined +} + +@memo +function Stringer( + arg: string, + @memo + content?: () => void +): void { + NodeAttach((): StringerNode /* TODO: es2panda writes type Node here, which is template argument of NodeAttach */ => + new StringerNode(1) /* TODO: default constructor argument is lost */, (node: StringerNode): void => { + node.data = arg + console.log("I am recomputing with arg: ", arg) + content ? content() : undefined /* TODO: gensym error */ + }) +} + +/* TODO: runtime error */ +// const state = mutableState(17) +class StateWrapper { + static state: MutableState = mutableState(17) +} + +@memo +function demo(node: StringerNode): void { + ETSGLOBAL.Stringer("First", () => { + console.log("Content of the first") + ETSGLOBAL.Stringer(`Second ${StateWrapper.state.value}`, () => { + console.log("Content of the second") + ETSGLOBAL.Stringer("Third") + }) + ETSGLOBAL.Stringer("Fourth", () => { + console.log("Content of the 4th") + ETSGLOBAL.Stringer("5th") + }) + }) + + // This is to dump the complete managed incremental scope tree + const scope = contextLocalValue(CONTEXT_ROOT_SCOPE) /* TODO: TypeArguments are lost for some reason */ + console.log(scope ? scope.toHierarchy() : undefined) /* TODO: gensym error */ } function main() { const manager = GlobalStateManager.instance - const state = manager.computableState((context: StateContext): Int => { + const state0 = manager.computableState((context: StateContext): Int => { memoEntry(context, 0, ETSGLOBAL.foo_wrapper) return 20 }) - console.log(state.value) + console.log(state0.value) + + console.log(StateWrapper.state.value) + + // memoRoot is the entry point here. + // It initializes the incremental runtime and computes the first frame. + // Have a look at its implementation. + const root = memoRoot(new StringerNode(0), ETSGLOBAL.demo) + console.log(root.value.toHierarchy()) // dump ui subtree + + updateStateManager() // Compute next frame. + console.log(root.value.toHierarchy()) + + StateWrapper.state.value = 19 + + updateStateManager() // Compute the next frame. + console.log(root.value.toHierarchy()) + console.log("-----END-----") } diff --git a/arkoala-arkts/memo-plugin/demo/demo.ts b/arkoala-arkts/memo-plugin/demo/demo.ts index 1d5af07338529b78ce9c12918343892e955781c4..6b4ed9e41702d70312dc2fce8bda81b0cd108407 100644 --- a/arkoala-arkts/memo-plugin/demo/demo.ts +++ b/arkoala-arkts/memo-plugin/demo/demo.ts @@ -1,7 +1,10 @@ // This file is needed to check how unmemoized (by ts plugin) should look like // It is not actually executed -import { GlobalStateManager, memoEntry, StateContext } from "@koalaui/runtime" +import { GlobalStateManager, memoEntry, StateContext, IncrementalNode, + NodeAttach, mutableState, contextLocalValue, ReadonlyTreeNode, CONTEXT_ROOT_SCOPE, + memoRoot, updateStateManager, +} from "@koalaui/runtime" import { memo_foo } from "./stub" /** @memo */ @@ -16,17 +19,82 @@ function f(s: string) { y("she") } +/** @memo */ +function bar( + arg1: number, + /** @memo */ arg2: (x: number) => number, +) { + console.log(arg1, arg2(arg1)) +} + /** @memo */ function foo_wrapper() { f("he") memo_foo("hello") + bar(1, (x: number): number => { return 3 + x; }) +} + +class StringerNode extends IncrementalNode { + constructor(kind: number = 1) { + super(kind) + } + data: string | undefined = undefined +} + +/** @memo */ +function Stringer( + arg: string, + /** @memo */ + content?: () => void +): void { + NodeAttach(() => new StringerNode(), (node: StringerNode): void => { + node.data = arg + console.log("I am recomputing with arg: ", arg) + content?.() + }) +} + +const state = mutableState(17) + +/** @memo */ +function demo(node: StringerNode): void { + Stringer("First", () => { + console.log("Content of the first") + Stringer(`Second ${state.value}`, () => { + console.log("Content of the second") + Stringer("Third") + }) + Stringer("Fourth", () => { + console.log("Content of the 4th") + Stringer("5th") + }) + }) + + // This is to dump the complete managed incremental scope tree + const scope = contextLocalValue(CONTEXT_ROOT_SCOPE) + console.log(scope?.toHierarchy()) } function main() { const manager = GlobalStateManager.instance - const state = manager.computableState((context: StateContext) => { + const state0 = manager.computableState((context: StateContext) => { memoEntry(context, 0, foo_wrapper) return 20 }) - console.log(state.value) + console.log(state0.value) + + // memoRoot is the entry point here. + // It initializes the incremental runtime and computes the first frame. + // Have a look at its implementation. + const root = memoRoot(new StringerNode(0), demo) + console.log(root.value.toHierarchy()) // dump ui subtree + + updateStateManager() // Compute next frame. + console.log(root.value.toHierarchy()) + + state.value = 19 + + updateStateManager() // Compute the next frame. + console.log(root.value.toHierarchy()) + console.log("-----END-----") } diff --git a/arkoala-arkts/memo-plugin/runtime-api/memo/contextLocal.sts b/arkoala-arkts/memo-plugin/runtime-api/memo/contextLocal.sts index 5cfb10ee7d897ea385e7948b24889d8a6482d536..21353cafd65dedbedcc0c49ee6834e16a2366981 100644 --- a/arkoala-arkts/memo-plugin/runtime-api/memo/contextLocal.sts +++ b/arkoala-arkts/memo-plugin/runtime-api/memo/contextLocal.sts @@ -14,7 +14,10 @@ */ import { memo, memo_intrinsic } from "../annotations" -import { State } from "../states/State" +import { State, StateContext } from "../states/State" +import { KoalaCallsiteKey } from "../common" +import { ReadonlyTreeNode } from "../tree/ReadonlyTreeNode" @memo_intrinsic export declare function contextLocal(name: string): State | undefined -@memo_intrinsic export declare function contextLocalValue(name: string): Value +@memo_intrinsic export declare function contextLocalValue(name: string): Value // TODO +export declare function contextLocalValue(stateContext: StateContext, id: KoalaCallsiteKey, name: string): Value // TODO @memo_intrinsic export declare function contextLocalScope(name: string, value: Value, @memo content: () => void): void diff --git a/arkoala-arkts/memo-plugin/src/FunctionTransformer.ts b/arkoala-arkts/memo-plugin/src/FunctionTransformer.ts index 93217012915ae13f6616ee418d07f42186c422c3..6be53d126de6d6148cd4c0d22a0d2208925d1026 100644 --- a/arkoala-arkts/memo-plugin/src/FunctionTransformer.ts +++ b/arkoala-arkts/memo-plugin/src/FunctionTransformer.ts @@ -18,17 +18,12 @@ import { factory } from "./MemoFactory" import { AbstractVisitor } from "./AbstractVisitor" import { PositionalIdTracker, - RuntimeNames + hasMemoAnnotation, + hasMemoIntrinsicAnnotation, } from "./utils" import { ParameterTransformer } from "./ParameterTransformer" import { ReturnTransformer } from "./ReturnTranformer" -function hasMemoAnnotation(node: arkts.ScriptFunction | arkts.ETSParameterExpression) { - return node.annotations.some((it) => - it.expr !== undefined && arkts.isIdentifier(it.expr) && it.expr.name === RuntimeNames.ANNOTATION - ) -} - function updateFunctionBody( node: arkts.BlockStatement, parameters: arkts.ETSParameterExpression[], @@ -93,43 +88,71 @@ export class FunctionTransformer extends AbstractVisitor { super() } + updateScriptFunction( + scriptFunction: arkts.ScriptFunction, + name: string = "", + ): arkts.ScriptFunction { + if (!scriptFunction.body) { + return scriptFunction + } + const [body, memoParametersDeclaration, syntheticReturnStatement] = updateFunctionBody( + scriptFunction.body, + scriptFunction.parameters, + scriptFunction.returnTypeAnnotation, + this.positionalIdTracker.id(name), + ) + const afterParameterTransformer = this.parameterTransformer + .withParameters(scriptFunction.parameters) + .skip(memoParametersDeclaration) + .visitor(body) + const afterReturnTransformer = this.returnTransformer + .skip(syntheticReturnStatement) + .visitor(afterParameterTransformer) + const updatedParameters = scriptFunction.parameters.map((param) => { + if (hasMemoAnnotation(param)) { + if (!(param.type instanceof arkts.ETSFunctionType)) { + throw "ArrowFunctionExpression expected for @memo parameter of @memo function" + } + param.type = arkts.factory.createFunctionType( + arkts.factory.createFunctionSignature( + undefined, + [...factory.createHiddenParameters(), ...param.type.params], + param.type.returnType, + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + ) + } + return param + }) + return arkts.factory.updateScriptFunction( + scriptFunction, + afterReturnTransformer, + scriptFunction.scriptFunctionFlags, + scriptFunction.modifiers, + false, + scriptFunction.ident, + [...factory.createHiddenParameters(), ...updatedParameters], + scriptFunction.typeParamsDecl, + scriptFunction.returnTypeAnnotation + ) + } + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { // TODO: Remove (currently annotations are lost on visitor) const methodDefinitionHasMemoAnnotation = beforeChildren instanceof arkts.MethodDefinition && hasMemoAnnotation(beforeChildren.scriptFunction) + const methodDefinitionHasMemoIntrinsicAnnotation = + beforeChildren instanceof arkts.MethodDefinition && hasMemoIntrinsicAnnotation(beforeChildren.scriptFunction) const node = this.visitEachChild(beforeChildren) if (node instanceof arkts.MethodDefinition && node.scriptFunction.body) { - if (methodDefinitionHasMemoAnnotation) { - const [body, memoParametersDeclaration, syntheticReturnStatement] = updateFunctionBody( - node.scriptFunction.body, - node.scriptFunction.parameters, - node.scriptFunction.returnTypeAnnotation, - this.positionalIdTracker.id(node.name.name), - ) - const afterParameterTransformer = this.parameterTransformer - .withParameters(node.scriptFunction.parameters) - .skip(memoParametersDeclaration) - .visitor(body) - const afterReturnTransformer = this.returnTransformer - .skip(syntheticReturnStatement) - .visitor(afterParameterTransformer) + if (methodDefinitionHasMemoAnnotation || methodDefinitionHasMemoIntrinsicAnnotation) { return arkts.factory.updateMethodDefinition( node, arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, node.name, arkts.factory.createFunctionExpression( - arkts.factory.updateScriptFunction( - node.scriptFunction, - afterReturnTransformer, - node.scriptFunction.scriptFunctionFlags, - node.scriptFunction.modifiers, - false, - node.scriptFunction.ident, - [...factory.createHiddenParameters(), ...node.scriptFunction.parameters], - node.scriptFunction.typeParamsDecl, - node.scriptFunction.returnTypeAnnotation - ) + this.updateScriptFunction(node.scriptFunction, node.name.name), ), node.modifiers, false @@ -139,10 +162,16 @@ export class FunctionTransformer extends AbstractVisitor { if (node instanceof arkts.CallExpression) { const expr = node.expression const decl = arkts.getDecl(expr) - if (decl instanceof arkts.MethodDefinition && hasMemoAnnotation(decl.scriptFunction)) { + if (decl instanceof arkts.MethodDefinition && (hasMemoAnnotation(decl.scriptFunction) || hasMemoIntrinsicAnnotation(decl.scriptFunction))) { const updatedArguments = node.arguments.map((it, index) => { - if (hasMemoAnnotation(decl.scriptFunction.parameters[index])) { - return factory.createComputeExpression(this.positionalIdTracker.id(decl.name.name), it) + if (decl.scriptFunction.parameters[index].type instanceof arkts.ETSFunctionType) { + if (!hasMemoAnnotation(decl.scriptFunction.parameters[index]) && !hasMemoIntrinsicAnnotation(decl.scriptFunction.parameters[index])) { + return factory.createComputeExpression(this.positionalIdTracker.id(decl.name.name), it) + } + if (!(it instanceof arkts.ArrowFunctionExpression)) { + throw "ArrowFunctionExpression expected for @memo argument of @memo function" + } + return this.updateScriptFunction(it.scriptFunction) } return it }) diff --git a/arkoala-arkts/memo-plugin/src/MemoFactory.ts b/arkoala-arkts/memo-plugin/src/MemoFactory.ts index 5c9246d91f3f493cf0a58997d1ec827b81324c0a..356491f5d5a358fce6563d8f84d00e4c7f44079d 100644 --- a/arkoala-arkts/memo-plugin/src/MemoFactory.ts +++ b/arkoala-arkts/memo-plugin/src/MemoFactory.ts @@ -108,6 +108,28 @@ export class factory { false, ) } + static createMemoParameterAccessMemo(name: string, hash: arkts.NumberLiteral | arkts.StringLiteral, passArgs?: arkts.AstNode[]): arkts.CallExpression { + const updatedArgs = passArgs ? passArgs : [] + return arkts.factory.createCallExpression( + arkts.factory.createIdentifier(name), + undefined, + [...factory.createHiddenArguments(hash), ...updatedArgs], + ) + } + static createMemoParameterAccessCall(name: string, hash: arkts.NumberLiteral | arkts.StringLiteral, passArgs?: arkts.AstNode[]): arkts.CallExpression { + const updatedArgs = passArgs ? passArgs : [] + return arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + factory.createMemoParameterIdentifier(name), + arkts.factory.createIdentifier(RuntimeNames.VALUE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_GETTER, + false, + false, + ), + undefined, + [...factory.createHiddenArguments(hash), ...updatedArgs], + ) + } // Recache static createScopeDeclaration(returnTypeAnnotation: arkts.AstNode | undefined, hash: arkts.NumberLiteral | arkts.StringLiteral, cnt: number): arkts.VariableDeclaration { diff --git a/arkoala-arkts/memo-plugin/src/MemoTransformer.ts b/arkoala-arkts/memo-plugin/src/MemoTransformer.ts index 8ac6ee906c6ef7537c4d5e4568d653459f05e2d6..876720259b87175c3d30d249142e55f1ac6e9bb6 100644 --- a/arkoala-arkts/memo-plugin/src/MemoTransformer.ts +++ b/arkoala-arkts/memo-plugin/src/MemoTransformer.ts @@ -29,7 +29,7 @@ export default function memoTransformer( ) { return (node: arkts.EtsScript) => { const positionalIdTracker = new PositionalIdTracker(arkts.getFileName(), false) - const parameterTransformer = new ParameterTransformer() + const parameterTransformer = new ParameterTransformer(positionalIdTracker) const returnTransformer = new ReturnTransformer() const functionTransformer = new FunctionTransformer(positionalIdTracker, parameterTransformer, returnTransformer) return functionTransformer.visitor( diff --git a/arkoala-arkts/memo-plugin/src/ParameterTransformer.ts b/arkoala-arkts/memo-plugin/src/ParameterTransformer.ts index cf4d874e3b21c517cfaed2db31e40a06cb7853ee..0e93b86be8c2d91ca677b1952ad50568a62cb058 100644 --- a/arkoala-arkts/memo-plugin/src/ParameterTransformer.ts +++ b/arkoala-arkts/memo-plugin/src/ParameterTransformer.ts @@ -17,15 +17,27 @@ import * as arkts from "@koalaui/libarkts" import { factory } from "./MemoFactory" import { AbstractVisitor } from "./AbstractVisitor" import { KPointer } from "@koalaui/interop" -import { isMemoParametersDeclaration } from "./utils" +import { hasMemoAnnotation, hasMemoIntrinsicAnnotation, isMemoParametersDeclaration, PositionalIdTracker } from "./utils" export class ParameterTransformer extends AbstractVisitor { - private rewrites?: Map arkts.MemberExpression> + private rewrites?: Map arkts.CallExpression | arkts.MemberExpression> private skipNode?: arkts.VariableDeclaration + constructor(private positionalIdTracker: PositionalIdTracker) { + super() + } + withParameters(parameters: arkts.ETSParameterExpression[]): ParameterTransformer { this.rewrites = new Map(parameters.map((it) => { - return [it.peer, () => factory.createMemoParameterAccess(it.identifier.name)] + return [it.peer, (passArgs?: arkts.AstNode[]) => { + if (it.type instanceof arkts.ETSFunctionType) { + if (hasMemoAnnotation(it) || hasMemoIntrinsicAnnotation(it)) { + return factory.createMemoParameterAccessMemo(it.identifier.name, this.positionalIdTracker?.id(), passArgs) + } + return factory.createMemoParameterAccessCall(it.identifier.name, this.positionalIdTracker?.id(), passArgs) + } + return factory.createMemoParameterAccess(it.identifier.name) + }] })) return this } @@ -41,11 +53,24 @@ export class ParameterTransformer extends AbstractVisitor { if (/* beforeChildren === this.skipNode */ isMemoParametersDeclaration(beforeChildren)) { return beforeChildren } + if (beforeChildren instanceof arkts.CallExpression) { + if (beforeChildren.expression instanceof arkts.Identifier) { + const decl = arkts.getDecl(beforeChildren.expression) + if (decl instanceof arkts.ETSParameterExpression && this.rewrites?.has(decl.peer)) { + return this.rewrites.get(decl.peer)!( + beforeChildren.arguments.map((it) => this.visitor(it)) + ) + } + } + } const node = this.visitEachChild(beforeChildren) if (node instanceof arkts.Identifier) { const decl = arkts.getDecl(node) - if (decl instanceof arkts.ETSParameterExpression && this.rewrites?.get(decl.peer)) { - return this.rewrites.get(decl.peer)?.() ?? node + if (decl instanceof arkts.ETSParameterExpression && this.rewrites?.has(decl.peer)) { + const res = this.rewrites.get(decl.peer)!() + if (res instanceof arkts.MemberExpression) { + return res + } } } return node diff --git a/arkoala-arkts/memo-plugin/src/utils.ts b/arkoala-arkts/memo-plugin/src/utils.ts index d936ba040ba8e9e209102689571149bbaa204448..df6005f28a46e6e1a1f454b84e7539def0a70a6a 100644 --- a/arkoala-arkts/memo-plugin/src/utils.ts +++ b/arkoala-arkts/memo-plugin/src/utils.ts @@ -20,6 +20,7 @@ export enum RuntimeNames { __CONTEXT = "__context", __ID = "__id", ANNOTATION = "memo", + ANNOTATION_INTRINSIC = "memo_intrinsic", COMPUTE = "compute", CONTEXT = "__memo_context", CONTEXT_TYPE = "__memo_context_type", @@ -64,7 +65,7 @@ export class PositionalIdTracker { return `${PositionalIdTracker.callCount++}_${callName}_id_DIRNAME/${fileName}` } - id(callName: string): arkts.NumberLiteral | arkts.StringLiteral { + id(callName: string = ""): arkts.NumberLiteral | arkts.StringLiteral { const fileName = this.stableForTests ? baseName(this.filename) : @@ -81,6 +82,18 @@ export class PositionalIdTracker { } } +export function hasMemoAnnotation(node: arkts.ScriptFunction | arkts.ETSParameterExpression) { + return node.annotations.some((it) => + it.expr !== undefined && arkts.isIdentifier(it.expr) && it.expr.name === RuntimeNames.ANNOTATION + ) +} + +export function hasMemoIntrinsicAnnotation(node: arkts.ScriptFunction | arkts.ETSParameterExpression) { + return node.annotations.some((it) => + it.expr !== undefined && arkts.isIdentifier(it.expr) && it.expr.name === RuntimeNames.ANNOTATION_INTRINSIC + ) +} + /** * TODO: * @deprecated