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 16import * as arkts from '@koalaui/libarkts'; 17import { factory } from './memo-factory'; 18import { AbstractVisitor, VisitorOptions } from '../common/abstract-visitor'; 19import { 20 MemoInfo, 21 PositionalIdTracker, 22 ReturnTypeInfo, 23 buildReturnTypeInfo, 24 castArrowFunctionExpression, 25 castIdentifier, 26 castOverloadsToMethods, 27 castParameters, 28 findMemoFromTypeAnnotation, 29 findThisAttribute, 30 getDeclResolveAlias, 31 hasMemoAnnotation, 32 hasMemoEntryAnnotation, 33 hasMemoIntrinsicAnnotation, 34 hasMemoStableAnnotation, 35 isDeclaredMethodWithMemoParams, 36 isFunctionProperty, 37 isMemoArrowFunction, 38 isMemoClassProperty, 39 isMemoDeclaredClassProperty, 40 isMemoDeclaredIdentifier, 41 isMemoDeclaredMethod, 42 isMemoETSParameterExpression, 43 isMemoMethodDefinition, 44 isMemoProperty, 45 isMemoTSTypeAliasDeclaration, 46 isMemoThisAttribute, 47 isMemoVariableDeclarator, 48 isStandaloneArrowFunction, 49 isThisAttributeAssignment, 50 removeMemoAnnotation, 51 parametrizedNodeHasReceiver 52} from './utils'; 53import { ParameterTransformer } from './parameter-transformer'; 54import { ReturnTransformer } from './return-transformer'; 55import { SignatureTransformer } from './signature-transformer'; 56import { moveToFront } from '../common/arkts-utils'; 57import { InternalsTransformer } from './internal-transformer'; 58 59interface ScopeInfo extends MemoInfo { 60 regardAsSameScope?: boolean; 61} 62 63export interface FunctionTransformerOptions extends VisitorOptions { 64 positionalIdTracker: PositionalIdTracker; 65 parameterTransformer: ParameterTransformer; 66 returnTransformer: ReturnTransformer; 67 signatureTransformer: SignatureTransformer; 68 internalsTransformer?: InternalsTransformer; 69} 70 71export class FunctionTransformer extends AbstractVisitor { 72 private readonly positionalIdTracker: PositionalIdTracker; 73 private readonly parameterTransformer: ParameterTransformer; 74 private readonly returnTransformer: ReturnTransformer; 75 private readonly signatureTransformer: SignatureTransformer; 76 private readonly internalsTransformer?: InternalsTransformer; 77 78 /* Tracking whether should import `__memo_context_type` and `__memo_id_type` */ 79 private modified = false; 80 81 constructor(options: FunctionTransformerOptions) { 82 super(options); 83 this.positionalIdTracker = options.positionalIdTracker; 84 this.parameterTransformer = options.parameterTransformer; 85 this.returnTransformer = options.returnTransformer; 86 this.signatureTransformer = options.signatureTransformer; 87 this.internalsTransformer = options.internalsTransformer; 88 } 89 90 private scopes: ScopeInfo[] = []; 91 private stable: number = 0; 92 93 reset() { 94 super.reset(); 95 this.scopes = []; 96 this.stable = 0; 97 this.modified = false; 98 this.parameterTransformer.reset(); 99 this.returnTransformer.reset(); 100 this.signatureTransformer.reset(); 101 } 102 103 private enterMethod(node: arkts.MethodDefinition): void { 104 const name = node.name.name; 105 const isMemo = isMemoMethodDefinition(node); 106 this.scopes.push({ name, isMemo }); 107 } 108 109 private enterClassPropety(node: arkts.ClassProperty): void { 110 const name = castIdentifier(node.key).name; 111 const isMemo = isMemoClassProperty(node); 112 this.scopes.push({ name, isMemo }); 113 } 114 115 private enterStandaloneArrowFunction(node: arkts.ArrowFunctionExpression): void { 116 const name = undefined; 117 const isMemo = isMemoArrowFunction(node); 118 this.scopes.push({ name, isMemo }); 119 } 120 121 private enterTSTypeAliasDeclaration(node: arkts.TSTypeAliasDeclaration): void { 122 const name = castIdentifier(node.id).name; 123 const isMemo = isMemoTSTypeAliasDeclaration(node); 124 this.scopes.push({ name, isMemo }); 125 } 126 127 private enterVariableDeclarator(node: arkts.VariableDeclarator): void { 128 const name = node.name.name; 129 const isMemo = isMemoVariableDeclarator(node); 130 this.scopes.push({ name, isMemo, regardAsSameScope: !!node.initializer }); 131 } 132 133 private enterTSAsExpression(node: arkts.TSAsExpression): void { 134 const isMemo = findMemoFromTypeAnnotation(node.typeAnnotation); 135 this.scopes.push({ isMemo }); 136 } 137 138 private enterFunctionProperty(node: arkts.Property): void { 139 const name = castIdentifier(node.key).name; 140 const isMemo = isMemoProperty(node, castArrowFunctionExpression(node.value)); 141 this.scopes.push({ name, isMemo }); 142 } 143 144 private enterThisAttributeAssignment(node: arkts.AssignmentExpression): void { 145 const thisAttribute = findThisAttribute(node.left!)!; 146 const name = thisAttribute.name; 147 const isMemo = isMemoThisAttribute(thisAttribute, castArrowFunctionExpression(node.right)); 148 this.scopes.push({ name, isMemo }); 149 } 150 151 enter(node: arkts.AstNode): this { 152 if (arkts.isMethodDefinition(node)) { 153 this.enterMethod(node); 154 } 155 if (arkts.isClassProperty(node) && !!node.key && arkts.isIdentifier(node.key)) { 156 this.enterClassPropety(node); 157 } 158 if (isStandaloneArrowFunction(node)) { 159 this.enterStandaloneArrowFunction(node); 160 } 161 if (arkts.isTSTypeAliasDeclaration(node) && !!node.id && !!node.typeAnnotation) { 162 this.enterTSTypeAliasDeclaration(node); 163 } 164 if (arkts.isVariableDeclarator(node)) { 165 this.enterVariableDeclarator(node); 166 } 167 if (arkts.isTSAsExpression(node) && !!node.expr && arkts.isArrowFunctionExpression(node.expr)) { 168 this.enterTSAsExpression(node); 169 } 170 if (isFunctionProperty(node)) { 171 this.enterFunctionProperty(node); 172 } 173 if (isThisAttributeAssignment(node) && !!node.right && arkts.isArrowFunctionExpression(node.right)) { 174 this.enterThisAttributeAssignment(node); 175 } 176 if (arkts.isClassDefinition(node)) { 177 if (hasMemoStableAnnotation(node)) { 178 this.stable++; 179 } 180 } 181 return this; 182 } 183 184 exit(node: arkts.AstNode) { 185 if (arkts.isMethodDefinition(node)) { 186 this.scopes.pop(); 187 } 188 if (arkts.isClassDefinition(node)) { 189 if (hasMemoStableAnnotation(node)) { 190 this.stable--; 191 } 192 } 193 return this; 194 } 195 196 enterAnonymousScope(node: arkts.ScriptFunction) { 197 const name = undefined; 198 const isMemo = hasMemoAnnotation(node) || hasMemoIntrinsicAnnotation(node); 199 this.scopes.push({ name, isMemo }); 200 return this; 201 } 202 203 exitAnonymousScope() { 204 this.scopes.pop(); 205 return this; 206 } 207 208 checkMemoCallInMethod(decl: arkts.MethodDefinition) { 209 const scope = this.scopes[this.scopes.length - 1]; 210 if (scope?.regardAsSameScope === false && scope?.isMemo === false) { 211 if (scope.name) { 212 console.error(`Attempt to call @memo-method ${decl.name.name} from non-@memo-method ${scope.name}`); 213 throw 'Invalid @memo usage'; 214 } else { 215 console.error(`Attempt to call @memo-method ${decl.name.name} from anonymous non-@memo-method`); 216 throw 'Invalid @memo usage'; 217 } 218 } 219 return this; 220 } 221 222 checkMemoCallInFunction() { 223 const scope = this.scopes[this.scopes.length - 1]; 224 if (scope?.regardAsSameScope === false && scope?.isMemo === false) { 225 console.error(`Attempt to call @memo-function`); 226 throw 'Invalid @memo usage'; 227 } 228 return this; 229 } 230 231 updateInternalsInScriptFunction(scriptFunction: arkts.ScriptFunction): arkts.ScriptFunction { 232 if (!scriptFunction.body || !arkts.isBlockStatement(scriptFunction.body) || !this.internalsTransformer) { 233 return scriptFunction; 234 } 235 const afterInternalsTransformer = this.internalsTransformer.visitor( 236 scriptFunction.body 237 ) as arkts.BlockStatement; 238 return arkts.factory.updateScriptFunction( 239 scriptFunction, 240 afterInternalsTransformer, 241 arkts.factory.createFunctionSignature( 242 scriptFunction.typeParams, 243 scriptFunction.params, 244 scriptFunction.returnTypeAnnotation, 245 scriptFunction.hasReceiver 246 ), 247 scriptFunction.flags, 248 scriptFunction.modifiers 249 ); 250 } 251 252 updateScriptFunction(scriptFunction: arkts.ScriptFunction, name: string = ''): arkts.ScriptFunction { 253 if (!scriptFunction.body || !arkts.isBlockStatement(scriptFunction.body)) { 254 return scriptFunction; 255 } 256 if (this.parameterTransformer.isTracked(scriptFunction.body)) { 257 return this.updateInternalsInScriptFunction(scriptFunction); 258 } 259 if (hasMemoIntrinsicAnnotation(scriptFunction) || hasMemoEntryAnnotation(scriptFunction)) { 260 return this.updateInternalsInScriptFunction(scriptFunction); 261 } 262 const returnType = scriptFunction.returnTypeAnnotation; 263 const isStableThis = this.stable > 0 && returnType !== undefined && arkts.isTSThisType(returnType); 264 const returnTypeInfo: ReturnTypeInfo = buildReturnTypeInfo( 265 returnType, 266 findMemoFromTypeAnnotation(returnType), 267 isStableThis 268 ); 269 const [body, parameterIdentifiers, memoParametersDeclaration, syntheticReturnStatement] = 270 factory.updateFunctionBody( 271 scriptFunction.body, 272 castParameters(scriptFunction.params), 273 returnTypeInfo, 274 this.positionalIdTracker.id(name) 275 ); 276 const afterParameterTransformer = this.parameterTransformer 277 .withParameters(parameterIdentifiers) 278 .skip(memoParametersDeclaration) 279 .visitor(body); 280 const afterReturnTransformer = this.returnTransformer 281 .skip(syntheticReturnStatement) 282 .registerReturnTypeInfo(returnTypeInfo) 283 .rewriteThis(this.stable > 0) 284 .visitor(afterParameterTransformer); 285 const updateScriptFunction = factory.updateScriptFunctionWithMemoParameters( 286 scriptFunction, 287 afterReturnTransformer, 288 returnTypeInfo.node 289 ); 290 this.modified = true; 291 this.parameterTransformer.track(updateScriptFunction.body); 292 return this.updateInternalsInScriptFunction(updateScriptFunction); 293 } 294 295 private updateMethodDefinition(node: arkts.MethodDefinition): arkts.MethodDefinition { 296 let updateMethod: arkts.MethodDefinition; 297 const that = this; 298 const updateOverloads = node.overloads?.map((overload) => that.visitor(overload)) ?? undefined; 299 const isMemo = 300 hasMemoAnnotation(node.scriptFunction) || 301 hasMemoIntrinsicAnnotation(node.scriptFunction) || 302 hasMemoEntryAnnotation(node.scriptFunction); 303 if (isMemo && node.scriptFunction.body) { 304 const hasIntrinsic = hasMemoIntrinsicAnnotation(node.scriptFunction); 305 updateMethod = arkts.factory.updateMethodDefinition( 306 node, 307 node.kind, 308 node.name, 309 this.signatureTransformer.visitor( 310 removeMemoAnnotation(this.updateScriptFunction(node.scriptFunction, node.name.name)), 311 hasIntrinsic 312 ), 313 node.modifiers, 314 false 315 ); 316 } else { 317 updateMethod = arkts.factory.updateMethodDefinition( 318 node, 319 node.kind, 320 node.name, 321 this.signatureTransformer.visitor(node.scriptFunction), 322 node.modifiers, 323 false 324 ); 325 } 326 if (!!updateOverloads) { 327 updateMethod.setOverloads(castOverloadsToMethods(updateOverloads)); 328 } 329 this.modified ||= this.signatureTransformer.modified; 330 return updateMethod; 331 } 332 333 private updateDeclaredMethodMemoCall( 334 node: arkts.CallExpression, 335 decl: arkts.MethodDefinition, 336 ignoreSelf: boolean = false 337 ): arkts.CallExpression { 338 let updatedArguments: arkts.AstNode[] = node.arguments.map((it, index) => { 339 const param = decl.scriptFunction.params.at(index); 340 if (!param || !arkts.isEtsParameterExpression(param)) { 341 return it; 342 } 343 if (isMemoETSParameterExpression(param) && arkts.isArrowFunctionExpression(it)) { 344 this.enterAnonymousScope(it.scriptFunction); 345 const res = this.updateScriptFunction(it.scriptFunction); 346 this.exitAnonymousScope(); 347 this.modified = true; 348 return arkts.factory.updateArrowFunction(it, res); 349 } 350 return it; 351 }); 352 if (!ignoreSelf) { 353 this.checkMemoCallInMethod(decl); 354 updatedArguments = [ 355 ...factory.createHiddenArguments(this.positionalIdTracker.id(decl.name.name)), 356 ...updatedArguments, 357 ]; 358 } 359 const isMemo = 360 hasMemoAnnotation(decl.scriptFunction) || 361 hasMemoIntrinsicAnnotation(decl.scriptFunction) || 362 hasMemoEntryAnnotation(decl.scriptFunction); 363 if (parametrizedNodeHasReceiver(decl.scriptFunction) && isMemo) { 364 updatedArguments = moveToFront(updatedArguments, 2); 365 } 366 this.modified = true; 367 return arkts.factory.updateCallExpression(node, node.expression, node.typeArguments, updatedArguments); 368 } 369 370 private updateDeclaredCallWithName(node: arkts.CallExpression, name: string): arkts.CallExpression { 371 this.modified = true; 372 return factory.insertHiddenArgumentsToCall(node, this.positionalIdTracker.id(name)); 373 } 374 375 private updateAnonymousCallWithMemoParams(node: arkts.CallExpression): arkts.CallExpression { 376 let newExpression: arkts.AstNode = node.expression; 377 if (isStandaloneArrowFunction(node.expression)) { 378 newExpression = arkts.factory.updateArrowFunction( 379 node.expression, 380 this.signatureTransformer.visitor(node.expression.scriptFunction) 381 ); 382 } 383 const updatedArguments: arkts.AstNode[] = node.arguments.map((it) => { 384 if (arkts.isArrowFunctionExpression(it) && isMemoArrowFunction(it)) { 385 this.enterAnonymousScope(it.scriptFunction); 386 const res = this.updateScriptFunction(it.scriptFunction); 387 this.exitAnonymousScope(); 388 this.modified = true; 389 return arkts.factory.updateArrowFunction(it, res); 390 } 391 return it; 392 }); 393 this.modified ||= this.signatureTransformer.modified; 394 return arkts.factory.updateCallExpression(node, newExpression, node.typeArguments, updatedArguments); 395 } 396 397 private updateAnonymousMemoCall( 398 node: arkts.CallExpression, 399 expression: arkts.ArrowFunctionExpression 400 ): arkts.CallExpression { 401 const scope = this.scopes[this.scopes.length - 1]; 402 const isValidScope = !!scope && scope.name === expression.scriptFunction.id?.name; 403 if (!isValidScope) { 404 return node; 405 } 406 this.exitAnonymousScope(); 407 if (!scope.isMemo) { 408 return this.updateAnonymousCallWithMemoParams(node); 409 } 410 this.checkMemoCallInFunction(); 411 412 this.enterAnonymousScope(expression.scriptFunction); 413 const res = this.updateScriptFunction(expression.scriptFunction, expression.scriptFunction.id?.name); 414 this.exitAnonymousScope(); 415 416 const newNode = this.updateAnonymousCallWithMemoParams(node); 417 this.modified = true; 418 return arkts.factory.updateCallExpression( 419 node, 420 arkts.factory.updateArrowFunction(expression, res), 421 newNode.typeArguments, 422 [...factory.createHiddenArguments(this.positionalIdTracker.id()), ...newNode.arguments] 423 ); 424 } 425 426 private updateCallExpressionWithNoDecl(node: arkts.CallExpression): arkts.CallExpression { 427 if (isStandaloneArrowFunction(node.expression)) { 428 return this.updateAnonymousMemoCall(node, node.expression); 429 } 430 return this.updateAnonymousCallWithMemoParams(node); 431 } 432 433 private updateCallExpression(node: arkts.CallExpression): arkts.CallExpression { 434 const expr = node.expression; 435 const decl = getDeclResolveAlias(expr); 436 if (!decl) { 437 return this.updateCallExpressionWithNoDecl(node); 438 } 439 if (arkts.isMethodDefinition(decl) && isMemoDeclaredMethod(decl)) { 440 return this.updateDeclaredMethodMemoCall(node, decl); 441 } 442 if (arkts.isMethodDefinition(decl) && isDeclaredMethodWithMemoParams(decl)) { 443 return this.updateDeclaredMethodMemoCall(node, decl, true); 444 } 445 if (arkts.isIdentifier(decl) && isMemoDeclaredIdentifier(decl)) { 446 return this.updateDeclaredCallWithName(node, decl.name); 447 } 448 if ( 449 arkts.isClassProperty(decl) && 450 isMemoDeclaredClassProperty(decl) && 451 !!decl.key && 452 arkts.isIdentifier(decl.key) 453 ) { 454 return this.updateDeclaredCallWithName(node, decl.key.name); 455 } 456 if (arkts.isEtsParameterExpression(decl) && isMemoETSParameterExpression(decl)) { 457 return this.updateDeclaredCallWithName(node, decl.identifier.name); 458 } 459 return this.updateCallExpressionWithNoDecl(node); 460 } 461 462 private updateClassProperty(node: arkts.ClassProperty, key: arkts.Identifier): arkts.ClassProperty { 463 const scope = this.scopes[this.scopes.length - 1]; 464 const isValidScope = !!scope && scope.name === key.name; 465 if (!isValidScope) { 466 return node; 467 } 468 this.exitAnonymousScope(); 469 if (!scope.isMemo) { 470 return node; 471 } 472 473 let res: arkts.ScriptFunction | undefined; 474 if (!!node.value && arkts.isArrowFunctionExpression(node.value)) { 475 this.enterAnonymousScope(node.value.scriptFunction); 476 res = this.updateScriptFunction(node.value.scriptFunction, key.name); 477 this.exitAnonymousScope(); 478 } 479 480 let typeAnnotation: arkts.TypeNode | undefined; 481 if (!!node.typeAnnotation && !(typeAnnotation = factory.updateMemoTypeAnnotation(node.typeAnnotation))) { 482 console.error(`ETSFunctionType or ETSUnionType expected for @memo-property ${key.name}`); 483 throw 'Invalid @memo usage'; 484 } 485 486 this.modified = true; 487 return arkts.factory.updateClassProperty( 488 node, 489 node.key, 490 res ? arkts.factory.updateArrowFunction(castArrowFunctionExpression(node.value), res) : undefined, 491 typeAnnotation, 492 node.modifiers, 493 node.isComputed 494 ); 495 } 496 497 private updateTSTypeAliasDeclaration(node: arkts.TSTypeAliasDeclaration): arkts.TSTypeAliasDeclaration { 498 const scope = this.scopes[this.scopes.length - 1]; 499 const isValidScope = !!scope && scope.name === node.id?.name; 500 if (!isValidScope) { 501 return node; 502 } 503 this.exitAnonymousScope(); 504 if (!scope.isMemo) { 505 if (!!node.typeAnnotation) { 506 const newNode = arkts.factory.updateTSTypeAliasDeclaration( 507 node, 508 node.id, 509 node.typeParams, 510 this.signatureTransformer.visitor(node.typeAnnotation) 511 ); 512 this.modified ||= this.signatureTransformer.modified; 513 return newNode; 514 } 515 return node; 516 } 517 518 let typeAnnotation: arkts.TypeNode | undefined; 519 if (!(typeAnnotation = factory.updateMemoTypeAnnotation(node.typeAnnotation))) { 520 console.error(`ETSFunctionType or ETSUnionType expected for @memo-type ${node.id!.name}`); 521 throw 'Invalid @memo usage'; 522 } 523 524 this.modified = true; 525 return arkts.factory.updateTSTypeAliasDeclaration(node, node.id, node.typeParams, typeAnnotation); 526 } 527 528 private updateStandaloneArrowFunction(node: arkts.ArrowFunctionExpression): arkts.ArrowFunctionExpression { 529 const scope = this.scopes[this.scopes.length - 1]; 530 const isValidScope = !!scope && scope.name === node.scriptFunction.id?.name; 531 if (!isValidScope) { 532 return node; 533 } 534 this.exitAnonymousScope(); 535 if (!scope.isMemo) { 536 return arkts.factory.updateArrowFunction(node, this.signatureTransformer.visitor(node.scriptFunction)); 537 } 538 539 this.enterAnonymousScope(node.scriptFunction); 540 const res = this.updateScriptFunction(node.scriptFunction, node.scriptFunction.id?.name); 541 this.exitAnonymousScope(); 542 543 this.modified = true; 544 return arkts.factory.updateArrowFunction(node, this.signatureTransformer.visitor(res)); 545 } 546 547 private updateVariableDeclarator(node: arkts.VariableDeclarator): arkts.VariableDeclarator { 548 const scope = this.scopes[this.scopes.length - 1]; 549 const isValidScope = !!scope && scope.name === node.name.name; 550 if (!isValidScope) { 551 return node; 552 } 553 this.exitAnonymousScope(); 554 if (!scope.isMemo) { 555 if (!!node.initializer && arkts.isArrowFunctionExpression(node.initializer)) { 556 return arkts.factory.updateVariableDeclarator( 557 node, 558 node.flag, 559 node.name, 560 arkts.factory.updateArrowFunction( 561 node.initializer, 562 this.signatureTransformer.visitor(node.initializer.scriptFunction) 563 ) 564 ); 565 } 566 return node; 567 } 568 569 let typeAnnotation: arkts.TypeNode | undefined; 570 if ( 571 !!node.name.typeAnnotation && 572 !(typeAnnotation = factory.updateMemoTypeAnnotation(node.name.typeAnnotation)) 573 ) { 574 console.error(`ETSFunctionType or ETSUnionType expected for @memo-variable-type ${node.name.name}`); 575 throw 'Invalid @memo usage'; 576 } 577 578 let initializer: arkts.AstNode | undefined = node.initializer; 579 if (!!initializer && arkts.isArrowFunctionExpression(initializer)) { 580 this.enterAnonymousScope(initializer.scriptFunction); 581 const res = this.updateScriptFunction(initializer.scriptFunction, initializer.scriptFunction.id?.name); 582 this.exitAnonymousScope(); 583 initializer = arkts.factory.updateArrowFunction(initializer, res); 584 } 585 586 this.modified = true; 587 return arkts.factory.updateVariableDeclarator( 588 node, 589 node.flag, 590 arkts.factory.updateIdentifier(node.name, node.name.name, typeAnnotation), 591 initializer 592 ); 593 } 594 595 private updateTSAsExpression( 596 node: arkts.TSAsExpression, 597 expr: arkts.ArrowFunctionExpression 598 ): arkts.TSAsExpression { 599 const scope = this.scopes[this.scopes.length - 1]; 600 const isValidScope = !!scope; 601 if (!isValidScope) { 602 return node; 603 } 604 this.exitAnonymousScope(); 605 if (!scope.isMemo) { 606 return node; 607 } 608 609 this.enterAnonymousScope(expr.scriptFunction); 610 const res = this.updateScriptFunction(expr.scriptFunction, expr.scriptFunction.id?.name); 611 this.exitAnonymousScope(); 612 613 let typeAnnotation: arkts.TypeNode | undefined; 614 if (!(typeAnnotation = factory.updateMemoTypeAnnotation(node.typeAnnotation))) { 615 console.error(`ETSFunctionType or ETSUnionType expected for @memo-as-type`); 616 throw 'Invalid @memo usage'; 617 } 618 619 this.modified = true; 620 return arkts.factory.updateTSAsExpression( 621 node, 622 arkts.factory.updateArrowFunction(expr, res), 623 typeAnnotation, 624 node.isConst 625 ); 626 } 627 628 private updateProperty( 629 node: arkts.Property, 630 key: arkts.Identifier, 631 value: arkts.ArrowFunctionExpression 632 ): arkts.Property { 633 const scope = this.scopes[this.scopes.length - 1]; 634 const isValidScope = !!scope && scope.name === key.name; 635 if (!isValidScope) { 636 return node; 637 } 638 this.exitAnonymousScope(); 639 if (!scope.isMemo) { 640 return node; 641 } 642 643 this.enterAnonymousScope(value.scriptFunction); 644 const res = this.updateScriptFunction(value.scriptFunction, value.scriptFunction.id?.name); 645 this.exitAnonymousScope(); 646 647 this.modified = true; 648 return arkts.factory.updateProperty(node, key, arkts.factory.updateArrowFunction(value, res)); 649 } 650 651 private updateThisAttributeAssignment( 652 node: arkts.AssignmentExpression, 653 thisAttribute: arkts.Identifier, 654 right: arkts.ArrowFunctionExpression 655 ): arkts.AssignmentExpression { 656 const scope = this.scopes[this.scopes.length - 1]; 657 const isValidScope = !!scope && scope.name === thisAttribute.name; 658 if (!isValidScope) { 659 return node; 660 } 661 this.exitAnonymousScope(); 662 if (!scope.isMemo) { 663 return node; 664 } 665 666 this.enterAnonymousScope(right.scriptFunction); 667 const res = this.updateScriptFunction(right.scriptFunction, right.scriptFunction.id?.name); 668 this.exitAnonymousScope(); 669 670 this.modified = true; 671 return arkts.factory.updateAssignmentExpression( 672 node, 673 node.left!, 674 node.operatorType, 675 arkts.factory.updateArrowFunction(right, res) 676 ); 677 } 678 679 visitor(beforeChildren: arkts.AstNode): arkts.AstNode { 680 this.enter(beforeChildren); 681 const node = this.visitEachChild(beforeChildren); 682 this.exit(beforeChildren); 683 if (arkts.isMethodDefinition(node)) { 684 return this.updateMethodDefinition(node); 685 } 686 if (arkts.isCallExpression(node)) { 687 return this.updateCallExpression(node); 688 } 689 if (arkts.isClassProperty(node) && !!node.key && arkts.isIdentifier(node.key)) { 690 return this.updateClassProperty(node, node.key); 691 } 692 if (arkts.isTSTypeAliasDeclaration(node)) { 693 return this.updateTSTypeAliasDeclaration(node); 694 } 695 if (isStandaloneArrowFunction(node)) { 696 return this.updateStandaloneArrowFunction(node); 697 } 698 if (arkts.isVariableDeclarator(node)) { 699 return this.updateVariableDeclarator(node); 700 } 701 if (arkts.isTSAsExpression(node) && node.expr && arkts.isArrowFunctionExpression(node.expr)) { 702 return this.updateTSAsExpression(node, node.expr); 703 } 704 if (isFunctionProperty(node)) { 705 return this.updateProperty(node, castIdentifier(node.key), castArrowFunctionExpression(node.value)); 706 } 707 if (isThisAttributeAssignment(node) && !!node.right && arkts.isArrowFunctionExpression(node.right)) { 708 const thisAttribute = findThisAttribute(node.left!)!; 709 return this.updateThisAttributeAssignment(node, thisAttribute, node.right); 710 } 711 if (arkts.isEtsScript(node) && this.modified) { 712 factory.createContextTypesImportDeclaration(this.program); 713 } 714 return node; 715 } 716} 717