From 394516d02fddf89fd9e354acb4bcfbc7f20d28ba Mon Sep 17 00:00:00 2001 From: txzhang94 Date: Mon, 7 Jul 2025 22:56:03 +0800 Subject: [PATCH 1/2] Builder/WrappedBuilder Signed-off-by: txzhang94 Change-Id: I379916c91ed9984c2359f1df1d6b947b1b6c7a5e --- .../test/demo/interop/builder_interop.ets | 82 +++++ .../builder-lambda-translators/utils.ts | 17 +- .../ui-plugins/checked-transformer.ts | 94 +++++- .../ui-plugins/interop/builder-interop.ts | 284 ++++++++++++++++++ arkui-plugins/ui-plugins/interop/interop.ts | 82 +---- .../ui-plugins/interop/legacy-transformer.ts | 53 ++++ .../ui-plugins/interop/predefines.ts | 13 + arkui-plugins/ui-plugins/interop/utils.ts | 90 +++++- koala-wrapper/src/arkts-api/index.ts | 2 + 9 files changed, 629 insertions(+), 88 deletions(-) create mode 100644 arkui-plugins/test/demo/interop/builder_interop.ets create mode 100644 arkui-plugins/ui-plugins/interop/builder-interop.ts diff --git a/arkui-plugins/test/demo/interop/builder_interop.ets b/arkui-plugins/test/demo/interop/builder_interop.ets new file mode 100644 index 000000000..633312297 --- /dev/null +++ b/arkui-plugins/test/demo/interop/builder_interop.ets @@ -0,0 +1,82 @@ +/* + * 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. + */ + + +// ArkTS1.2 +'use static' +import { Entry, Text, Column, Component, Button, ClickEvent } from '@ohos.arkui.component' +import { State } from '@ohos.arkui.stateManagement' +import hilog from '@ohos.hilog' +import { demoBuilder1_1 } from 'har1' + +@Entry +@Component +struct MyStateSample { + @State stateVar: string = 'state var'; + message: string = 'var'; + + build() { + Column(undefined) { + Text('Hello World').fontSize(20) + Button(this.message).backgroundColor('#FFFF00FF') + .onClick((e: ClickEvent) => { + hilog.info(0x0000, 'testTag', 'On Click'); + }) + Text(this.stateVar).fontSize(20) + demoBuilder1_1({ a:"a23", b:this.stateVar}) + } + } +} + + +//ArkT1.1 +@Builder +export function demoBuilder1_1( param1:aa) { + +} +export class aa { + a:string = '' + b:string = '' +} + + +//transform 1.1 Builder to compatibleComponent + +compatibleComponent((() => { + let global = ESValue.getGlobal(); + let viewStackProcessor = global.getProperty("ViewStackProcessor"); + let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + let elmtId = createId.invoke(); + let createCompatibleNode = global.getProperty("createCompatibleNodeWithFunc"); + let paramObject0 = ESValue.instantiateEmptyObject(); + paramObject0.setProperty("a", ESValue.wrap("a23")); + paramObject0.setProperty("b", ESValue.wrap(this.stateVar)); + let component = createCompatibleNode.invoke(ESValue.wrap(demoBuilder1_1), elmtId, paramObject0); + let builderViewClass = global.getProperty("getViewV2Class"); + let builderV2 = builderViewClass.invoke(); + let createFn = builderV2.getProperty("create"); + createFn.invoke(component); + return { + component: component, + name: "demoBuilder1_1", + }; +}), ((instance: ESValue) => { + let param = instance.getProperty("arg1"); + param.setProperty("a", ESValue.wrap("a23")); + param.setProperty("b", ESValue.wrap(this.stateVar)); + let global = ESValue.getGlobal(); + let afterUpdateProperty = global.getProperty("afterUpdateProperty"); + afterUpdateProperty.invoke(); +})); diff --git a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts index ab738bc3d..48405da31 100644 --- a/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts +++ b/arkui-plugins/ui-plugins/builder-lambda-translators/utils.ts @@ -84,8 +84,8 @@ export function builderLambdaArgumentName(annotation: arkts.AnnotationUsage): st return property.value.str; } -export function isBuilderLambda(node: arkts.AstNode): boolean { - const builderLambdaCall: arkts.AstNode | undefined = getDeclForBuilderLambda(node); +export function isBuilderLambda(node: arkts.AstNode, nodeDecl?:arkts.AstNode | undefined): boolean { + const builderLambdaCall: arkts.AstNode | undefined = getDeclForBuilderLambda(node, nodeDecl); if (!builderLambdaCall) { return arkts.isCallExpression(node) && node.arguments.length > 0 && isBuilderLambda(node.arguments[0]); } @@ -176,7 +176,7 @@ export function getDeclForBuilderLambdaMethodDecl(node: arkts.AstNode): arkts.As return undefined; } -export function getDeclForBuilderLambda(node: arkts.AstNode): arkts.AstNode | undefined { +export function getDeclForBuilderLambda(node: arkts.AstNode, nodeDecl?:arkts.AstNode | undefined): arkts.AstNode | undefined { if (!node || !arkts.isCallExpression(node)) { return undefined; } @@ -201,15 +201,18 @@ export function getDeclForBuilderLambda(node: arkts.AstNode): arkts.AstNode | un currNode = _node.object; } - if (isBuilderLambdaCall(node)) { + if (isBuilderLambdaCall(node, nodeDecl)) { return node; } return undefined; } -export function isBuilderLambdaCall(node: arkts.CallExpression | arkts.Identifier): boolean { - const expr = arkts.isIdentifier(node) ? node : node.expression; - const decl = arkts.getDecl(expr); +export function isBuilderLambdaCall(node: arkts.CallExpression | arkts.Identifier, nodeDecl?:arkts.AstNode | undefined): boolean { + let decl = nodeDecl; + if (decl === undefined) { + const expr = arkts.isIdentifier(node) ? node : node.expression; + decl = arkts.getDecl(expr); + } if (!decl) { return false; diff --git a/arkui-plugins/ui-plugins/checked-transformer.ts b/arkui-plugins/ui-plugins/checked-transformer.ts index d5efa568a..b2cb2ab70 100644 --- a/arkui-plugins/ui-plugins/checked-transformer.ts +++ b/arkui-plugins/ui-plugins/checked-transformer.ts @@ -42,9 +42,14 @@ import { isSpecificNewClass, } from './utils'; import { findAndCollectMemoableNode } from '../collectors/memo-collectors/factory'; +import { InteroperAbilityNames } from './interop/predefines'; +import { generateBuilderCompatible } from './interop/builder-interop'; export class CheckedTransformer extends AbstractVisitor { private scope: ScopeInfoCollection; + private legacyBuilderSet: Set = new Set(); + private needCompatibleComponent : boolean = false; + private componentSet = new Set(); projectConfig: ProjectConfig | undefined; aceBuildJson: LoaderJson; resourceInfo: ResourceInfo; @@ -55,11 +60,32 @@ export class CheckedTransformer extends AbstractVisitor { this.scope = { customComponents: [] }; this.aceBuildJson = loadBuildJson(this.projectConfig); this.resourceInfo = initResourceInfo(this.projectConfig, this.aceBuildJson); + this.legacyBuilderSet = new Set(); + this.initBuilderMap(); + } + + initBuilderMap(): void { + const moduleList = this.projectConfig?.dependentModuleList; + if (moduleList === undefined) { + return; + } + for (const module of moduleList) { + const language = module.language; + const moduleName = module.moduleName; + if (language !== InteroperAbilityNames.ARKTS_1_1) { + continue; + } + if (!this.legacyBuilderSet.has(moduleName)) { + this.legacyBuilderSet.add(moduleName); + } + } } reset(): void { super.reset(); this.scope = { customComponents: [] }; + this.needCompatibleComponent = false; + this.componentSet = new Set(); PropertyCache.getInstance().reset(); ImportCollector.getInstance().reset(); DeclarationCollector.getInstance().reset(); @@ -92,11 +118,59 @@ export class CheckedTransformer extends AbstractVisitor { } } + isFromBuilder1_1(decl: arkts.AstNode | undefined): boolean { + if (!decl || this.legacyBuilderSet.size === 0) { + return false; + } + const moduleName = arkts.getProgramFromAstNode(decl).moduleName?.split('/')[0]; + + if (!this.legacyBuilderSet.has(moduleName)) { + return false; + } + + let isFrom1_1 = false; + if (arkts.isMethodDefinition(decl)) { + const annotations = decl.scriptFunction.annotations; + const decorators: string[] = annotations.map(annotation => { + return (annotation.expr as arkts.Identifier).name; + }); + decorators.forEach(element => { + if (element === 'Builder') { + isFrom1_1 = true; + return; + } + }); + } + return isFrom1_1; + } + + addComponentSet(node: arkts.ImportDeclaration): void { + node.specifiers.forEach(specifier => { + if (arkts.isImportSpecifier(specifier) && specifier.imported?.name) { + this.componentSet.add(specifier.imported?.name); + } + }); + } + + addcompatibleComponentImport(node: arkts.EtsScript): void { + if (this.needCompatibleComponent && !this.componentSet.has('compatibleComponent')) { + ImportCollector.getInstance().collectSource('compatibleComponent', 'arkui.component.interop'); + ImportCollector.getInstance().collectImport('compatibleComponent'); + } + } + visitor(beforeChildren: arkts.AstNode): arkts.AstNode { this.enter(beforeChildren); - if (arkts.isCallExpression(beforeChildren) && isBuilderLambda(beforeChildren)) { - const lambda = builderLambdaFactory.transformBuilderLambda(beforeChildren); - return this.visitEachChild(lambda); + if (arkts.isCallExpression(beforeChildren)) { + const decl = arkts.getDecl(beforeChildren.expression); + if (arkts.isIdentifier(beforeChildren.expression) && this.isFromBuilder1_1(decl)) { + // Builder + this.needCompatibleComponent = true; + return generateBuilderCompatible(beforeChildren, beforeChildren.expression.name); + } else if (isBuilderLambda(beforeChildren, decl)) { + const lambda = builderLambdaFactory.transformBuilderLambda(beforeChildren); + return this.visitEachChild(lambda); + } } else if (arkts.isMethodDefinition(beforeChildren) && isBuilderLambdaMethodDecl(beforeChildren)) { const lambda = builderLambdaFactory.transformBuilderLambdaMethodDecl( beforeChildren, @@ -128,11 +202,17 @@ export class CheckedTransformer extends AbstractVisitor { return structFactory.tranformInterfaceMembers(node, this.externalSourceName); } else if (arkts.isETSNewClassInstanceExpression(node) && isSpecificNewClass(node, CustomDialogNames.CUSTOM_DIALOG_CONTROLLER)) { return structFactory.transformCustomDialogController(node); + } else if (arkts.isImportDeclaration(node) && + (node.source?.str === '@ohos.arkui.component' || node.source?.str === 'arkui.component.interop')) { + this.addComponentSet(node); } - if (arkts.isEtsScript(node) && ImportCollector.getInstance().importInfos.length > 0) { - ImportCollector.getInstance().insertCurrentImports(this.program); - LogCollector.getInstance().shouldIgnoreError(this.projectConfig?.ignoreError); - LogCollector.getInstance().emitLogInfo(); + if (arkts.isEtsScript(node)) { + this.addcompatibleComponentImport(node); + if (ImportCollector.getInstance().importInfos.length > 0) { + ImportCollector.getInstance().insertCurrentImports(this.program); + LogCollector.getInstance().shouldIgnoreError(this.projectConfig?.ignoreError); + LogCollector.getInstance().emitLogInfo(); + } } return node; } diff --git a/arkui-plugins/ui-plugins/interop/builder-interop.ts b/arkui-plugins/ui-plugins/interop/builder-interop.ts new file mode 100644 index 000000000..cc12832e2 --- /dev/null +++ b/arkui-plugins/ui-plugins/interop/builder-interop.ts @@ -0,0 +1,284 @@ +/* + * 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 { ESValueMethodNames, builderMethodNames, InteroperAbilityNames, buildAbilityName } from './predefines'; +import { + createELMTID, + createEmptyESValue, + createGlobal, + createInitReturn, + getPropertyESValue, + getWrapValue, + setPropertyESValue +} from './utils'; + +interface builderParam { + args: arkts.AstNode[], + paramsInfo: arkts.Statement[] +} + +function invokeFunctionWithParam(functionName: string, result: string, className: string, args: arkts.AstNode[]): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(result), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(functionName), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + getWrapValue(arkts.factory.createIdentifier(className)), + arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), + ...args + ] + ) + )] + ); +} + +function invokeFunctionWithResult(functionName: string, result: string): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(result), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(functionName), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + )] + ); +} + +function invokeCreateViewV2(functionName: string, param: string): arkts.Statement { + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(functionName), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + [ + arkts.factory.createIdentifier(param) + ] + ) + ); +} + +function invokeAfterUpdateProperty(): arkts.Statement { + return arkts.factory.createExpressionStatement( + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(builderMethodNames.AFTERUPDATEPROPERTY), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + ); +} + +function createBuilderInitializer(className: string, functionName: string, param: builderParam): arkts.ArrowFunctionExpression { + const block = arkts.factory.createBlock( + [ + createGlobal(), + ...createELMTID(), + getPropertyESValue(builderMethodNames.CREATECOMPATIBLENODE, InteroperAbilityNames.GLOBAL, functionName), + ...param.paramsInfo, + invokeFunctionWithParam(builderMethodNames.CREATECOMPATIBLENODE, InteroperAbilityNames.COMPONENT, className, param.args), + getPropertyESValue(buildAbilityName.BUILDERVIEWCLASS, InteroperAbilityNames.GLOBAL, buildAbilityName.GETVIEWV2CLASS), + invokeFunctionWithResult(buildAbilityName.BUILDERVIEWCLASS, buildAbilityName.BUILDERV2), + getPropertyESValue(builderMethodNames.CREATEFN, buildAbilityName.BUILDERV2, buildAbilityName.CREATE), + invokeCreateViewV2(builderMethodNames.CREATEFN, InteroperAbilityNames.COMPONENT), + createInitReturn(className) + ] + ); + return arkts.factory.createArrowFunction( + arkts.factory.createScriptFunction( + block, + arkts.factory.createFunctionSignature( + undefined, + [], + undefined, + false, + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + ) + ); +} + +function getUpdateArgs(node: arkts.CallExpression): arkts.Statement[] { + let body: arkts.Statement[] = []; + if (node.arguments.length !== 1) { + return []; + } + node.arguments.forEach((argument) => { + if (arkts.isObjectExpression(argument)) { + body?.push(getPropertyESValue('param', InteroperAbilityNames.INSTANCE, 'arg1')); + for (const property of argument.properties) { + if (!(property instanceof arkts.Property)) { + continue; + } + const key = property.key; + const value = property.value; + if (!(key instanceof arkts.Identifier) || value === undefined) { + throw Error('Error arguments in Legacy Builder Function'); + } + body?.push(setPropertyESValue('param', key.name, getWrapValue(value))); + } + const endBody = + [ + createGlobal(), + getPropertyESValue(builderMethodNames.AFTERUPDATEPROPERTY, + InteroperAbilityNames.GLOBAL, + builderMethodNames.AFTERUPDATEPROPERTY), + invokeAfterUpdateProperty() + ]; + body?.push(...endBody); + } else { + body = []; + return; + } + }); + return body; +} + +function createBuilderUpdate(node: arkts.CallExpression): arkts.ArrowFunctionExpression { + return arkts.factory.createArrowFunction( + arkts.factory.createScriptFunction( + arkts.factory.createBlock( + [ + ...getUpdateArgs(node) + ] + ), + arkts.factory.createFunctionSignature( + undefined, + [ + arkts.factory.createParameterDeclaration( + arkts.factory.createIdentifier(InteroperAbilityNames.INSTANCE, + arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE) + ) + ) + ), + undefined, + ), + ], + undefined, + false, + ), + arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + ) + ); +} + +function getInitArgs(node: arkts.CallExpression): builderParam { + const args: arkts.AstNode[] = []; + let ObjectExpressionNum: number = 0; + const body: arkts.Statement[] = []; + node.arguments.forEach((argument) => { + if (arkts.isObjectExpression(argument)) { + const paramName: string = 'paramObject' + ObjectExpressionNum; + ObjectExpressionNum++; + body.push(createEmptyESValue(paramName)); + for (const property of argument.properties) { + if (!(property instanceof arkts.Property)) { + continue; + } + const key = property.key; + const value = property.value; + if (!(key instanceof arkts.Identifier) || value === undefined) { + throw Error('Error arguments in Legacy Builder Function'); + } + body.push(setPropertyESValue(paramName, key.name, getWrapValue(value))); + } + args.push(arkts.factory.createIdentifier(paramName)); + } else { + args.push(getWrapValue(argument)); + } + }); + return { args: args, paramsInfo: body }; +} + +/** + * + * @param node node + * @param moduleName moduleName + * @returns After Checked, transform builder/WrappedBuilder -> compatibleComponent + */ +export function generateBuilderCompatible(node: arkts.CallExpression, moduleName: string): arkts.CallExpression { + let functionName; + switch (node.arguments.length) { + case 0: + functionName = 'createCompatibleNodeWithFuncVoid'; + break; + case 1: + functionName = 'createCompatibleNodeWithFunc'; + break; + case 2: + functionName = 'createCompatibleNodeWithFunc2'; + break; + case 3: + functionName = 'createCompatibleNodeWithFunc3'; + break; + case 4: + functionName = 'createCompatibleNodeWithFunc4'; + break; + case 5: + functionName = 'createCompatibleNodeWithFunc5'; + break; + default: + throw Error('Error arguments in Legacy Builder Function'); + } + let param: builderParam = getInitArgs(node); + const initializer = createBuilderInitializer(moduleName, functionName, param); + const updater: arkts.ArrowFunctionExpression = createBuilderUpdate(node); + const result = arkts.factory.updateCallExpression( + node, + arkts.factory.createIdentifier(InteroperAbilityNames.ARKUICOMPATIBLE), + undefined, + [ + initializer, + updater, + ] + ); + arkts.NodeCache.getInstance().collect(result); + return result; +} \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/interop.ts b/arkui-plugins/ui-plugins/interop/interop.ts index bfbceccad..433265a80 100644 --- a/arkui-plugins/ui-plugins/interop/interop.ts +++ b/arkui-plugins/ui-plugins/interop/interop.ts @@ -21,7 +21,15 @@ import { getCustomComponentOptionsName } from '../utils'; import { InteropContext } from '../component-transformer'; import { createVariableLet, initialArgs} from './initstatevar'; import { createProvideInterop, setAndResetFindProvide } from './provide'; -import { getPropertyESValue, getWrapValue, setPropertyESValue, createEmptyESValue } from './utils'; +import { + getPropertyESValue, + getWrapValue, + setPropertyESValue, + createEmptyESValue, + createGlobal, + createELMTID, + createInitReturn +} from './utils'; import { ImportCollector } from '../../common/import-collector'; @@ -66,25 +74,6 @@ function paramsLambdaDeclaration(name: string, args?: arkts.ObjectExpression): a return result; } -function createInitReturn(componentName: string): arkts.ReturnStatement { - return arkts.factory.createReturnStatement( - arkts.ObjectExpression.createObjectExpression( - arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, - [ - arkts.Property.createProperty( - arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT), - arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) - ), - arkts.Property.createProperty( - arkts.factory.createIdentifier('name'), - arkts.factory.createStringLiteral(componentName) - ) - ], - false - ), - ); -} - function createExtraInfo(properties: string[], value: string[]): arkts.Statement[] { const body: arkts.AstNode[] = []; body.push(createEmptyESValue(InteroperAbilityNames.EXTRAINFO)); @@ -99,59 +88,6 @@ function createExtraInfo(properties: string[], value: string[]): arkts.Statement return body; } - -function createGlobal(): arkts.Statement { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(InteroperAbilityNames.GLOBAL), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE), - arkts.factory.createIdentifier('getGlobal'), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - undefined - ) - )] - ); -} - -function createELMTID(): arkts.Statement[] { - const body: arkts.Statement[] = []; - const viewStackProcessor = getPropertyESValue('viewStackProcessor', InteroperAbilityNames.GLOBAL, 'ViewStackProcessor'); - body.push(viewStackProcessor); - const createId = getPropertyESValue('createId', 'viewStackProcessor', 'AllocateNewElmetIdForNextComponent'); - body.push(createId); - const elmtId = arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier('createId'), - arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - undefined - ) - )] - ); - body.push(elmtId); - return body; -} - - function generateTSASExpression(expression: arkts.AstNode): arkts.Expression { return arkts.factory.createTSAsExpression( arkts.factory.createCallExpression( diff --git a/arkui-plugins/ui-plugins/interop/legacy-transformer.ts b/arkui-plugins/ui-plugins/interop/legacy-transformer.ts index 14a7a4b95..777a50e60 100644 --- a/arkui-plugins/ui-plugins/interop/legacy-transformer.ts +++ b/arkui-plugins/ui-plugins/interop/legacy-transformer.ts @@ -259,6 +259,56 @@ export class LegacyTransformer extends AbstractVisitor { } } + handleWrappedBuilderNode(node: arkts.ETSTypeReference): arkts.ETSTypeReference { + if (node.part && arkts.isETSTypeReferencePart(node.part) && node.part.name + && arkts.isIdentifier(node.part.name) && node.part.name.name === 'WrappedBuilder') { + return arkts.factory.createTypeReference( + arkts.factory.createTypeReferencePart( + arkts.factory.createIdentifier('Any') + ) + ); + } + return node; + } + + // handle WrappedBuilder + handleWrappedBuilder(node: arkts.VariableDeclarator): arkts.VariableDeclarator { + if (arkts.isIdentifier(node.name) && node.name.typeAnnotation) { + let typeAnnotation = node.name.typeAnnotation; + // WrappedBuilder<[aa]>[] => Any[] + if (arkts.isTSArrayType(typeAnnotation) && typeAnnotation.elementType && + arkts.isETSTypeReference(typeAnnotation.elementType)) { + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + arkts.factory.updateIdentifier( + node.name, + node.name.name, + arkts.TSArrayType.updateTSArrayType( + typeAnnotation, + this.handleWrappedBuilderNode(typeAnnotation.elementType) + ) + ), + node.initializer + ); + } + // WrappedBuilder<[aa]> => Any + if (arkts.isETSTypeReference(typeAnnotation)) { + return arkts.factory.updateVariableDeclarator( + node, + node.flag, + arkts.factory.updateIdentifier( + node.name, + node.name.name, + this.handleWrappedBuilderNode(typeAnnotation) + ), + node.initializer + ); + } + } + return node; + } + visitor(node: arkts.AstNode): arkts.AstNode { this.enter(node); const newNode = this.visitEachChild(node); @@ -285,6 +335,9 @@ export class LegacyTransformer extends AbstractVisitor { return updateNode; } } + if (arkts.isVariableDeclarator(newNode)) { + return this.handleWrappedBuilder(newNode); + } return newNode; } } \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/predefines.ts b/arkui-plugins/ui-plugins/interop/predefines.ts index 6762902a0..4bfadb51c 100644 --- a/arkui-plugins/ui-plugins/interop/predefines.ts +++ b/arkui-plugins/ui-plugins/interop/predefines.ts @@ -60,4 +60,17 @@ export enum InteropProvideNames { SETFINDPROVIDE = 'setFindProvideInterop', SETVIEWPUFINDPROVIDE = 'setViewPUFindProvideInterop', FINDPROVIDECALLBACK = 'findProvideInterop', +} + +export enum builderMethodNames { + AFTERUPDATEPROPERTY = 'afterUpdateProperty', + CREATECOMPATIBLENODE = 'createCompatibleNode', + CREATEFN = 'createFn', +} + +export enum buildAbilityName { + BUILDERVIEWCLASS = 'builderViewClass', + BUILDERV2 = 'builderV2', + CREATE = 'create', + GETVIEWV2CLASS = 'getViewV2Class', } \ No newline at end of file diff --git a/arkui-plugins/ui-plugins/interop/utils.ts b/arkui-plugins/ui-plugins/interop/utils.ts index 14574a264..8ce38fe30 100644 --- a/arkui-plugins/ui-plugins/interop/utils.ts +++ b/arkui-plugins/ui-plugins/interop/utils.ts @@ -16,7 +16,7 @@ import * as arkts from '@koalaui/libarkts'; -import { ESValueMethodNames } from './predefines'; +import { ESValueMethodNames, InteroperAbilityNames } from './predefines'; /** @@ -133,3 +133,91 @@ export function stateProxy(stateVarName: string): string { return `__Proxy_${stateVarName}`; } +/** + * get elmtId + * @returns + * let viewStackProcessor = global.getProperty("ViewStackProcessor"); + * let createId = viewStackProcessor.getProperty("AllocateNewElmetIdForNextComponent"); + * let elmtId = createId.invoke(); + */ +export function createELMTID(): arkts.Statement[] { + const body: arkts.Statement[] = []; + const viewStackProcessor = getPropertyESValue('viewStackProcessor', InteroperAbilityNames.GLOBAL, 'ViewStackProcessor'); + body.push(viewStackProcessor); + const createId = getPropertyESValue('createId', 'viewStackProcessor', 'AllocateNewElmetIdForNextComponent'); + body.push(createId); + const elmtId = arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier('createId'), + arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + )] + ); + body.push(elmtId); + return body; +} + +/** + * + * @param componentName + * @returns return { + * component: component, + * name: componentName, + * }; + */ +export function createInitReturn(componentName: string): arkts.ReturnStatement { + return arkts.factory.createReturnStatement( + arkts.ObjectExpression.createObjectExpression( + arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, + [ + arkts.Property.createProperty( + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT), + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) + ), + arkts.Property.createProperty( + arkts.factory.createIdentifier('name'), + arkts.factory.createStringLiteral(componentName) + ) + ], + false + ), + ); +} + +/** + * createGlobal + * @returns let global = ESValue.getGlobal(); + */ +export function createGlobal(): arkts.Statement { + return arkts.factory.createVariableDeclaration( + arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, + arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, + [arkts.factory.createVariableDeclarator( + arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, + arkts.factory.createIdentifier(InteroperAbilityNames.GLOBAL), + arkts.factory.createCallExpression( + arkts.factory.createMemberExpression( + arkts.factory.createIdentifier(ESValueMethodNames.ESVALUE), + arkts.factory.createIdentifier('getGlobal'), + arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, + false, + false + ), + undefined, + undefined + ) + )] + ); +} diff --git a/koala-wrapper/src/arkts-api/index.ts b/koala-wrapper/src/arkts-api/index.ts index eef8304a5..b192306d6 100644 --- a/koala-wrapper/src/arkts-api/index.ts +++ b/koala-wrapper/src/arkts-api/index.ts @@ -63,6 +63,8 @@ export * from '../generated/peers/TSArrayType'; export * from '../generated/peers/ArrayExpression'; export * from '../generated/peers/TryStatement'; export * from '../generated/peers/ETSNullType'; +export * from '../generated/peers/ETSTuple'; +export * from '../generated/peers/ImportDeclaration' export * from './types'; export * from './utilities/private'; -- Gitee From b1860cc749319129129bee39c678c2db139f207e Mon Sep 17 00:00:00 2001 From: txzhang94 Date: Thu, 10 Jul 2025 23:31:57 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=95=B4=E7=90=86builder=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: txzhang94 Change-Id: I714a8dc4d9e1eeb85d439bdb3e3217bcbb541b86 --- .../test/demo/interop/builder_interop.ets | 6 +- .../ui-plugins/checked-transformer.ts | 24 ++-- .../ui-plugins/interop/builder-interop.ts | 135 ++++++++---------- .../ui-plugins/interop/legacy-transformer.ts | 4 +- .../ui-plugins/interop/predefines.ts | 8 -- 5 files changed, 70 insertions(+), 107 deletions(-) diff --git a/arkui-plugins/test/demo/interop/builder_interop.ets b/arkui-plugins/test/demo/interop/builder_interop.ets index 633312297..a5b3fafb0 100644 --- a/arkui-plugins/test/demo/interop/builder_interop.ets +++ b/arkui-plugins/test/demo/interop/builder_interop.ets @@ -64,10 +64,8 @@ compatibleComponent((() => { paramObject0.setProperty("a", ESValue.wrap("a23")); paramObject0.setProperty("b", ESValue.wrap(this.stateVar)); let component = createCompatibleNode.invoke(ESValue.wrap(demoBuilder1_1), elmtId, paramObject0); - let builderViewClass = global.getProperty("getViewV2Class"); - let builderV2 = builderViewClass.invoke(); - let createFn = builderV2.getProperty("create"); - createFn.invoke(component); + let viewPUCreate = global.getProperty("viewPUCreate"); + viewPUCreate.invoke(component); return { component: component, name: "demoBuilder1_1", diff --git a/arkui-plugins/ui-plugins/checked-transformer.ts b/arkui-plugins/ui-plugins/checked-transformer.ts index b2cb2ab70..81d3df185 100644 --- a/arkui-plugins/ui-plugins/checked-transformer.ts +++ b/arkui-plugins/ui-plugins/checked-transformer.ts @@ -49,7 +49,6 @@ export class CheckedTransformer extends AbstractVisitor { private scope: ScopeInfoCollection; private legacyBuilderSet: Set = new Set(); private needCompatibleComponent : boolean = false; - private componentSet = new Set(); projectConfig: ProjectConfig | undefined; aceBuildJson: LoaderJson; resourceInfo: ResourceInfo; @@ -85,7 +84,6 @@ export class CheckedTransformer extends AbstractVisitor { super.reset(); this.scope = { customComponents: [] }; this.needCompatibleComponent = false; - this.componentSet = new Set(); PropertyCache.getInstance().reset(); ImportCollector.getInstance().reset(); DeclarationCollector.getInstance().reset(); @@ -144,16 +142,8 @@ export class CheckedTransformer extends AbstractVisitor { return isFrom1_1; } - addComponentSet(node: arkts.ImportDeclaration): void { - node.specifiers.forEach(specifier => { - if (arkts.isImportSpecifier(specifier) && specifier.imported?.name) { - this.componentSet.add(specifier.imported?.name); - } - }); - } - addcompatibleComponentImport(node: arkts.EtsScript): void { - if (this.needCompatibleComponent && !this.componentSet.has('compatibleComponent')) { + if (this.needCompatibleComponent) { ImportCollector.getInstance().collectSource('compatibleComponent', 'arkui.component.interop'); ImportCollector.getInstance().collectImport('compatibleComponent'); } @@ -189,7 +179,13 @@ export class CheckedTransformer extends AbstractVisitor { const newClass: arkts.ClassDeclaration = structFactory.tranformClassMembers(node, scope); this.exit(beforeChildren); return newClass; - } else if (isEntryWrapperClass(node)) { + } + + return this.visitorAstNode(node); + } + + visitorAstNode(node: arkts.AstNode): arkts.AstNode { + if (isEntryWrapperClass(node)) { entryFactory.addMemoToEntryWrapperClassMethods(node); return node; } else if (arkts.isClassDeclaration(node)) { @@ -202,10 +198,8 @@ export class CheckedTransformer extends AbstractVisitor { return structFactory.tranformInterfaceMembers(node, this.externalSourceName); } else if (arkts.isETSNewClassInstanceExpression(node) && isSpecificNewClass(node, CustomDialogNames.CUSTOM_DIALOG_CONTROLLER)) { return structFactory.transformCustomDialogController(node); - } else if (arkts.isImportDeclaration(node) && - (node.source?.str === '@ohos.arkui.component' || node.source?.str === 'arkui.component.interop')) { - this.addComponentSet(node); } + if (arkts.isEtsScript(node)) { this.addcompatibleComponentImport(node); if (ImportCollector.getInstance().importInfos.length > 0) { diff --git a/arkui-plugins/ui-plugins/interop/builder-interop.ts b/arkui-plugins/ui-plugins/interop/builder-interop.ts index cc12832e2..7d2b63282 100644 --- a/arkui-plugins/ui-plugins/interop/builder-interop.ts +++ b/arkui-plugins/ui-plugins/interop/builder-interop.ts @@ -14,7 +14,7 @@ */ import * as arkts from '@koalaui/libarkts'; -import { ESValueMethodNames, builderMethodNames, InteroperAbilityNames, buildAbilityName } from './predefines'; +import { ESValueMethodNames, builderMethodNames, InteroperAbilityNames } from './predefines'; import { createELMTID, createEmptyESValue, @@ -56,60 +56,40 @@ function invokeFunctionWithParam(functionName: string, result: string, className ); } -function invokeFunctionWithResult(functionName: string, result: string): arkts.Statement { - return arkts.factory.createVariableDeclaration( - arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, - arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, - [arkts.factory.createVariableDeclarator( - arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, - arkts.factory.createIdentifier(result), - arkts.factory.createCallExpression( - arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(functionName), - arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), - arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, - false, - false - ), - undefined, - undefined - ) - )] - ); -} - -function invokeCreateViewV2(functionName: string, param: string): arkts.Statement { +function invokeAfterUpdateProperty(): arkts.Statement { return arkts.factory.createExpressionStatement( arkts.factory.createCallExpression( arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(functionName), + arkts.factory.createIdentifier(builderMethodNames.AFTERUPDATEPROPERTY), arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, false ), undefined, - [ - arkts.factory.createIdentifier(param) - ] + undefined ) ); } -function invokeAfterUpdateProperty(): arkts.Statement { - return arkts.factory.createExpressionStatement( +function invokeComponent(): arkts.Statement[] { + const viewPU = getPropertyESValue('viewPUCreate', InteroperAbilityNames.GLOBAL, 'viewPUCreate'); + const create = arkts.factory.createExpressionStatement( arkts.factory.createCallExpression( arkts.factory.createMemberExpression( - arkts.factory.createIdentifier(builderMethodNames.AFTERUPDATEPROPERTY), - arkts.factory.createIdentifier(ESValueMethodNames.INVOKE), + arkts.factory.createIdentifier('viewPUCreate'), + arkts.factory.createIdentifier('invoke'), arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, false, false ), undefined, - undefined + [ + arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) + ] ) ); + return [viewPU, create]; } function createBuilderInitializer(className: string, functionName: string, param: builderParam): arkts.ArrowFunctionExpression { @@ -120,10 +100,7 @@ function createBuilderInitializer(className: string, functionName: string, param getPropertyESValue(builderMethodNames.CREATECOMPATIBLENODE, InteroperAbilityNames.GLOBAL, functionName), ...param.paramsInfo, invokeFunctionWithParam(builderMethodNames.CREATECOMPATIBLENODE, InteroperAbilityNames.COMPONENT, className, param.args), - getPropertyESValue(buildAbilityName.BUILDERVIEWCLASS, InteroperAbilityNames.GLOBAL, buildAbilityName.GETVIEWV2CLASS), - invokeFunctionWithResult(buildAbilityName.BUILDERVIEWCLASS, buildAbilityName.BUILDERV2), - getPropertyESValue(builderMethodNames.CREATEFN, buildAbilityName.BUILDERV2, buildAbilityName.CREATE), - invokeCreateViewV2(builderMethodNames.CREATEFN, InteroperAbilityNames.COMPONENT), + ...invokeComponent(), createInitReturn(className) ] ); @@ -143,38 +120,35 @@ function createBuilderInitializer(className: string, functionName: string, param } function getUpdateArgs(node: arkts.CallExpression): arkts.Statement[] { - let body: arkts.Statement[] = []; if (node.arguments.length !== 1) { return []; } - node.arguments.forEach((argument) => { - if (arkts.isObjectExpression(argument)) { - body?.push(getPropertyESValue('param', InteroperAbilityNames.INSTANCE, 'arg1')); - for (const property of argument.properties) { - if (!(property instanceof arkts.Property)) { - continue; - } - const key = property.key; - const value = property.value; - if (!(key instanceof arkts.Identifier) || value === undefined) { - throw Error('Error arguments in Legacy Builder Function'); - } - body?.push(setPropertyESValue('param', key.name, getWrapValue(value))); + let body: arkts.Statement[] = []; + let argument = node.arguments[0]; + if (arkts.isObjectExpression(argument)) { + body?.push(getPropertyESValue('param', InteroperAbilityNames.INSTANCE, 'arg1')); + for (const property of argument.properties) { + if (!(property instanceof arkts.Property)) { + continue; } - const endBody = - [ - createGlobal(), - getPropertyESValue(builderMethodNames.AFTERUPDATEPROPERTY, - InteroperAbilityNames.GLOBAL, - builderMethodNames.AFTERUPDATEPROPERTY), - invokeAfterUpdateProperty() - ]; - body?.push(...endBody); - } else { - body = []; - return; + const key = property.key; + const value = property.value; + if (!(key instanceof arkts.Identifier) || value === undefined) { + throw Error('Error arguments in Legacy Builder Function'); + } + body?.push(setPropertyESValue('param', key.name, getWrapValue(value))); } - }); + const endBody = + [ + createGlobal(), + getPropertyESValue(builderMethodNames.AFTERUPDATEPROPERTY, + InteroperAbilityNames.GLOBAL, + builderMethodNames.AFTERUPDATEPROPERTY), + invokeAfterUpdateProperty() + ]; + body?.push(...endBody); + } + return body; } @@ -215,21 +189,8 @@ function getInitArgs(node: arkts.CallExpression): builderParam { const body: arkts.Statement[] = []; node.arguments.forEach((argument) => { if (arkts.isObjectExpression(argument)) { - const paramName: string = 'paramObject' + ObjectExpressionNum; + processArgument(argument, ObjectExpressionNum, body, args); ObjectExpressionNum++; - body.push(createEmptyESValue(paramName)); - for (const property of argument.properties) { - if (!(property instanceof arkts.Property)) { - continue; - } - const key = property.key; - const value = property.value; - if (!(key instanceof arkts.Identifier) || value === undefined) { - throw Error('Error arguments in Legacy Builder Function'); - } - body.push(setPropertyESValue(paramName, key.name, getWrapValue(value))); - } - args.push(arkts.factory.createIdentifier(paramName)); } else { args.push(getWrapValue(argument)); } @@ -237,6 +198,24 @@ function getInitArgs(node: arkts.CallExpression): builderParam { return { args: args, paramsInfo: body }; } +function processArgument(argument: arkts.ObjectExpression, ObjectExpressionNum:number, + body: arkts.Statement[], args: arkts.AstNode[]): void { + const paramName: string = 'paramObject' + ObjectExpressionNum; + body.push(createEmptyESValue(paramName)); + for (const property of argument.properties) { + if (!(property instanceof arkts.Property)) { + continue; + } + const key = property.key; + const value = property.value; + if (!(key instanceof arkts.Identifier) || value === undefined) { + throw Error('Error arguments in Legacy Builder Function'); + } + body.push(setPropertyESValue(paramName, key.name, getWrapValue(value))); + } + args.push(arkts.factory.createIdentifier(paramName)); +} + /** * * @param node node diff --git a/arkui-plugins/ui-plugins/interop/legacy-transformer.ts b/arkui-plugins/ui-plugins/interop/legacy-transformer.ts index 777a50e60..e3248f6e9 100644 --- a/arkui-plugins/ui-plugins/interop/legacy-transformer.ts +++ b/arkui-plugins/ui-plugins/interop/legacy-transformer.ts @@ -260,8 +260,8 @@ export class LegacyTransformer extends AbstractVisitor { } handleWrappedBuilderNode(node: arkts.ETSTypeReference): arkts.ETSTypeReference { - if (node.part && arkts.isETSTypeReferencePart(node.part) && node.part.name - && arkts.isIdentifier(node.part.name) && node.part.name.name === 'WrappedBuilder') { + if (node.part && arkts.isETSTypeReferencePart(node.part) && node.part.name && + arkts.isIdentifier(node.part.name) && node.part.name.name === 'WrappedBuilder') { return arkts.factory.createTypeReference( arkts.factory.createTypeReferencePart( arkts.factory.createIdentifier('Any') diff --git a/arkui-plugins/ui-plugins/interop/predefines.ts b/arkui-plugins/ui-plugins/interop/predefines.ts index 4bfadb51c..f169741bd 100644 --- a/arkui-plugins/ui-plugins/interop/predefines.ts +++ b/arkui-plugins/ui-plugins/interop/predefines.ts @@ -65,12 +65,4 @@ export enum InteropProvideNames { export enum builderMethodNames { AFTERUPDATEPROPERTY = 'afterUpdateProperty', CREATECOMPATIBLENODE = 'createCompatibleNode', - CREATEFN = 'createFn', -} - -export enum buildAbilityName { - BUILDERVIEWCLASS = 'builderViewClass', - BUILDERV2 = 'builderV2', - CREATE = 'create', - GETVIEWV2CLASS = 'getViewV2Class', } \ No newline at end of file -- Gitee