diff --git a/arkui-plugins/memo-plugins/function-transformer.ts b/arkui-plugins/memo-plugins/function-transformer.ts index fb059b409db8920833821960f34c0ecdb1707498..f8d11cca802d8e33ae9200cae089cee3527584b4 100644 --- a/arkui-plugins/memo-plugins/function-transformer.ts +++ b/arkui-plugins/memo-plugins/function-transformer.ts @@ -54,6 +54,7 @@ import { ParameterTransformer } from './parameter-transformer'; import { ReturnTransformer } from './return-transformer'; import { SignatureTransformer } from './signature-transformer'; import { moveToFront } from '../common/arkts-utils'; +import { rewriteByType } from './memo-cache-factory'; interface ScopeInfo extends MemoInfo { regardAsSameScope?: boolean; @@ -64,6 +65,7 @@ export interface FunctionTransformerOptions extends VisitorOptions { parameterTransformer: ParameterTransformer; returnTransformer: ReturnTransformer; signatureTransformer: SignatureTransformer; + useCache?: boolean; } export class FunctionTransformer extends AbstractVisitor { @@ -71,6 +73,7 @@ export class FunctionTransformer extends AbstractVisitor { private readonly parameterTransformer: ParameterTransformer; private readonly returnTransformer: ReturnTransformer; private readonly signatureTransformer: SignatureTransformer; + private readonly useCache: boolean; /* Tracking whether should import `__memo_context_type` and `__memo_id_type` */ private modified = false; @@ -81,6 +84,7 @@ export class FunctionTransformer extends AbstractVisitor { this.parameterTransformer = options.parameterTransformer; this.returnTransformer = options.returnTransformer; this.signatureTransformer = options.signatureTransformer; + this.useCache = !!options.useCache; } private scopes: ScopeInfo[] = []; @@ -652,7 +656,33 @@ 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 ([ 80].includes(value.type)) { + // return node; + // } + if (rewriteByType.has(value.type)) { + // console.log(`[MEMO BEFORE] type: ${value.type} node: `, node.dumpSrc()); + const newNode = rewriteByType.get(value.type)!(node); + // console.log(`[MEMO AFTER] type: ${value.type} node: `, newNode.dumpSrc()); + this.modified = true; + return newNode + } else { + console.log(`[CANNOT MEMO] type: ${value.type} node: `, node.dumpSrc()); + } + } + 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 aa5c2de65a05c30a87710b35e2cb346098076e43..7cd4a249bdd0891be79658c9c61e302562fb2207 100644 --- a/arkui-plugins/memo-plugins/index.ts +++ b/arkui-plugins/memo-plugins/index.ts @@ -45,6 +45,7 @@ export function unmemoizeTransform(): Plugins { ); arkts.Performance.getInstance().createEvent('memo-checked'); + // arkts.NodeCache.getInstance().visualize(); const positionalIdTracker = new PositionalIdTracker(arkts.getFileName(), false); const parameterTransformer = new ParameterTransformer({ @@ -57,6 +58,7 @@ export function unmemoizeTransform(): Plugins { parameterTransformer, returnTransformer, signatureTransformer, + useCache: arkts.NodeCache.getInstance().isCollected() }); const programVisitor = new ProgramVisitor({ @@ -70,6 +72,8 @@ export function unmemoizeTransform(): Plugins { program = programVisitor.programVisitor(program); script = program.astNode; + arkts.NodeCache.getInstance().clear(); + arkts.Performance.getInstance().stopEvent('memo-checked', false); debugLog('[AFTER MEMO SCRIPT] script: ', script.dumpSrc()); 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 0000000000000000000000000000000000000000..a51b743ff18d180d7f7abe5aa08e2ea0bd1d3fe7 --- /dev/null +++ b/arkui-plugins/memo-plugins/memo-cache-factory.ts @@ -0,0 +1,195 @@ +/* + * 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 { + findUnmemoizedScopeInFunctionBody, + getFunctionParamsBeforeUnmemoized, + isVoidType, + mayAddLastReturn, + PositionalIdTracker, +} from './utils'; + +export class cacheFactory { + static rewriteUnionType(node: arkts.ETSUnionType): arkts.ETSUnionType { + return arkts.factory.updateUnionType( + node, + node.types.map((t) => { + if (arkts.isETSFunctionType(t)) { + return cacheFactory.rewriteFunctionType(t); + } + return t; + }) + ); + } + + static rewriteFunctionType(node: arkts.ETSFunctionType): arkts.ETSFunctionType { + return factory.updateFunctionTypeWithMemoParameters(node); + } + + static rewriteTypeAlias(node: arkts.TSTypeAliasDeclaration): arkts.TSTypeAliasDeclaration { + if (!node.typeAnnotation) { + return node; + } + let newNodeType = node.typeAnnotation; + if (arkts.isETSFunctionType(newNodeType)) { + // console.log(`[rewriteTypeAlias BEFORE] node: `, newNodeType.dumpSrc()); + newNodeType = cacheFactory.rewriteFunctionType(newNodeType); + // console.log(`[rewriteTypeAlias AFTER] node: `, newNodeType.dumpSrc()); + } else if (arkts.isETSUnionType(newNodeType)) { + // console.log(`[rewriteTypeAlias BEFORE] node: `, newNodeType.dumpSrc()); + newNodeType = cacheFactory.rewriteUnionType(newNodeType); + // console.log(`[rewriteTypeAlias AFTER] node: `, newNodeType.dumpSrc()); + } + return arkts.factory.updateTSTypeAliasDeclaration(node, node.id, node.typeParams, newNodeType); + } + + static rewriteParameter(node: arkts.ETSParameterExpression): arkts.ETSParameterExpression { + if (!node.type && !node.initializer) { + return node; + } + let newNodeType = node.type; + let newNodeInitializer = node.initializer; + if (!!newNodeType && arkts.isETSFunctionType(newNodeType)) { + newNodeType = cacheFactory.rewriteFunctionType(newNodeType); + } else if (!!newNodeType && arkts.isETSUnionType(newNodeType)) { + newNodeType = cacheFactory.rewriteUnionType(newNodeType); + } + node.type = newNodeType; + return arkts.factory.updateParameterDeclaration(node, node.identifier, newNodeInitializer); + } + + static rewriteClassProperty(node: arkts.ClassProperty): arkts.ClassProperty { + return node; // TODO: add rewrite + } + + static rewriteArrowFunction(node: arkts.ArrowFunctionExpression): arkts.ArrowFunctionExpression { + return arkts.factory.updateArrowFunction(node, cacheFactory.rewriteScriptFunction(node.scriptFunction)); + } + + static rewriteScriptFunctionBody( + node: arkts.ScriptFunction, + body: arkts.BlockStatement, + callName?: string, + hasReceiver?: boolean + ): arkts.BlockStatement { + const _hasReceiver = hasReceiver ?? node.hasReceiver; + const _callName = callName ?? node.id?.name; + const _gensymCount = 0; + const parameters = getFunctionParamsBeforeUnmemoized(node.params, _hasReceiver); + const declaredParams: arkts.Identifier[] = parameters.map( + (p) => (p as arkts.ETSParameterExpression).identifier + ); // TODO: consider gensym%% + if (findUnmemoizedScopeInFunctionBody(body, _gensymCount)) { + return body; + } + const returnType = + node.returnTypeAnnotation ?? + arkts.factory.createPrimitiveType(arkts.Es2pandaPrimitiveType.PRIMITIVE_TYPE_VOID); + const isVoidReturn = isVoidType(returnType); + const scopeDeclaration = factory.createScopeDeclaration( + returnType, + PositionalIdTracker.getInstance(arkts.getFileName()).id(_callName), + declaredParams.length + ); + const memoParametersDeclaration = node.params.length + ? factory.createMemoParameterDeclaration(declaredParams.map((p) => p.name)) + : undefined; + const syntheticReturnStatement = factory.createSyntheticReturnStatement(false); // TODO: find isStableThis + const unchangedCheck = factory.createIfStatementWithSyntheticReturnStatement( + syntheticReturnStatement, + isVoidReturn + ); + const lastReturn = mayAddLastReturn(body) + ? factory.createWrappedReturnStatement(factory.createRecacheCall(), isVoidReturn) + : undefined; + return arkts.factory.updateBlock(body, [ + scopeDeclaration, + ...(!!memoParametersDeclaration ? [memoParametersDeclaration] : []), + unchangedCheck, + ...body.statements, + ...(!!lastReturn ? [lastReturn] : []), + ]); + } + + static rewriteScriptFunction(node: arkts.ScriptFunction, callName?: string): arkts.ScriptFunction { + const isDecl = arkts.hasModifierFlag(node, arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_DECLARE); + const newParams = factory.createHiddenParameterIfNotAdded(node.params, node.hasReceiver); + const newReturnType = node.returnTypeAnnotation; // TODO: return type can be @memo + // TODO: getter/setter + const shouldRewriteBody = !isDecl && !!node.body && arkts.isBlockStatement(node.body); + const newBody = shouldRewriteBody + ? cacheFactory.rewriteScriptFunctionBody(node, node.body, callName) + : node.body; // TODO: change body for normal class + return arkts.factory.updateScriptFunction( + node, + newBody, + arkts.factory.createFunctionSignature(node.typeParams, newParams, newReturnType, node.hasReceiver), + node.flags, + node.modifiers + ); + } + + static rewriteMethodDefinition(node: arkts.MethodDefinition): arkts.MethodDefinition { + // TODO: getter/setter + return arkts.factory.updateMethodDefinition( + node, + node.kind, + node.name, + cacheFactory.rewriteScriptFunction(node.scriptFunction, node.name.name), + node.modifiers, + false + ); + } + + static rewriteCallExpression(node: arkts.CallExpression): arkts.CallExpression { + let callName: string | undefined; + if (arkts.isIdentifier(node.expression)) { + callName = node.expression.name; + } else if (arkts.isMemberExpression(node.expression) && arkts.isIdentifier(node.expression.property)) { + callName = node.expression.property.name; + } + // console.log(`[rewriteCallExpression BEFORE] node: `, node.dumpSrc()); + const newNode = factory.insertHiddenArgumentsToCall( + node, + PositionalIdTracker.getInstance(arkts.getFileName()).id(callName) + ); + // console.log(`[rewriteCallExpression AFTER] node: `, newNode.dumpSrc()); + return newNode; + } + + static rewriteIdentifier(node: arkts.Identifier): arkts.Identifier | arkts.MemberExpression { + return factory.createMemoParameterAccess(node.name); // TODO: consider gensym%% + } + + static rewriteReturnStatement(node: arkts.ReturnStatement): arkts.ReturnStatement | arkts.BlockStatement { + return factory.createWrappedReturnStatement(factory.createRecacheCall(node.argument), !node.argument); + } +} + +export const rewriteByType = new Map arkts.AstNode>([ + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_UNION_TYPE, cacheFactory.rewriteUnionType], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_FUNCTION_TYPE, cacheFactory.rewriteFunctionType], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_TS_TYPE_ALIAS_DECLARATION, cacheFactory.rewriteTypeAlias], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_PARAMETER_EXPRESSION, cacheFactory.rewriteParameter], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CLASS_PROPERTY, cacheFactory.rewriteClassProperty], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ARROW_FUNCTION_EXPRESSION, cacheFactory.rewriteArrowFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_SCRIPT_FUNCTION, cacheFactory.rewriteScriptFunction], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_METHOD_DEFINITION, cacheFactory.rewriteMethodDefinition], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_CALL_EXPRESSION, cacheFactory.rewriteCallExpression], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_IDENTIFIER, cacheFactory.rewriteIdentifier], + [arkts.Es2pandaAstNodeType.AST_NODE_TYPE_RETURN_STATEMENT, cacheFactory.rewriteReturnStatement], +]); diff --git a/arkui-plugins/memo-plugins/memo-factory.ts b/arkui-plugins/memo-plugins/memo-factory.ts index 37eed959b1d779988e23d86a63d8bfc13f68966e..237bacf18a3cde7886ed0d6a5c9925fc0a0108b1 100644 --- a/arkui-plugins/memo-plugins/memo-factory.ts +++ b/arkui-plugins/memo-plugins/memo-factory.ts @@ -17,7 +17,7 @@ import * as arkts from '@koalaui/libarkts'; import { fixGensymParams, buildeParamInfos, - isUnmemoizedInFunction, + isUnmemoizedInFunctionParams, mayAddLastReturn, ParamInfo, ReturnTypeInfo, @@ -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]; @@ -298,6 +298,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 { diff --git a/arkui-plugins/memo-plugins/utils.ts b/arkui-plugins/memo-plugins/utils.ts index 51a17d0321eac6b9591a81ce75caa7ff7877d476..1da1a9f340cd93d4d41c52c5c904f5198a373520 100644 --- a/arkui-plugins/memo-plugins/utils.ts +++ b/arkui-plugins/memo-plugins/utils.ts @@ -41,7 +41,7 @@ export enum RuntimeNames { SCOPE = '__memo_scope', THIS = 'this', VALUE = 'value', - EQUAL_T = '=t' + EQUAL_T = '=t', } export interface ReturnTypeInfo { @@ -69,6 +69,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! @@ -499,11 +508,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 { @@ -527,16 +542,52 @@ 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, diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts index 7171c74dde56200f6a946f4f45bb86907ca7857e..87e3f59fa73f1aac2b85f38e47963eb921a7fc44 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/factory.ts @@ -14,13 +14,7 @@ */ import * as arkts from '@koalaui/libarkts'; -import { - addMemoAnnotation, - BuilderLambdaNames, - CustomComponentNames, - findCanAddMemoFromParamExpression, - isCustomComponentAnnotation, -} from '../utils'; +import { BuilderLambdaNames } from '../utils'; import { backingField, filterDefined, removeAnnotationByName } from '../../common/arkts-utils'; import { BuilderLambdaDeclInfo, @@ -41,12 +35,19 @@ import { isStyleChainedCall, isStyleWithReceiverCall, builderLambdaType, + BuilderLambdaSecondLastArgInfo, + buildSecondLastArgInfo, } from './utils'; import { isDecoratorIntrinsicAnnotation } from '../property-translators/utils'; import { factory as PropertyFactory } from '../property-translators/factory'; import { ProjectConfig } from '../../common/plugin-context'; -import { BindableDecl, DecoratorIntrinsicNames, StructDecoratorNames } from '../../common/predefines'; +import { BindableDecl, DecoratorIntrinsicNames } from '../../common/predefines'; import { ImportCollector } from '../../common/import-collector'; +import { + addMemoAnnotation, + collectMemoableInfoInParameter, + findCanAddMemoFromParameter, +} from '../memo-translators/utils'; export class factory { /** @@ -229,9 +230,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( @@ -246,7 +249,7 @@ export class factory { arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC ); - return arkts.factory.createArrowFunction(func); + return addMemoAnnotation(arkts.factory.createArrowFunction(func)); } /* @@ -269,25 +272,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; } @@ -379,16 +372,22 @@ 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, projectConfig); + if (canAddMemo) { + // console.log("[DEBUG] newNode: ", newNode.dumpSrc()); + addMemoAnnotation(newNode); + } + return newNode; } // this is too optimistic to check if this is an options argument... if (arkts.isTSAsExpression(arg) || arkts.isObjectExpression(arg)) { @@ -397,6 +396,19 @@ 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.isXComponent && !!argInfo.packageInfo) { + const packageInfoNode = arkts.factory.createStringLiteral(argInfo.packageInfo); + return this.createOrUpdateArgInBuilderLambda(packageInfoNode, undefined, undefined); + } else if (!argInfo.isFunctionCall) { + return this.createOrUpdateArgInBuilderLambda(arkts.factory.createUndefinedLiteral(), undefined, undefined); + } + return undefined; + } + /** * transform arguments in a builder lambda call. */ @@ -406,52 +418,63 @@ export class factory { declInfo: BuilderLambdaDeclInfo, projectConfig: ProjectConfig | undefined ): (arkts.AstNode | undefined)[] { + // if (leaf.expression.dumpSrc().includes('Column')) { + // console.log('[COLUMN] leaf: ', leaf.dumpSrc()); + // } + 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)]; + const args: (arkts.AstNode | undefined)[] = [ + this.createStyleArgInBuilderLambda(lambdaBody, returnType, moduleName), + ]; + const secondLastArgInfo = buildSecondLastArgInfo(type, projectConfig, isFunctionCall); 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, - type?.name, - arkts.factory.createUndefinedLiteral() - ) - ); - } else if (type?.name === 'XComponent' && index === params.length - 1) { - let packageInfo: string = ''; - if (projectConfig?.bundleName && projectConfig?.moduleName) { - packageInfo = projectConfig?.bundleName + '/' + projectConfig?.moduleName; - } - const packageInfoNode = arkts.factory.createStringLiteral(packageInfo); - args.push( - this.createOrUpdateArgInBuilderLambda( - leaf.arguments.at(index), - projectConfig, - type?.name, - packageInfoNode - ) - ); - } else { - args.push(this.createOrUpdateArgInBuilderLambda(leaf.arguments.at(index), projectConfig, type?.name)); + let skips = 0; + while (index < params.length - 1) { + const memoableInfo = collectMemoableInfoInParameter(params.at(index)!); + const canAddMemo = (!!memoableInfo.hasBuilder || !!memoableInfo.hasMemo) && !!memoableInfo.hasProperType; + const currIndex = leaf.isTrailingCall ? index - 1 : index; + let argument: arkts.Expression | undefined = currIndex < 0 ? undefined : leaf.arguments.at(currIndex); + if (!!argument) { + skips += 1; } + args.push(this.createOrUpdateArgInBuilderLambda(arkts.factory.createUndefinedLiteral(), argument, projectConfig, type?.name, canAddMemo)); index++; } + args.push(this.createSecondLastArgInBuilderLambda(secondLastArgInfo)); + + // console.log("[DEBUG] leaf: ", leaf.dumpSrc()); + // console.log("[DEBUG] leaf isTrailingCall: ", leaf.isTrailingCall); + // console.log("[DEBUG] parameter length: ", params.length); + // console.log("[DEBUG] argument length: ", leaf.arguments.length); + // console.log("[DEBUG] index: ", index); + // console.log("[DEBUG] skips: ", skips); + + // const paramDecl = params.at(params.length - 1); + // console.log("[DEBUG] paramDecl LAST: ", paramDecl?.dumpSrc()); + const memoableInfo = collectMemoableInfoInParameter(params.at(params.length - 1)!); + const canAddMemo = (!!memoableInfo.hasBuilder || !!memoableInfo.hasMemo) && !!memoableInfo.hasProperType; + // console.log("[DEBUG] canAddMemo LAST: ", canAddMemo); + const lastIndex = leaf.isTrailingCall ? leaf.arguments.length - 1 : leaf.arguments.length + skips; + const argument = leaf.arguments.at(lastIndex); + // console.log("[DEBUG] node LAST: ", node?.dumpSrc()); + args.push(this.createOrUpdateArgInBuilderLambda(arkts.factory.createUndefinedLiteral(), argument, projectConfig, type?.name, canAddMemo)); + + // const paramDecl = params.at(params.length - 1); + // // console.log("[DEBUG] paramDecl LAST: ", paramDecl?.dumpSrc()); + // const memoableInfo = collectMemoableInfoInParameter(params.at(params.length - 1)!); + // const canAddMemo = (!!memoableInfo.hasBuilder || !!memoableInfo.hasMemo) && !!memoableInfo.hasProperType; + // // console.log("[DEBUG] canAddMemo LAST: ", canAddMemo); + // args.push( + // this.createOrUpdateArgInBuilderLambda( + // undefined, + // leaf.arguments.at(leaf.arguments.length - 1), + // projectConfig, + // type?.name, + // canAddMemo + // ) + // ); + return filterDefined(args); } @@ -548,12 +571,14 @@ export class factory { factory.transformBuilderLambdaMethodDecl(method) ); - return this.updateBuilderLambdaMethodDecl( + const newNode = this.updateBuilderLambdaMethodDecl( node, prefixArgs, removeAnnotationByName(func.annotations, BuilderLambdaNames.ANNOTATION_NAME), replaceBuilderLambdaDeclMethodName(node.name.name) ).setOverloads(newOverloads); + arkts.NodeCache.getInstance().collect(newNode); + return newNode; } /** @@ -635,6 +660,7 @@ 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); }); @@ -646,7 +672,9 @@ export class factory { declInfo, projectConfig ); - return arkts.factory.updateCallExpression(node, replace, leaf.typeArguments, filterDefined(args)); + 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 7aac15fc1779275eb7aedafbf4e8e2cd6364c2a3..2843c688f33ff0209fdb9a9a16379b889cef8c88 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts @@ -15,10 +15,11 @@ 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'; +import { ProjectConfig } from '../../common/plugin-context'; export type BuilderLambdaDeclInfo = { isFunctionCall: boolean; // isFunctionCall means it is from $_instantiate. @@ -34,6 +35,52 @@ export type InstanceCallInfo = { call: arkts.CallExpression; }; +export type BuilderLambdaArgInfo = { + isFunctionCall: boolean; +}; + +export type BuilderLambdaReusableArgInfo = { + isReusable?: boolean; + reuseId?: string; +}; + +export type BuilderLambdaXComponentArgInfo = { + isXComponent?: boolean; + packageInfo?: string; +}; + +export type BuilderLambdaSecondLastArgInfo = BuilderLambdaArgInfo & + BuilderLambdaReusableArgInfo & + BuilderLambdaXComponentArgInfo; + +export function buildSecondLastArgInfo( + type: arkts.Identifier | undefined, + projectConfig: ProjectConfig | undefined, + isFunctionCall: boolean +): BuilderLambdaSecondLastArgInfo { + let isReusable: boolean | undefined; + let reuseId: string | undefined; + let isXComponent: boolean | undefined; + let packageInfo: string = ''; + 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; + } + if (type?.name === 'XComponent') { + isXComponent = true; + if (projectConfig?.bundleName && projectConfig?.moduleName) { + packageInfo = projectConfig?.bundleName + '/' + projectConfig?.moduleName; + } + } + return { isFunctionCall, isReusable, reuseId, isXComponent, packageInfo }; +} + /** * 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 357a4183021aeb35a44a662531c1d963b8310337..7ba5247e3696c1d735a2ce17a947111932190e48 100644 --- a/arkui-plugins/ui-plugins/checked-transformer.ts +++ b/arkui-plugins/ui-plugins/checked-transformer.ts @@ -19,37 +19,38 @@ 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, -} from './struct-translators/utils'; +import { collectCustomComponentScopeInfo, CustomComponentNames, isCustomComponentClass } from './utils'; +import { CustomComponentScopeInfo, ScopeInfoCollection, isResourceNode } from './struct-translators/utils'; import { isBuilderLambda, isBuilderLambdaMethodDecl } from './builder-lambda-translators/utils'; import { isEntryWrapperClass } from './entry-translators/utils'; import { ImportCollector } from '../common/import-collector'; import { DeclarationCollector } from '../common/declaration-collector'; import { PropertyCache } from './property-translators/utils'; import { isArkUICompatible, generateArkUICompatible } from './interop/interop'; +import { + addMemoAnnotation, + findCanAddMemoFromArrowFunction, + findCanAddMemoFromMethod, + findCanAddMemoFromParameter, + findCanAddMemoFromTypeAlias, +} from './memo-translators/utils'; +// import { StructTransformer } from './struct-translators/struct-transformer'; export class CheckedTransformer extends AbstractVisitor { + // private structTransformer: StructTransformer; private scope: ScopeInfoCollection; projectConfig: ProjectConfig | undefined; constructor(projectConfig: ProjectConfig | undefined) { super(); this.projectConfig = projectConfig; + // this.structTransformer = new StructTransformer(this.projectConfig); this.scope = { customComponents: [] }; } reset(): void { super.reset(); + // this.structTransformer.reset(); this.scope = { customComponents: [] }; PropertyCache.getInstance().reset(); ImportCollector.getInstance().reset(); @@ -84,13 +85,18 @@ export class CheckedTransformer extends AbstractVisitor { visitor(beforeChildren: arkts.AstNode): arkts.AstNode { this.enter(beforeChildren); + // this.structTransformer.enter(beforeChildren); if (arkts.isCallExpression(beforeChildren) && isBuilderLambda(beforeChildren)) { const lambda = builderLambdaFactory.transformBuilderLambda(beforeChildren, this.projectConfig); 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); } + // return this.structTransformer.visitor(beforeChildren); const node = this.visitEachChild(beforeChildren); if ( arkts.isClassDeclaration(node) && @@ -112,12 +118,16 @@ 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)) { + } else if (findCanAddMemoFromTypeAlias(node)) { return addMemoAnnotation(node); + } else if (findCanAddMemoFromParameter(node)) { + return addMemoAnnotation(node); + } else if (findCanAddMemoFromMethod(node)) { + return addMemoAnnotation(node.scriptFunction); + } else if (findCanAddMemoFromArrowFunction(node)) { + return addMemoAnnotation(node.scriptFunction); } else if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { ImportCollector.getInstance().insertCurrentImports(this.program); - } else if (arkts.isTSTypeAliasDeclaration(node)) { - return structFactory.transformTSTypeAlias(node); } return node; } diff --git a/arkui-plugins/ui-plugins/entry-translators/factory.ts b/arkui-plugins/ui-plugins/entry-translators/factory.ts index 09d1c85b7f988633145c62c6a30d163999883027..4fedb602aeb3336f844e291cb6797de947af6e8d 100644 --- a/arkui-plugins/ui-plugins/entry-translators/factory.ts +++ b/arkui-plugins/ui-plugins/entry-translators/factory.ts @@ -17,6 +17,7 @@ import * as arkts from '@koalaui/libarkts'; import { EntryWrapperNames } from './utils'; import { annotation, createAndInsertImportDeclaration } from '../../common/arkts-utils'; import { ENTRY_POINT_IMPORT_SOURCE_NAME } from '../../common/predefines'; +import { addMemoAnnotation } from '../memo-translators/utils'; export class factory { /** @@ -219,7 +220,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 a50014ecfaaa4482de0f3059feb2b273b93a3327..d856ea9095ff6340664713ca5532a8a2bbbdc2fe 100644 --- a/arkui-plugins/ui-plugins/index.ts +++ b/arkui-plugins/ui-plugins/index.ts @@ -114,9 +114,9 @@ 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', false); + // arkts.Performance.getInstance().createEvent('ui-recheck'); + // arkts.recheckSubtree(script); + // arkts.Performance.getInstance().stopEvent('ui-recheck', false); arkts.Performance.getInstance().clearAllEvents(false); arkts.Performance.getInstance().visualizeEvents(true); arkts.Performance.getInstance().clearHistory(); diff --git a/arkui-plugins/ui-plugins/memo-translators/utils.ts b/arkui-plugins/ui-plugins/memo-translators/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad4e46be8b5f01eeb92b4551b657592cf4ee8905 --- /dev/null +++ b/arkui-plugins/ui-plugins/memo-translators/utils.ts @@ -0,0 +1,330 @@ +/* + * 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 } from '../../common/arkts-utils'; +import { ImportCollector } from '../../common/import-collector'; +import { DecoratorNames, MEMO_IMPORT_SOURCE_NAME } from '../../common/predefines'; +import { isDecoratorAnnotation } from '../property-translators/utils'; + +export enum MemoNames { + MEMO = 'memo', +} + +export type MemoAstNode = + | arkts.ScriptFunction + | arkts.ETSParameterExpression + | arkts.ClassProperty + | arkts.TSTypeAliasDeclaration + | arkts.ETSFunctionType + | arkts.ArrowFunctionExpression + | arkts.ETSUnionType; + +interface MemoableAnnotationInfo { + hasMemo?: boolean; + hasBuilder?: boolean; +} + +type MemoableInfo = MemoableAnnotationInfo & { + hasProperType?: boolean; +} + +export function hasMemoAnnotation(node: T): boolean { + return node.annotations.some((it) => isMemoAnnotation(it, MemoNames.MEMO)); +} + +export function hasMemoableAnnotation(node: T): MemoableAnnotationInfo { + let hasBuilder: boolean = false; + let hasMemo: boolean = false; + node.annotations.forEach((it) => { + hasBuilder ||= isDecoratorAnnotation(it, DecoratorNames.BUILDER); + hasMemo ||= isMemoAnnotation(it, MemoNames.MEMO); + }); + return { + ...(hasMemo ? { hasMemo } : {}), + ...(hasBuilder ? { hasBuilder }: {}) + }; +} + +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.isETSUnionType(node)) { + return currInfo; + } + node.types.forEach((t) => { + currInfo = { + ...currInfo, + ...collectMemoableInfoInTypeRerence(t), + ...collectMemoableInfoInFunctionType(t) + } + }); + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + return currInfo; +} + +export function collectMemoableInfoInTypeRerence(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + 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.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.isTSTypeAliasDeclaration(node)) { + return currInfo; + } + currInfo.hasProperType = true; + 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.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 collectMemoableInfoInClassProperty(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + 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.isArrowFunctionExpression(node)) { + return currInfo; + } + currInfo.hasProperType = true; + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + if (!!node.scriptFunction) { + currInfo = { + ...currInfo, + ...collectMemoableInfoInScriptFunction(node.scriptFunction) + } + } + return currInfo; +} + +export function collectMemoableInfoInScriptFunction(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + if (!arkts.isScriptFunction(node)) { + return currInfo; + } + currInfo.hasProperType = true; + currInfo = { ...currInfo, ...hasMemoableAnnotation(node) }; + return currInfo; +} + +export function collectMemoableInfoInType(node: arkts.AstNode, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + return { + ...currInfo, + ...collectMemoableInfoInFunctionType(node), + ...collectMemoableInfoInUnionType(node), + ...collectMemoableInfoInTypeRerence(node) + } +} + +export function collectMemoableInfo(node: T, info?: MemoableInfo): MemoableInfo { + let currInfo = info ?? {}; + return { + ...currInfo, + ...collectMemoableInfoInScriptFunction(node), + ...collectMemoableInfoInClassProperty(node), + ...collectMemoableInfoInParameter(node), + ...collectMemoableInfoInTypeAlias(node), + ...collectMemoableInfoInFunctionType(node), + ...collectMemoableInfoInArrowFunction(node), + ...collectMemoableInfoInUnionType(node) + } +} + +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; +} + +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); + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo; +} + +export function findCanAddMemoFromArrowFunction(node: arkts.AstNode): node is arkts.ArrowFunctionExpression { + if (!arkts.isArrowFunctionExpression(node)) { + return false; + } + const memoableInfo = collectMemoableInfoInArrowFunction(node); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(node); + } + + if (!!node.scriptFunction.returnTypeAnnotation) { + const returnTypeMemoableInfo = collectMemoableInfoInType(node.scriptFunction.returnTypeAnnotation); + let shouldCollectReturnStatement = !!node.scriptFunction.body && arkts.isBlockStatement(node.scriptFunction.body); + if (!!returnTypeMemoableInfo.hasMemo && !!returnTypeMemoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(node.scriptFunction.returnTypeAnnotation); + shouldCollectReturnStatement &&= true; + } + if (shouldCollectReturnStatement) { + // TODO: collect return statement + } + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo; +} + +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; +} + +export function findCanAddMemoFromMethod(node: arkts.AstNode): node is arkts.MethodDefinition { + if (!arkts.isMethodDefinition(node)) { + return false; + } + const memoableInfo = collectMemoableInfoInScriptFunction(node.scriptFunction); + if (!!memoableInfo.hasMemo && !!memoableInfo.hasProperType) { + arkts.NodeCache.getInstance().collect(node); + } + return !!memoableInfo.hasBuilder && !memoableInfo.hasMemo; +} + +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)) { + 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 isMemoableAnotation(annotation: arkts.AnnotationUsage): boolean { + return isDecoratorAnnotation(annotation, DecoratorNames.BUILDER) || isMemoAnnotation(annotation, MemoNames.MEMO); +} diff --git a/arkui-plugins/ui-plugins/property-translators/builderParam.ts b/arkui-plugins/ui-plugins/property-translators/builderParam.ts index 44266f3d13023e92ccfa5fd4599624c0c64e3b32..6bfdb2947ece273e8bbbc953b26aa33e0de270b1 100644 --- a/arkui-plugins/ui-plugins/property-translators/builderParam.ts +++ b/arkui-plugins/ui-plugins/property-translators/builderParam.ts @@ -27,8 +27,8 @@ import { } 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, findCanAddMemoFromParameter, findCanAddMemoFromTypeAnnotation } from '../memo-translators/utils'; export class BuilderParamTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { translateMember(): arkts.AstNode[] { @@ -147,7 +147,7 @@ export class BuilderParamInterfaceTranslator e removeDecorator(method, DecoratorNames.BUILDER_PARAM); } else if (method.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET) { const param: arkts.Expression | undefined = method.scriptFunction.params.at(0); - if (findCanAddMemoFromParamExpression(param)) { + if (findCanAddMemoFromParameter(param)) { addMemoAnnotation(param); } removeDecorator(method, DecoratorNames.BUILDER_PARAM); diff --git a/arkui-plugins/ui-plugins/property-translators/factory.ts b/arkui-plugins/ui-plugins/property-translators/factory.ts index f7704736399995e23dbd5acf19d7247e5d2c753d..a13bee51805b656860829608bedaeeb3a62863d1 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 '../memo-translators/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 2a750395a60900508b7a02a32890b5bf923031f5..1648322e6cf505745d1250ab4322f5a1d4bbf414 100644 --- a/arkui-plugins/ui-plugins/property-translators/utils.ts +++ b/arkui-plugins/ui-plugins/property-translators/utils.ts @@ -25,7 +25,7 @@ import { StateManagementTypes, GetSetTypes } from '../../common/predefines'; -import { addMemoAnnotation, findCanAddMemoFromParamExpression, findCanAddMemoFromTypeAnnotation } from '../utils'; +import { addMemoAnnotation, findCanAddMemoFromParameter, findCanAddMemoFromTypeAnnotation } from '../memo-translators/utils'; export interface DecoratorInfo { annotation: arkts.AnnotationUsage; @@ -211,7 +211,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 fc0a1ca57d897a7427b9baad1b6e7c44f33db388..3fa12954318c8a7f138292e754f05f14d90df8a2 100644 --- a/arkui-plugins/ui-plugins/struct-translators/factory.ts +++ b/arkui-plugins/ui-plugins/struct-translators/factory.ts @@ -16,18 +16,16 @@ import * as arkts from '@koalaui/libarkts'; import { BuilderLambdaNames, - addMemoAnnotation, CustomComponentNames, getCustomComponentOptionsName, getGettersFromClassDecl, getTypeNameFromTypeParameter, getTypeParamsFromClassDecl, isCustomComponentInterface, - MemoNames, } 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, @@ -39,7 +37,6 @@ import { import { CustomComponentScopeInfo, isEtsGlobalClass, isKnownMethodDefinition } 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 { ARKUI_COMPONENT_COMMON_SOURCE_NAME, @@ -48,6 +45,7 @@ import { StateManagementTypes, } from '../../common/predefines'; import { ObservedTrackTranslator } from '../property-translators/observedTrack'; +import { addMemoAnnotation } from '../memo-translators/utils'; export class factory { /* @@ -92,13 +90,15 @@ export class factory { const modifiers: arkts.Es2pandaModifierFlags = isDecl ? arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_ABSTRACT : arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PUBLIC; - return arkts.factory.createMethodDefinition( + const newNode = arkts.factory.createMethodDefinition( arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_METHOD, updateKey, updateScriptFunction, modifiers, false ); + // arkts.NodeCache.getInstance().collect(newNode); + return newNode } /* @@ -529,13 +529,6 @@ export class factory { return arkts.factory.updateClassDeclaration(node, newClassDef); } - static transformTSTypeAlias(node: arkts.TSTypeAliasDeclaration): arkts.TSTypeAliasDeclaration { - if (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 05b470a094ad4abbd31a1c6c481206577da420f5..86148273a4cb592d5f9cbff926eec509767efc63 100644 --- a/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts +++ b/arkui-plugins/ui-plugins/struct-translators/struct-transformer.ts @@ -17,18 +17,19 @@ 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 { CustomComponentScopeInfo, findCanAddMemoFromArrowFunction, isResourceNode, ScopeInfoCollection } from './utils'; +import { CustomComponentScopeInfo, isResourceNode, ScopeInfoCollection } from './utils'; import { factory } from './factory'; import { isEntryWrapperClass } from '../entry-translators/utils'; 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 { addMemoAnnotation, findCanAddMemoFromArrowFunction, findCanAddMemoFromMethod, findCanAddMemoFromParameter, findCanAddMemoFromTypeAlias } from '../memo-translators/utils'; +import { generateArkUICompatible, isArkUICompatible } from '../interop/interop'; export class StructTransformer extends AbstractVisitor { private scope: ScopeInfoCollection; @@ -76,6 +77,10 @@ export class StructTransformer extends AbstractVisitor { visitor(beforeChildren: arkts.AstNode): arkts.AstNode { this.enter(beforeChildren); const node = this.visitEachChild(beforeChildren); + // if (arkts.isCallExpression(node)) { + // console.log(`[UPCALL 2] ptr: ${node.peer} lambda: `, node.dumpSrc()); + // // arkts.NodeCache.getInstance().collect(node); + // } if ( arkts.isClassDeclaration(node) && this.scope.customComponents.length > 0 && @@ -92,10 +97,18 @@ export class StructTransformer extends AbstractVisitor { return factory.transformNormalClass(node); } else if (arkts.isCallExpression(node) && isResourceNode(node)) { return factory.transformResource(node, this.projectConfig); + } 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)) { + } else if (findCanAddMemoFromTypeAlias(node)) { + return addMemoAnnotation(node); + } else if (findCanAddMemoFromParameter(node)) { return addMemoAnnotation(node); + } else if (findCanAddMemoFromMethod(node)) { + return addMemoAnnotation(node.scriptFunction); + } else if (findCanAddMemoFromArrowFunction(node)) { + return addMemoAnnotation(node.scriptFunction); } else if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { ImportCollector.getInstance().insertCurrentImports(this.program); } diff --git a/arkui-plugins/ui-plugins/struct-translators/utils.ts b/arkui-plugins/ui-plugins/struct-translators/utils.ts index 7c68e9429b804c4cbd1ce2406ece29789c2f34a9..a140a2e877aee0470b755eade771a7a4a2bcfc92 100644 --- a/arkui-plugins/ui-plugins/struct-translators/utils.ts +++ b/arkui-plugins/ui-plugins/struct-translators/utils.ts @@ -14,11 +14,10 @@ */ 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, DecoratorNames, Dollars } from '../../common/predefines'; import { DeclarationCollector } from '../../common/declaration-collector'; +import { ARKUI_IMPORT_PREFIX_NAMES, Dollars } from '../../common/predefines'; export type ScopeInfoCollection = { customComponents: CustomComponentScopeInfo[]; @@ -85,35 +84,3 @@ 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; -} diff --git a/arkui-plugins/ui-plugins/ui-factory.ts b/arkui-plugins/ui-plugins/ui-factory.ts index c5f735bf9ead722c29cb2952d73f1dd2e00aca33..faddfa3e2b107d46646c4661e37ba37b1dc61c8e 100644 --- a/arkui-plugins/ui-plugins/ui-factory.ts +++ b/arkui-plugins/ui-plugins/ui-factory.ts @@ -15,16 +15,15 @@ import * as arkts from '@koalaui/libarkts'; import { - addMemoAnnotation, BuilderLambdaNames, 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 './memo-translators/utils'; export interface ScriptFunctionConfiguration { key: arkts.Identifier | undefined; @@ -104,9 +103,7 @@ export class factory { static createStyleParameter(typeName: string): arkts.ETSParameterExpression { const styleParam: arkts.Identifier = factory.createStyleIdentifier(typeName); const param: arkts.ETSParameterExpression = arkts.factory.createParameterDeclaration(styleParam, undefined); - if (findCanAddMemoFromParamExpression(param)) { - addMemoAnnotation(param); - } + addMemoAnnotation(param); return param; } @@ -149,9 +146,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; } diff --git a/arkui-plugins/ui-plugins/utils.ts b/arkui-plugins/ui-plugins/utils.ts index d23949753b1cb923460b434eda629197a7411caa..da3a98d91523d62b996ac4d13db6fbbd7454eab7 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', @@ -44,10 +43,6 @@ export enum BuilderLambdaNames { ANIMATION_STOP = 'animationStop', } -export enum MemoNames { - MEMO = 'memo', -} - // IMPORT export function findImportSourceByName(importName: string): string { const source = DeclarationCollector.getInstance().findExternalSourceFromName(importName); @@ -237,81 +232,3 @@ export function isCustomComponentInterface(node: arkts.TSInterfaceDeclaration): 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; -} diff --git a/koala-wrapper/native/include/common.h b/koala-wrapper/native/include/common.h index 399d28d2229fe63c5d7a8f730469ef5694ccb3a0..ad087c22ac708ac1c025f3c132163283bfff2739 100644 --- a/koala-wrapper/native/include/common.h +++ b/koala-wrapper/native/include/common.h @@ -26,6 +26,11 @@ using std::string, std::cout, std::endl, std::vector; +// struct AstNodeContext { +// KInt astNodeType = 0; +// es2panda_AstNode *ptr = nullptr; +// }; + es2panda_Impl *GetImpl(); string getString(KStringPtr ptr); diff --git a/koala-wrapper/native/src/bridges.cc b/koala-wrapper/native/src/bridges.cc index 5333ea8b5ff7c89d24ad985d36c6bb296b4ca626..961dee6f77d1e72923ce3226ddcf8892c46e014c 100644 --- a/koala-wrapper/native/src/bridges.cc +++ b/koala-wrapper/native/src/bridges.cc @@ -19,9 +19,68 @@ #include #include +// thread_local std::unordered_map cachedAstNodeContext; std::set globalStructInfo; std::mutex g_structMutex; +// KInt impl_AstNodeContextAstNodeType(KNativePointer context, KNativePointer receiver) +// { +// const auto astNodeContext = reinterpret_cast(receiver); +// return astNodeContext->astNodeType; +// } +// KOALA_INTEROP_2(AstNodeContextAstNodeType, KInt, KNativePointer, KNativePointer); + +// KNativePointer impl_AstNodeContextAstNodePointer(KNativePointer context, KNativePointer receiver) +// { +// const auto astNodeContext = reinterpret_cast(receiver); +// return astNodeContext->ptr; +// } +// KOALA_INTEROP_2(AstNodeContextAstNodePointer, KNativePointer, KNativePointer, KNativePointer); + +// void impl_InsertAstNodeContext(KNativePointer context, KNativePointer receiver) +// { +// const auto _context = reinterpret_cast(context); +// const auto _receiver = reinterpret_cast(receiver); +// const auto type = GetImpl()->AstNodeTypeConst(_context, _receiver); +// AstNodeContext* astNodeContext = new AstNodeContext(); +// astNodeContext->astNodeType = type; +// astNodeContext->ptr = _receiver; +// cachedAstNodeContext[receiver] = astNodeContext; +// return; +// } +// KOALA_INTEROP_V2(InsertAstNodeContext, KNativePointer, KNativePointer); + +// AstNodeContext* impl_GetAstNodeContextFromCache(KNativePointer context, KNativePointer receiver) +// { +// const auto _receiver = reinterpret_cast(receiver); +// if (cachedAstNodeContext.count(_receiver) == 0U) { +// return nullptr; +// } +// return nullptr; +// } +// KOALA_INTEROP_2(GetAstNodeContextFromCache, AstNodeContext, KNativePointer, KStringPtr); + +// KBoolean impl_HasAstNodeContextInCache(KNativePointer context, KStringPtr& keyPtr) +// { +// return cachedAstNodeContext.find(keyPtr) != cachedAstNodeContext.end(); +// } +// KOALA_INTEROP_2(HasAstNodeContextInCache, KBoolean, KNativePointer, KStringPtr); + +// void impl_ClearAstNodeContextCache(KNativePointer context) +// { +// for (auto& pair : cachedAstNodeContext) { +// delete pair.second; +// } +// cachedAstNodeContext.clear(); +// } +// KOALA_INTEROP_V1(ClearAstNodeContextCache, KNativePointer); + +// KBoolean impl_IsAstNodeContextCacheEmpty(KNativePointer context) +// { +// return cachedAstNodeContext.empty(); +// } +// KOALA_INTEROP_1(ClearAstNodeContextCache, KBoolean, KNativePointer); + void impl_InsertGlobalStructInfo(KNativePointer contextPtr, KStringPtr& instancePtr) { std::lock_guard lock(g_structMutex); @@ -316,6 +375,23 @@ static KNativePointer impl_ExternalSourcePrograms(KNativePointer instance) } KOALA_INTEROP_1(ExternalSourcePrograms, KNativePointer, KNativePointer); +KNativePointer impl_CreateContextGenerateAbcForExternalSourceFiles(KNativePointer configPtr, KInt fileNamesCount, KStringArray fileNames) +{ + auto config = reinterpret_cast(configPtr); + const std::size_t HEADER_LEN = 4; + const char **argv = new const char *[static_cast(fileNamesCount)]; + std::size_t position = HEADER_LEN; + std::size_t strLen; + for (std::size_t i = 0; i < static_cast(fileNamesCount); ++i) { + strLen = unpackUInt(fileNames + position); + position += HEADER_LEN; + argv[i] = strdup(std::string(reinterpret_cast(fileNames + position), strLen).c_str()); + position += strLen; + } + return GetImpl()->CreateContextGenerateAbcForExternalSourceFiles(config, fileNamesCount, argv); +} +KOALA_INTEROP_3(CreateContextGenerateAbcForExternalSourceFiles, KNativePointer, KNativePointer, KInt, KStringArray) + KBoolean impl_IsClassProperty(KNativePointer nodePtr) { auto node = reinterpret_cast(nodePtr); @@ -627,6 +703,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) { @@ -646,6 +723,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) { @@ -656,3 +734,12 @@ void impl_LogDiagnosticWithSuggestion(KNativePointer context, KNativePointer dia GetImpl()->LogDiagnosticWithSuggestion(_context, _diagnosticInfo, _suggestionInfo, _range); } 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); + diff --git a/koala-wrapper/src/Es2pandaNativeModule.ts b/koala-wrapper/src/Es2pandaNativeModule.ts index 6c5b48e7d330356d41d068180c0b263f13838de7..89c01f7d500cd630978478261cb85dcbd63d71de 100644 --- a/koala-wrapper/src/Es2pandaNativeModule.ts +++ b/koala-wrapper/src/Es2pandaNativeModule.ts @@ -105,6 +105,10 @@ export class Es2pandaNativeModule { _CreateContextFromString(config: KPtr, source: String, filename: String): KPtr { throw new Error('Not implemented'); } + _CreateContextGenerateAbcForExternalSourceFiles(config: KPtr, fileCount: KInt, filenames: + string[]): KPtr { + throw new Error('Not implemented'); + } _CreateContextFromFile(config: KPtr, filename: String): KPtr { throw new Error('Not implemented'); } @@ -929,6 +933,10 @@ export class Es2pandaNativeModule { _SetUpSoPath(soPath: string): void { throw new Error('Not implemented'); } + + _CallExpressionIsTrailingCallConst(context: KNativePointer, node: KNativePointer): boolean { + throw new Error('CallExpressionIsTrailingCallConst was not overloaded by native module initialization'); + } } export function initEs2panda(): Es2pandaNativeModule { diff --git a/koala-wrapper/src/arkts-api/class-by-peer.ts b/koala-wrapper/src/arkts-api/class-by-peer.ts index f8d79996c6031066da0aafe1cb7535c86d6250a6..a12afcba877e8284d8cf61eec1898067b3089889 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 f0a9a91d143027d46b4a4f793c0adf8cdf5b838c..1a0e34eb7329b08350e9cc3e4a08d7807da71ef8 100644 --- a/koala-wrapper/src/arkts-api/index.ts +++ b/koala-wrapper/src/arkts-api/index.ts @@ -68,6 +68,7 @@ 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" diff --git a/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts b/koala-wrapper/src/arkts-api/node-utilities/ArrowFunctionExpression.ts index 9ad9e11afbee51c6175dd54396dc075d8c039cc9..bb155cd3be4dcedfd92ffa0439bd3b566323e6a7 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,11 @@ 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)) { + // console.log(`[NODE CACHE UPDATE] [ArrowFunctionExpression] [ORIGINAL] ptr ${original.peer} node: `, original.dumpSrc()); + // console.log(`[NODE CACHE UPDATE] [ArrowFunctionExpression] [NEW NODE] ptr ${newNode.peer} node: `, newNode.dumpSrc()); + 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 d1489441acb897ed397d6a1d0a89098b216fd891..03dd58971100eb67206d16e1dae0ba65d9f641e3 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,11 @@ 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)) { + // console.log(`[NODE CACHE UPDATE] [CallExpression] [ORIGINAL] ptr ${original.peer} node: `, original.dumpSrc()); + // console.log(`[NODE CACHE UPDATE] [CallExpression] [NEW NODE] ptr ${newNode.peer} node: `, newNode.dumpSrc()); + 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 06450c0cb4c32b74ed30eeb105eb7971ae6ea60d..e600a4ccac008206f8a1b70b966f579b6292e9c3 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,11 @@ 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)) { + // console.log(`[NODE CACHE UPDATE] [ClassProperty] [ORIGINAL] ptr ${original.peer} node: `, original.dumpSrc()); + // console.log(`[NODE CACHE UPDATE] [ClassProperty] [NEW NODE] ptr ${newNode.peer} node: `, newNode.dumpSrc()); + 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 a0e5220406037d90b62668942eecb5663266da94..bdd05a3dcbf7a96ec501e071d70982ba90ca7e42 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,11 @@ 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)) { + // console.log(`[NODE CACHE UPDATE] [ETSFunctionType] [ORIGINAL] ptr ${original.peer} node: `, original.dumpSrc()); + // console.log(`[NODE CACHE UPDATE] [ETSFunctionType] [NEW NODE] ptr ${newNode.peer} node: `, newNode.dumpSrc()); + 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 b9e8f0514f159eab38c00d6168e002a7ca92cea4..cd483c17af8d83cfabc85c2a070e063fa17a13e4 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( @@ -36,8 +37,15 @@ export function updateETSParameterExpression( attachModifiers, (node: ETSParameterExpression, original: ETSParameterExpression) => { node.annotations = original.annotations; + node.type = original.type; return node; - } + }, ); - return update(original, identifier, initializer); + const newNode = update(original, identifier, initializer); + if (NodeCache.getInstance().has(original)) { + // console.log(`[NODE CACHE UPDATE] [ETSParameterExpression] [ORIGINAL] ptr ${original.peer} node: `, original.dumpSrc()); + // console.log(`[NODE CACHE UPDATE] [ETSParameterExpression] [NEW NODE] ptr ${newNode.peer} node: `, newNode.dumpSrc()); + 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 c8dc20720dde2cb9b61918cbb90cabe459efa0a8..c4bc75dae2cda749efaa1a006100ab9064274b72 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,11 @@ 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)) { + // console.log(`[NODE CACHE UPDATE] [ETSUnionType] [ORIGINAL] ptr ${original.peer} node: `, original.dumpSrc()); + // console.log(`[NODE CACHE UPDATE] [ETSUnionType] [NEW NODE] ptr ${newNode.peer} node: `, newNode.dumpSrc()); + 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 f31d25fc675f6c4a08c904785c28b83017638deb..586d1a0e4c8f1624f8d1936f6f97763d5f7d246f 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,11 @@ 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)) { + // console.log(`[NODE CACHE UPDATE] [Identifier] [ORIGINAL] ptr ${original.peer} node: `, original.dumpSrc()); + // console.log(`[NODE CACHE UPDATE] [Identifier] [NEW NODE] ptr ${newNode.peer} node: `, newNode.dumpSrc()); + 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 42006a30098d4b5fbba20cf9f5e9c926afa36236..d1363f87ca7308f19b48336fd9f0bfe1f944cbde 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,11 @@ 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)) { + // console.log(`[NODE CACHE UPDATE] [MethodDefinition] [ORIGINAL] ptr ${original.peer} node: `, original.dumpSrc()); + // console.log(`[NODE CACHE UPDATE] [MethodDefinition] [NEW NODE] ptr ${newNode.peer} node: `, newNode.dumpSrc()); + 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 9fc05a3715761588fe7d4faca9a006539d1bd2db..fc942c24f9feaf528678af56a149361eb6ac94ec 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,11 @@ 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)) { + // console.log(`[NODE CACHE UPDATE] [ReturnStatement] [ORIGINAL] ptr ${original.peer} node: `, original.dumpSrc()); + // console.log(`[NODE CACHE UPDATE] [ReturnStatement] [NEW NODE] ptr ${newNode.peer} node: `, newNode.dumpSrc()); + 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 745a5398c7425ecc77cd306a047cca69caa362d7..6af60d3049bee6a6fb03f0561b0e670487d422d7 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,11 @@ 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)) { + // console.log(`[NODE CACHE UPDATE] [ScriptFunction] [ORIGINAL] ptr ${original.peer} node: `, original.dumpSrc()); + // console.log(`[NODE CACHE UPDATE] [ScriptFunction] [NEW NODE] ptr ${newNode.peer} node: `, newNode.dumpSrc()); + 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 3531ee3a95fbd0171fc0afcab621111bb21b58f9..6889495eb0d90151fc791c21ed25fe0d8787b08e 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,11 @@ 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)) { + // console.log(`[NODE CACHE UPDATE] [TSTypeAliasDeclaration] [ORIGINAL] ptr ${original.peer} node: `, original.dumpSrc()); + // console.log(`[NODE CACHE UPDATE] [TSTypeAliasDeclaration] [NEW NODE] ptr ${newNode.peer} node: `, newNode.dumpSrc()); + NodeCache.getInstance().refresh(original, newNode); + } + return newNode; } diff --git a/koala-wrapper/src/arkts-api/peers/Context.ts b/koala-wrapper/src/arkts-api/peers/Context.ts index b4bf7a203faa00c7eb776d1dfc428ff0d54a923d..60c923698a8632c2827d8eaa756e80648eff8927 100644 --- a/koala-wrapper/src/arkts-api/peers/Context.ts +++ b/koala-wrapper/src/arkts-api/peers/Context.ts @@ -16,8 +16,8 @@ import { ArktsObject } from './ArktsObject'; import { global } from '../static/global'; import { throwError, filterSource } from '../../utils'; -import { passString } from '../utilities/private'; -import { KBoolean, KNativePointer } from '@koalaui/interop'; +import { passString, passStringArray } from '../utilities/private'; +import { KBoolean, KInt, KNativePointer } from '@koalaui/interop'; import { AstNode } from './AstNode'; import { Program } from './Program'; export class Context extends ArktsObject { @@ -45,6 +45,16 @@ export class Context extends ArktsObject { ); } + static createContextGenerateAbcForExternalSourceFiles(filenames: string[]): Context { + if (!global.configIsInitialized()) { + throwError(`Config not initialized`); + } + return new Context( + global.es2panda._CreateContextGenerateAbcForExternalSourceFiles(global.config, filenames.length, passStringArray(filenames)) + ); + } + + static destroyAndRecreate(ast: AstNode): Context { console.log('[TS WRAPPER] DESTROY AND RECREATE'); const source = filterSource(ast.dumpSrc()); diff --git a/koala-wrapper/src/arkts-api/types.ts b/koala-wrapper/src/arkts-api/types.ts index be1c316516d4398d11e7ace0bdc4be48dbdfd972..7666ec71185b9c71f1471694dafc891c45339265 100644 --- a/koala-wrapper/src/arkts-api/types.ts +++ b/koala-wrapper/src/arkts-api/types.ts @@ -241,6 +241,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 0000000000000000000000000000000000000000..3c2215e8093e61999edfb9a745959f32366e51c4 --- /dev/null +++ b/koala-wrapper/src/arkts-api/utilities/nodeCache.ts @@ -0,0 +1,81 @@ +/* + * 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; +} + +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): void { + const peer = node.originalPeer; + const type = global.generatedEs2panda._AstNodeTypeConst(global.context, node.peer); + this.cacheMap.set(peer, { peer, type }); + this._isCollected = true; + } + + refresh(original: AstNode, node: AstNode): void { + if (!this.has(original)) { + this.cacheMap.delete(original.originalPeer); + } + this.collect(node); + } + + isCollected(): boolean { + return this._isCollected; + } + + has(node: AstNode): boolean { + return this.cacheMap.has(node.originalPeer); + } + + get(node: AstNode): AstNodeCacheValue | undefined { + return this.cacheMap.get(node.originalPeer); + } + + clear(): void { + this.cacheMap.clear(); + this._isCollected = false; + } + + visualize(): void { + Array.from(this.cacheMap.values()).forEach(({ peer, type }) => { + const node = nodeByType.get(type) ?? UnsupportedNode; + const newNode = getOrPut(peer, (peer) => new node(peer)) as AstNode; + console.log(`[NODE CACHE] ptr ${peer} node: `, newNode.dumpSrc()); + }); + } +} diff --git a/koala-wrapper/src/arkts-api/utilities/public.ts b/koala-wrapper/src/arkts-api/utilities/public.ts index d7e2dfc3353ad0cd4acf5f2d4ea5013c493a8653..879e95766a82e1fb6f753c1ad9cab02489b1ce8a 100644 --- a/koala-wrapper/src/arkts-api/utilities/public.ts +++ b/koala-wrapper/src/arkts-api/utilities/public.ts @@ -15,7 +15,7 @@ import { global } from '../static/global'; import { isNumber, throwError, getEnumName } from '../../utils'; -import { KNativePointer, KInt, nullptr, withStringResult } from '@koalaui/interop'; +import { KNativePointer, KInt, nullptr, withStringResult, KStringArrayPtr } from '@koalaui/interop' import { passNode, passString, passStringArray, unpackNodeArray, unpackNonNullableNode } from './private'; import { isFunctionDeclaration, isMemberExpression, isMethodDefinition, isNumberLiteral } from '../factory/nodeTests'; import { @@ -42,6 +42,7 @@ import { Program } from '../peers/Program'; import { clearNodeCache } from '../class-by-peer'; import { SourcePosition } from '../peers/SourcePosition'; import { MemberExpression } from '../to-be-generated/MemberExpression'; +import { KNativePointerArray } from 'src/generated/Es2pandaNativeModule'; export function proceedToState(state: Es2pandaContextState, context: KNativePointer, forceDtsEmit = false): void { console.log('[TS WRAPPER] PROCEED TO STATE: ', getEnumName(Es2pandaContextState, state)); @@ -317,3 +318,12 @@ export function insertGlobalStructInfo(structName: string): void { export function hasGlobalStructInfo(structName: string): boolean { return global.es2panda._HasGlobalStructInfo(global.context, passString(structName)); } + +export function createContextGenerateAbcForExternalSourceFiles( + fileCount: KInt, + filenames: string[] +): KNativePointer { + console.log("1232131") + console.log("filenames", filenames); + return global.es2panda._CreateContextGenerateAbcForExternalSourceFiles(global.config, fileCount, passStringArray(filenames)); +}