1/* 2 * Copyright (c) 2022-2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 17 18import * as arkts from '@koalaui/libarkts'; 19import { InteroperAbilityNames } from '../common/predefines'; 20import { getCustomComponentOptionsName } from './utils'; 21import { InteropContext } from './component-transformer'; 22import { annotation, backingField, isAnnotation } from '../common/arkts-utils'; 23import { hasLink, processLink, processNormal } from './initstatevar'; 24 25interface propertyInfo { 26 decorators: string[], 27 type: arkts.TypeNode, 28} 29 30export function createEmptyESValue(name: string): arkts.VariableDeclaration { 31 return arkts.factory.createVariableDeclaration( 32 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 33 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, 34 [ 35 arkts.factory.createVariableDeclarator( 36 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, 37 arkts.factory.createIdentifier(name), 38 arkts.factory.createCallExpression( 39 arkts.factory.createMemberExpression( 40 arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE), 41 arkts.factory.createIdentifier(InteroperAbilityNames.INITEMPTYOBJECT), 42 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 43 false, 44 false 45 ), 46 undefined, 47 undefined 48 ) 49 ) 50 ] 51 ); 52} 53 54export function getWrapValue(value: arkts.AstNode): arkts.AstNode { 55 return arkts.factory.createCallExpression( 56 arkts.factory.createMemberExpression( 57 arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE), 58 arkts.factory.createIdentifier(InteroperAbilityNames.WRAP), 59 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 60 false, 61 false 62 ), 63 undefined, 64 [value] 65 ); 66} 67 68export function setPropertyESValue(name: string, key: string, wrapValue: arkts.AstNode): arkts.ExpressionStatement { 69 return arkts.factory.createExpressionStatement( 70 arkts.factory.createCallExpression( 71 arkts.factory.createMemberExpression( 72 arkts.factory.createIdentifier(name), 73 arkts.factory.createIdentifier(InteroperAbilityNames.SETPROPERTY), 74 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 75 false, 76 false 77 ), 78 undefined, 79 [ 80 arkts.factory.createStringLiteral(key), 81 wrapValue 82 ] 83 ) 84 ); 85} 86 87export function getPropertyESValue(result: string, object: string, key: string): arkts.VariableDeclaration { 88 return arkts.factory.createVariableDeclaration( 89 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 90 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, 91 [ 92 arkts.factory.createVariableDeclarator( 93 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, 94 arkts.factory.createIdentifier(result), 95 arkts.factory.createCallExpression( 96 arkts.factory.createMemberExpression( 97 arkts.factory.createIdentifier(object), 98 arkts.factory.createIdentifier(InteroperAbilityNames.GETPROPERTY), 99 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 100 false, 101 false 102 ), 103 undefined, 104 [arkts.factory.create1StringLiteral(key)] 105 ) 106 ) 107 ] 108 ); 109} 110 111function initialArgs(args: arkts.ObjectExpression, varMap: Map<string, propertyInfo>): arkts.Statement[] { 112 const result: arkts.Statement[] = [ 113 createEmptyESValue(InteroperAbilityNames.PARAM), 114 getPropertyESValue('createState', 'global', 'createStateVariable') 115 ]; 116 117 const proxySet = new Set<string>(); 118 119 for (const property of args.properties) { 120 if (!(property instanceof arkts.Property)) { 121 continue; 122 } 123 const key = property.key; 124 const value = property.value; 125 if (!(key instanceof arkts.Identifier)) { 126 throw Error('Error arguments in Legacy Component'); 127 } 128 const name = key.name; 129 const decorators = varMap.get(name)?.decorators; 130 const type = varMap.get(name)?.type!; 131 if (decorators !== undefined && hasLink(decorators)) { 132 const initParam = processLink(key.name, value!, type, proxySet); 133 result.push(...initParam); 134 } else { 135 const initParam = processNormal(key.name, value!); 136 result.push(...initParam); 137 } 138 } 139 return result; 140} 141 142function instantiateComponent(params: arkts.AstNode[]): arkts.VariableDeclaration { 143 return arkts.factory.createVariableDeclaration( 144 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 145 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, 146 [ 147 arkts.factory.createVariableDeclarator( 148 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, 149 arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT), 150 arkts.factory.createCallExpression( 151 arkts.factory.createMemberExpression( 152 arkts.factory.createIdentifier(InteroperAbilityNames.STRUCTOBJECT), 153 arkts.factory.createIdentifier(InteroperAbilityNames.INSTANTIATE), 154 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 155 false, 156 false 157 ), 158 undefined, 159 params 160 ) 161 ) 162 ] 163 ); 164} 165 166function paramsLambdaDeclaration(name: string, args?: arkts.ObjectExpression): arkts.Statement[] { 167 const result = []; 168 result.push( 169 arkts.factory.createVariableDeclaration( 170 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 171 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, 172 [ 173 arkts.factory.createVariableDeclarator( 174 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, 175 arkts.factory.createIdentifier(InteroperAbilityNames.PARAMSLAMBDA), 176 arkts.factory.createArrowFunction( 177 arkts.factory.createScriptFunction( 178 arkts.factory.createBlock([arkts.factory.createReturnStatement( 179 args ? args : arkts.ObjectExpression.createObjectExpression( 180 arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, 181 [], 182 false 183 ), 184 )]), 185 arkts.factory.createFunctionSignature( 186 undefined, 187 [], 188 arkts.factory.createTypeReference( 189 arkts.factory.createTypeReferencePart( 190 arkts.factory.createIdentifier(getCustomComponentOptionsName(name)) 191 ) 192 ), 193 false 194 ), 195 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, 196 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 197 ) 198 ) 199 ), 200 201 ] 202 ) 203 ); 204 return result; 205} 206 207function createInitReturn(componentName: string): arkts.ReturnStatement { 208 return arkts.factory.createReturnStatement( 209 arkts.ObjectExpression.createObjectExpression( 210 arkts.Es2pandaAstNodeType.AST_NODE_TYPE_OBJECT_EXPRESSION, 211 [ 212 arkts.Property.createProperty( 213 arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT), 214 arkts.factory.createIdentifier(InteroperAbilityNames.COMPONENT) 215 ), 216 arkts.Property.createProperty( 217 arkts.factory.createIdentifier('name'), 218 arkts.factory.createStringLiteral(componentName) 219 ) 220 ], 221 false 222 ), 223 ); 224} 225 226function createExtraInfo(properties: string[], value: string[]): arkts.Statement[] { 227 const body: arkts.AstNode[] = []; 228 body.push(createEmptyESValue(InteroperAbilityNames.EXTRAINFO)); 229 properties.forEach((prop, index) => { 230 const val = value[index]; 231 body.push(setPropertyESValue( 232 InteroperAbilityNames.EXTRAINFO, 233 prop, 234 arkts.factory.createStringLiteral(val)) 235 ); 236 }); 237 return body; 238} 239 240function createESParent(): arkts.Statement { 241 return arkts.factory.createVariableDeclaration( 242 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 243 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, 244 [ 245 arkts.factory.createVariableDeclarator( 246 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, 247 arkts.factory.createIdentifier('esparent'), 248 getWrapValue(arkts.factory.createIdentifier(InteroperAbilityNames.PARENT)) 249 ) 250 ] 251 ); 252} 253 254function createESUndefined(): arkts.Statement { 255 return arkts.factory.createVariableDeclaration( 256 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 257 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, 258 [ 259 arkts.factory.createVariableDeclarator( 260 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, 261 arkts.factory.createIdentifier('esundefined'), 262 getWrapValue(arkts.factory.createUndefinedLiteral()) 263 ) 264 ] 265 ); 266} 267 268function createESBlank(): arkts.Statement[] { 269 const body: arkts.Statement[] = []; 270 const blank = arkts.factory.createVariableDeclaration( 271 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 272 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, 273 [ 274 arkts.factory.createVariableDeclarator( 275 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, 276 arkts.factory.createIdentifier('blank'), 277 arkts.factory.createArrowFunction( 278 arkts.factory.createScriptFunction( 279 arkts.factory.createBlock([]), 280 arkts.factory.createFunctionSignature( 281 undefined, 282 [], 283 undefined, 284 false 285 ), 286 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, 287 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 288 ) 289 ) 290 ) 291 ] 292 ); 293 body.push(blank); 294 const asExpression = arkts.factory.createTSAsExpression( 295 arkts.factory.createIdentifier('blank'), 296 arkts.factory.createTypeReference( 297 arkts.factory.createTypeReferencePart( 298 arkts.factory.createIdentifier('object') 299 ) 300 ), 301 false 302 ); 303 const esblank = arkts.factory.createVariableDeclaration( 304 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 305 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, 306 [ 307 arkts.factory.createVariableDeclarator( 308 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, 309 arkts.factory.createIdentifier('esblank'), 310 getWrapValue(asExpression) 311 ) 312 ] 313 ); 314 body.push(esblank); 315 return body; 316} 317 318function createGlobal(): arkts.Statement { 319 return arkts.factory.createVariableDeclaration( 320 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 321 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, 322 [arkts.factory.createVariableDeclarator( 323 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, 324 arkts.factory.createIdentifier('global'), 325 arkts.factory.createCallExpression( 326 arkts.factory.createMemberExpression( 327 arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE), 328 arkts.factory.createIdentifier('getGlobal'), 329 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 330 false, 331 false 332 ), 333 undefined, 334 undefined 335 ) 336 )] 337 ); 338} 339 340function createELMTID(): arkts.Statement[] { 341 const body: arkts.Statement[] = []; 342 const viewStackProcessor = getPropertyESValue('viewStackProcessor', 'global', 'ViewStackProcessor'); 343 body.push(viewStackProcessor); 344 const createId = getPropertyESValue('createId', 'viewStackProcessor', 'AllocateNewElmetIdForNextComponent'); 345 body.push(createId); 346 const elmtId = arkts.factory.createVariableDeclaration( 347 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 348 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, 349 [arkts.factory.createVariableDeclarator( 350 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, 351 arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), 352 arkts.factory.createCallExpression( 353 arkts.factory.createMemberExpression( 354 arkts.factory.createIdentifier('createId'), 355 arkts.factory.createIdentifier('invoke'), 356 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 357 false, 358 false 359 ), 360 undefined, 361 undefined 362 ) 363 )] 364 ); 365 body.push(elmtId); 366 return body; 367} 368 369function createComponent(moduleName: string, className: string): arkts.Statement[] { 370 const body: arkts.Statement[] = []; 371 const module = arkts.factory.createVariableDeclaration( 372 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 373 arkts.Es2pandaVariableDeclarationKind.VARIABLE_DECLARATION_KIND_LET, 374 [ 375 arkts.factory.createVariableDeclarator( 376 arkts.Es2pandaVariableDeclaratorFlag.VARIABLE_DECLARATOR_FLAG_LET, 377 arkts.factory.createIdentifier(moduleName), 378 arkts.factory.createCallExpression( 379 arkts.factory.createMemberExpression( 380 arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE), 381 arkts.factory.createIdentifier(InteroperAbilityNames.LOAD), 382 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 383 false, 384 false 385 ), 386 undefined, 387 [arkts.factory.create1StringLiteral(InteroperAbilityNames.OHMURL)] 388 ) 389 ) 390 ] 391 ); 392 body.push(module); 393 const structObject = getPropertyESValue('structObject', moduleName, className); 394 body.push(structObject); 395 const component = instantiateComponent( 396 [ 397 arkts.factory.createIdentifier('esundefined'), 398 arkts.factory.createIdentifier(InteroperAbilityNames.PARAM), 399 arkts.factory.createIdentifier('esundefined'), 400 arkts.factory.createIdentifier(InteroperAbilityNames.ELMTID), 401 arkts.factory.createIdentifier('esblank'), 402 arkts.factory.createIdentifier(InteroperAbilityNames.EXTRAINFO) 403 ] 404 ); 405 body.push(component); 406 return body; 407} 408 409function invokeViewPUCreate(): arkts.Statement[] { 410 const body: arkts.Statement[] = []; 411 const createMethod = getPropertyESValue('create', 'structObject', 'create'); 412 body.push(createMethod); 413 const viewPUCreate = arkts.factory.createExpressionStatement( 414 arkts.factory.createCallExpression( 415 arkts.factory.createMemberExpression( 416 arkts.factory.createIdentifier('create'), 417 arkts.factory.createIdentifier('invoke'), 418 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 419 false, 420 false 421 ), 422 undefined, 423 [ 424 arkts.factory.createIdentifier('component') 425 ] 426 ) 427 ); 428 body.push(viewPUCreate); 429 return body; 430} 431 432function createWrapperBlock(context: InteropContext, varMap: Map<string, propertyInfo>): arkts.BlockStatement { 433 const className = context.className; 434 const path = context.path; 435 const args = context.arguments; 436 const index = path.indexOf('/'); 437 if (index === -1) { 438 throw new Error('Error path of Legacy Component.'); 439 } 440 const moduleName = path.substring(0, index); 441 const initialArgsStatement = args ? initialArgs(args, varMap) : []; 442 return arkts.factory.createBlock( 443 [ 444 createGlobal(), 445 ...initialArgsStatement, 446 ...createExtraInfo(['page'], [path]), 447 createESUndefined(), 448 ...createESBlank(), 449 ...createELMTID(), 450 ...createComponent(moduleName, className), 451 ...invokeViewPUCreate(), 452 // ...paramsLambdaDeclaration(className, args), 453 // setPropertyESValue( 454 // 'component', 455 // 'paramsGenerator_', 456 // arkts.factory.createIdentifier(InteroperAbilityNames.PARAMSLAMBDA) 457 // ), 458 createInitReturn(className) 459 ] 460 ); 461} 462 463function createInitializer(context: InteropContext, varMap: Map<string, propertyInfo>): arkts.ArrowFunctionExpression { 464 const block = createWrapperBlock(context, varMap); 465 return arkts.factory.createArrowFunction( 466 arkts.factory.createScriptFunction( 467 block, 468 arkts.factory.createFunctionSignature( 469 undefined, 470 [], 471 undefined, 472 false, 473 ), 474 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, 475 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 476 ) 477 ); 478} 479 480function createUpdater(esvalue: arkts.ETSTypeReference, varMap: Map<string, propertyInfo>): arkts.ArrowFunctionExpression { 481 return arkts.factory.createArrowFunction( 482 arkts.factory.createScriptFunction( 483 arkts.factory.createBlock( 484 [ 485 486 ] 487 ), 488 arkts.factory.createFunctionSignature( 489 undefined, 490 [ 491 arkts.factory.createParameterDeclaration( 492 arkts.factory.createIdentifier(InteroperAbilityNames.INSTANCE, esvalue), 493 undefined, 494 ), 495 ], 496 undefined, 497 false, 498 ), 499 arkts.Es2pandaScriptFunctionFlags.SCRIPT_FUNCTION_FLAGS_ARROW, 500 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_NONE, 501 ) 502 ); 503} 504 505function generateVarMap(node: arkts.Identifier): Map<string, propertyInfo> { 506 const decl = arkts.getDecl(node); 507 if (!(decl instanceof arkts.ClassDefinition)) { 508 throw Error("can't find legacy class declaration"); 509 } 510 const result = new Map<string, propertyInfo>(); 511 const definition = decl; 512 const body = definition.body; 513 body.forEach(node => { 514 if (node instanceof arkts.ClassProperty && node.key instanceof arkts.Identifier) { 515 const key = node.key.name; 516 const annotations = node.annotations; 517 const decorators: string[] = annotations.map(annotation => { 518 return (annotation.expr as arkts.Identifier).name; 519 }); 520 const type: arkts.TypeNode = node.typeAnnotation!; 521 result.set(key, {decorators: decorators, type: type}); 522 } 523 }); 524 return result; 525} 526 527export function updateArkUICompatible(node: arkts.CallExpression): arkts.CallExpression { 528 const classInterop = (node.expression as arkts.MemberExpression).object as arkts.Identifier; 529 const className = classInterop.name; 530 const args = node.arguments; 531 const path = (args[0] as arkts.StringLiteral).str; 532 const line = args[1] instanceof arkts.UndefinedLiteral ? undefined : (args[1] as arkts.NumberLiteral).value; 533 const col = args[2] instanceof arkts.UndefinedLiteral ? undefined : (args[2] as arkts.NumberLiteral).value; 534 const options = args[3] instanceof arkts.UndefinedLiteral ? undefined : args[3] as arkts.ObjectExpression; 535 const context: InteropContext = { 536 className: className, 537 path: path, 538 line: line, 539 col: col, 540 arguments: options 541 }; 542 543 const varMap: Map<string, propertyInfo> = generateVarMap(classInterop); 544 const esvalue = arkts.factory.createTypeReference( 545 arkts.factory.createTypeReferencePart( 546 arkts.factory.createIdentifier(InteroperAbilityNames.ESVALUE) 547 ) 548 ); 549 const initializer = createInitializer(context, varMap); 550 const updater = createUpdater(esvalue, varMap); 551 return arkts.factory.updateCallExpression( 552 node, 553 arkts.factory.createIdentifier(InteroperAbilityNames.ARKUICOMPATIBLE), 554 undefined, 555 [ 556 initializer, 557 updater, 558 ] 559 ); 560} 561 562 563function generateStructInfo(context: InteropContext): arkts.AstNode[] { 564 const result: arkts.AstNode[] = [ 565 arkts.factory.createStringLiteral(context.path), 566 context.line ? arkts.factory.createIdentifier(context.line.toString()) : arkts.factory.createUndefinedLiteral(), 567 context.col ? arkts.factory.createIdentifier(context.col.toString()) : arkts.factory.createUndefinedLiteral(), 568 context.arguments ?? arkts.factory.createUndefinedLiteral() 569 ]; 570 return result; 571 572} 573 574export function generateTempCallFunction(context: InteropContext): arkts.CallExpression { 575 return arkts.factory.createCallExpression( 576 arkts.factory.createMemberExpression( 577 arkts.factory.createIdentifier(context.className), 578 arkts.factory.createIdentifier('instantiate_Interop'), 579 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 580 false, 581 false 582 ), 583 undefined, 584 generateStructInfo(context) 585 ); 586} 587 588export function isArkUICompatible(node: arkts.AstNode): boolean { 589 if (node instanceof arkts.CallExpression && node.expression instanceof arkts.MemberExpression && 590 node.expression.property instanceof arkts.Identifier && 591 node.expression.property.name === 'instantiate_Interop') { 592 return true; 593 } 594 return false; 595}