1/* 2 * Copyright (c) 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'; 17 18import { backingField, expectName } from '../../common/arkts-utils'; 19import { PropertyTranslator } from './base'; 20import { GetterSetter, InitializerConstructor } from './types'; 21import { 22 DecoratorNames, 23 generateToRecord, 24 createGetter, 25 createSetter2, 26 generateThisBacking, 27 generateGetOrSetCall, 28 judgeIfAddWatchFunc, 29} from './utils'; 30import { createOptionalClassProperty } from '../utils'; 31 32function getStorageLinkValueStr(node: arkts.AstNode): string | undefined { 33 if (!arkts.isClassProperty(node) || !node.value) return undefined; 34 35 return arkts.isStringLiteral(node.value) ? node.value.str : undefined; 36} 37 38function getStorageLinkAnnotationValue(anno: arkts.AnnotationUsage): string | undefined { 39 const isStorageLinkAnnotation: boolean = 40 !!anno.expr && arkts.isIdentifier(anno.expr) && anno.expr.name === DecoratorNames.STORAGE_LINK; 41 42 if (isStorageLinkAnnotation && anno.properties.length === 1) { 43 return getStorageLinkValueStr(anno.properties.at(0)!); 44 } 45 return undefined; 46} 47 48function getStorageLinkValueInAnnotation(node: arkts.ClassProperty): string | undefined { 49 const annotations: readonly arkts.AnnotationUsage[] = node.annotations; 50 51 for (let i = 0; i < annotations.length; i++) { 52 const anno: arkts.AnnotationUsage = annotations[i]; 53 const str: string | undefined = getStorageLinkAnnotationValue(anno); 54 if (!!str) { 55 return str; 56 } 57 } 58 return undefined; 59} 60 61export class StorageLinkTranslator extends PropertyTranslator implements InitializerConstructor, GetterSetter { 62 translateMember(): arkts.AstNode[] { 63 const originalName: string = expectName(this.property.key); 64 const newName: string = backingField(originalName); 65 66 this.cacheTranslatedInitializer(newName, originalName); // TODO: need to release cache after some point... 67 return this.translateWithoutInitializer(newName, originalName); 68 } 69 70 cacheTranslatedInitializer(newName: string, originalName: string): void { 71 const currentStructInfo: arkts.StructInfo = arkts.GlobalInfo.getInfoInstance().getStructInfo(this.structName); 72 const initializeStruct: arkts.AstNode = this.generateInitializeStruct(newName, originalName); 73 currentStructInfo.initializeBody.push(initializeStruct); 74 75 if (currentStructInfo.isReusable) { 76 const toRecord = generateToRecord(newName, originalName); 77 currentStructInfo.toRecordBody.push(toRecord); 78 } 79 80 arkts.GlobalInfo.getInfoInstance().setStructInfo(this.structName, currentStructInfo); 81 } 82 83 generateInitializeStruct(newName: string, originalName: string): arkts.AstNode { 84 const storageLinkValueStr: string | undefined = getStorageLinkValueInAnnotation(this.property); 85 if (!storageLinkValueStr) { 86 throw new Error('StorageLink required only one value!!'); // TODO: replace this with proper error message. 87 } 88 89 const args: arkts.Expression[] = [ 90 arkts.factory.createStringLiteral(storageLinkValueStr), 91 arkts.factory.create1StringLiteral(originalName), 92 this.property.value ?? arkts.factory.createUndefinedLiteral(), 93 ]; 94 judgeIfAddWatchFunc(args, this.property); 95 96 const newClass = arkts.factory.createETSNewClassInstanceExpression( 97 arkts.factory.createTypeReference( 98 arkts.factory.createTypeReferencePart( 99 arkts.factory.createIdentifier('StorageLinkDecoratedVariable'), 100 arkts.factory.createTSTypeParameterInstantiation( 101 this.property.typeAnnotation ? [this.property.typeAnnotation] : [] 102 ) 103 ) 104 ), 105 args 106 ); 107 108 return arkts.factory.createAssignmentExpression( 109 arkts.factory.createMemberExpression( 110 arkts.factory.createThisExpression(), 111 arkts.factory.createIdentifier(newName), 112 arkts.Es2pandaMemberExpressionKind.MEMBER_EXPRESSION_KIND_PROPERTY_ACCESS, 113 false, 114 false 115 ), 116 arkts.Es2pandaTokenType.TOKEN_TYPE_PUNCTUATOR_SUBSTITUTION, 117 newClass 118 ); 119 } 120 121 translateWithoutInitializer(newName: string, originalName: string): arkts.AstNode[] { 122 const field = createOptionalClassProperty( 123 newName, 124 this.property, 125 'StorageLinkDecoratedVariable', 126 arkts.Es2pandaModifierFlags.MODIFIER_FLAGS_PRIVATE 127 ); 128 const thisValue: arkts.Expression = generateThisBacking(newName, false, true); 129 const thisGet: arkts.CallExpression = generateGetOrSetCall(thisValue, 'get'); 130 const thisSet: arkts.ExpressionStatement = arkts.factory.createExpressionStatement( 131 generateGetOrSetCall(thisValue, 'set') 132 ); 133 const getter: arkts.MethodDefinition = this.translateGetter( 134 originalName, 135 this.property.typeAnnotation, 136 thisGet 137 ); 138 const setter: arkts.MethodDefinition = this.translateSetter( 139 originalName, 140 this.property.typeAnnotation, 141 thisSet 142 ); 143 return [field, getter, setter]; 144 } 145 146 translateGetter( 147 originalName: string, 148 typeAnnotation: arkts.TypeNode | undefined, 149 returnValue: arkts.Expression 150 ): arkts.MethodDefinition { 151 return createGetter(originalName, typeAnnotation, returnValue); 152 } 153 154 translateSetter( 155 originalName: string, 156 typeAnnotation: arkts.TypeNode | undefined, 157 statement: arkts.AstNode 158 ): arkts.MethodDefinition { 159 return createSetter2(originalName, typeAnnotation, statement); 160 } 161} 162