• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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