idlize
603 строки · 20.9 Кб
1/*
2* Copyright (c) 2022-2024 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 ts from "ohos-typescript"
17import {
18Any,
19assignment,
20createThisFieldAccess,
21dropReadonly,
22Exclamation,
23findDecoratorArgument,
24id,
25orUndefined,
26parameter,
27undefinedValue,
28} from "./ApiUtils"
29import { Importer } from "./Importer"
30import {
31backingField,
32BuilderParamDecorator,
33collect,
34ConsumeDecorator,
35createBlock,
36createNotNullAccessor,
37createNullableAccessor,
38createNullishCoalescing,
39createValueAccessor,
40dropDecorators,
41filterDecorators,
42idTextOrError,
43initializers,
44isBuilderParam,
45isConsume,
46isLink,
47isLocalStorageLink,
48isLocalStorageProp,
49isObjectLink,
50isProp,
51isProvide,
52isState,
53isStorageLink,
54isStorageProp,
55LinkDecorator,
56LocalStorageLinkDecorator,
57LocalStoragePropDecorator,
58LocalStoragePropertyName,
59ObjectLinkDecorator,
60prependDoubleLineMemoComment,
61prependMemoComment,
62PropDecorator,
63ProvideDecorator,
64StateDecorator,
65StorageLinkDecorator,
66StoragePropDecorator,
67SyncedPropertyConstructor,
68WatchDecorator,
69} from "./utils"
70
71export abstract class PropertyTranslator {
72private cachedType: ts.TypeNode | undefined // do not analyze this.property.initializer every time
73constructor(protected property: ts.PropertyDeclaration, protected importer: Importer) { }
74
75get propertyName(): string {
76return idTextOrError(this.property.name)
77}
78get propertyType(): ts.TypeNode {
79let type = this.property.type ?? this.cachedType
80if (!type) this.cachedType = type = this.typeInference(this.property.initializer)
81return type
82}
83private typeInference(initializer?: ts.Expression): ts.TypeNode {
84// TODO: try to analyze this.property.initializer
85return Any()
86}
87
88abstract translateMember(): ts.ClassElement[]
89translateInitializer(): ts.Expression|undefined {
90return undefined
91}
92translateToInitialization(): ts.Statement | undefined {
93return undefined
94}
95translateToUpdate(): ts.Statement | undefined {
96return undefined
97}
98translateToBuildParameter(): ts.ParameterDeclaration | undefined {
99return undefined
100}
101
102createStateOf(type: ts.TypeNode | undefined, ...initializer: ts.Expression[]): ts.Expression {
103return createStateOf(this.importer, type, ...initializer)
104}
105
106translateStateMember(
107property: ts.PropertyDeclaration,
108decoratorName: string,
109initializer: ts.Expression | undefined,
110type: ts.TypeNode | undefined,
111addExclamation: boolean
112): ts.ClassElement[] {
113
114const originalName = idTextOrError(property.name)
115const newName = backingField(originalName)
116
117const field = ts.factory.updatePropertyDeclaration(
118property,
119dropDecorators(property.modifiers, decoratorName, WatchDecorator),
120newName,
121addExclamation ? Exclamation() : undefined,
122type,
123initializer
124)
125
126const getter = this.createStateGetter(originalName, newName)
127const setter = this.createStateSetter(originalName, newName)
128
129return [field, getter, setter]
130}
131
132translatePlainMember(
133property: ts.PropertyDeclaration,
134initializer: ts.Expression | undefined,
135type: ts.TypeNode | undefined,
136decorator?: string,
137memo?: boolean
138): ts.ClassElement[] {
139const originalName = idTextOrError(property.name)
140const newName = backingField(originalName)
141
142const field = ts.factory.updatePropertyDeclaration(
143property,
144dropReadonly(dropDecorators(property.modifiers, decorator)),
145newName,
146initializer ? undefined : ts.factory.createToken(ts.SyntaxKind.ExclamationToken),
147type,
148initializer
149)
150
151const getter = this.createPlainGetter(originalName, newName, type, memo)
152const setter = this.createPlainSetter(originalName, newName, type, memo)
153
154const backingFieldWithMemo = memo ? prependMemoComment(field) : field
155
156// TODO: don't produce any setters for readonly properties
157return [backingFieldWithMemo, getter, setter]
158}
159
160mutableState(type: ts.TypeNode,): ts.TypeNode {
161return ts.factory.createTypeReferenceNode(
162id(this.importer.withRuntimeImport("MutableState")),
163[type]
164)
165}
166
167mutableStateOrUndefined(type: ts.TypeNode, importer: Importer): ts.TypeNode {
168return orUndefined(this.mutableState(type))
169}
170
171
172translateStateWithoutInitializer(
173property: ts.PropertyDeclaration,
174decoratorName: string,
175syncedProperty: boolean = false,
176): ts.ClassElement[] {
177return this.translateStateMember(
178property,
179decoratorName,
180undefined,
181ts.factory.createTypeReferenceNode(
182syncedProperty
183? this.importer.withAdaptorImport("SyncedProperty")
184: this.importer.withRuntimeImport("MutableState"),
185[
186this.propertyType
187]
188),
189true
190)
191}
192
193translateStateWithInitializer(
194property: ts.PropertyDeclaration,
195decorator: string,
196initializer: ts.Expression,
197): ts.ClassElement[] {
198return this.translateStateMember(property, decorator, initializer, undefined, false)
199}
200
201protected createAppStorageState(decoratorName: string): ts.Expression {
202return ts.factory.createCallExpression(
203id(this.importer.withAdaptorImport("AppStorageLinkState")),
204[this.propertyType],
205[
206findDecoratorArgument(filterDecorators(this.property), decoratorName, 0),
207this.property.initializer!
208]
209)
210}
211
212protected createLocalStorageState(decoratorName: string): ts.Expression {
213return ts.factory.createCallExpression(
214id(this.importer.withAdaptorImport("StorageLinkState")),
215[this.propertyType],
216[
217createThisFieldAccess(LocalStoragePropertyName),
218findDecoratorArgument(filterDecorators(this.property), decoratorName, 0),
219this.property.initializer!
220]
221)
222}
223
224private createStateGetter(originalName: string, newName: string): ts.GetAccessorDeclaration {
225return ts.factory.createGetAccessorDeclaration(
226undefined,
227id(originalName),
228[],
229this.propertyType,
230createBlock(
231ts.factory.createReturnStatement(
232createValueAccessor(
233// TODO: issue a message if there is no @Provide.
234ts.factory.createNonNullExpression(
235createThisFieldAccess(newName)
236)
237)
238)
239)
240)
241
242}
243
244private createStateSetter(originalName: string, newName: string, postStatements?: ts.Statement[]) {
245const preStatement =
246(postStatements?.length ?? 0) > 0 ?
247ts.factory.createVariableStatement(
248undefined,
249ts.factory.createVariableDeclarationList(
250[
251ts.factory.createVariableDeclaration(
252id("oldValue"),
253undefined,
254undefined,
255createValueAccessor(
256ts.factory.createNonNullExpression(
257createThisFieldAccess(newName)
258)
259)
260)
261],
262ts.NodeFlags.Const
263)
264)
265: undefined
266
267return ts.factory.createSetAccessorDeclaration(
268undefined,
269id(originalName),
270[
271parameter(
272"value",
273this.propertyType
274)
275],
276ts.factory.createBlock(
277collect(
278preStatement,
279assignment(
280createValueAccessor(
281// TODO: issue a message if there is no @Provide.
282ts.factory.createNonNullExpression(
283createThisFieldAccess(newName)
284)
285),
286ts.factory.createCallExpression(
287id(this.importer.withAdaptorImport("observableProxy")),
288undefined,
289[
290id("value"),
291]
292)
293),
294postStatements
295),
296true
297)
298)
299}
300
301createPlainGetter(originalName: string, newName: string, type: ts.TypeNode | undefined, memo?: boolean): ts.GetAccessorDeclaration {
302const getter = ts.factory.createGetAccessorDeclaration(
303undefined,
304id(originalName),
305[],
306this.propertyType,
307createBlock(
308ts.factory.createReturnStatement(
309createThisFieldAccess(newName)
310)
311)
312)
313return memo ? prependMemoComment(getter) : getter
314}
315
316createPlainSetter(originalName: string, newName: string, type: ts.TypeNode | undefined, memo?: boolean) {
317const param = parameter(
318id("value"),
319type
320)
321
322return ts.factory.createSetAccessorDeclaration(
323undefined,
324id(originalName),
325[
326memo ? prependDoubleLineMemoComment(param) : param
327],
328createBlock(assignToField(newName, id("value")))
329)
330}
331
332translateInitializerOfSyncedProperty(constructorName: SyncedPropertyConstructor, withValue?: ts.Expression): ts.Expression {
333return ts.factory.createCallExpression(
334id(this.importer.withAdaptorImport(constructorName)),
335this.property.type ? [this.property.type] : undefined,
336withValue ? [withValue] : []
337)
338}
339
340translateToUpdateSyncedProperty(withValue: ts.Expression): ts.Statement {
341return ts.factory.createExpressionStatement(
342ts.factory.createCallExpression(
343ts.factory.createPropertyAccessExpression(
344createThisFieldAccess(backingField(this.propertyName)),
345"update"
346),
347undefined,
348[withValue]
349)
350)
351}
352}
353
354
355function createStateOf(importer: Importer, type: ts.TypeNode | undefined, ...initializer: ts.Expression[]): ts.Expression {
356return ts.factory.createCallExpression(
357id(importer.withAdaptorImport("stateOf")),
358type ? [type] : undefined,
359initializer
360)
361}
362
363class State extends PropertyTranslator {
364translateMember(): ts.ClassElement[] {
365return this.translateStateWithoutInitializer(this.property, StateDecorator)
366}
367
368translateToInitialization(): ts.Statement {
369const name = this.propertyName
370return assignToBackingField(
371name,
372this.createStateOf(
373this.propertyType,
374createNullishCoalescing(
375createNullableAccessor(initializers(), name),
376this.property.initializer!
377),
378ts.factory.createThis())
379)
380}
381}
382
383export class ClassState extends PropertyTranslator {
384translateMember(): ts.ClassElement[] {
385// special case: @State in any class other than struct
386return this.translateStateWithInitializer(this.property, StateDecorator, this.createStateOf(
387this.property.type,
388this.property.initializer!,
389undefinedValue(),
390undefinedValue()))
391}
392}
393
394class Prop extends PropertyTranslator {
395translateMember(): ts.ClassElement[] {
396return this.translateStateWithoutInitializer(this.property, PropDecorator, true)
397}
398override translateToInitialization(): ts.Statement {
399return assignToBackingField(
400this.propertyName,
401this.translateInitializerOfSyncedProperty(SyncedPropertyConstructor.propState, this.property.initializer))
402}
403translateToUpdate(): ts.Statement {
404return this.translateToUpdateSyncedProperty(createNullableAccessor(initializers(), this.propertyName))
405}
406translateToBuildParameter(): ts.ParameterDeclaration {
407return translatePropOrObjectLinkToBuildParameter(this.property, this.importer)
408}
409}
410
411class Link extends PropertyTranslator {
412translateMember(): ts.ClassElement[] {
413return this.translateStateWithoutInitializer(this.property, LinkDecorator)
414}
415translateToInitialization(): ts.Statement {
416return translateToInitializationFromInitializers(backingField(this.propertyName))
417}
418}
419
420class ObjectLink extends PropertyTranslator {
421translateMember(): ts.ClassElement[] {
422return this.translateStateWithoutInitializer(this.property, ObjectLinkDecorator, true)
423}
424override translateToInitialization(): ts.Statement {
425return assignToBackingField(
426this.propertyName,
427this.translateInitializerOfSyncedProperty(SyncedPropertyConstructor.objectLink, this.property.initializer))
428}
429translateToUpdate(): ts.Statement {
430return this.translateToUpdateSyncedProperty(createNullableAccessor(initializers(), this.propertyName))
431}
432translateToBuildParameter(): ts.ParameterDeclaration {
433return translatePropOrObjectLinkToBuildParameter(this.property, this.importer)
434}
435}
436
437class Provide extends PropertyTranslator {
438translateMember(): ts.ClassElement[] {
439return this.translateStateWithoutInitializer(this.property, ProvideDecorator)
440}
441translateToInitialization() {
442return translateToInitializationFromInitializers(backingField(this.propertyName))
443}
444}
445
446class Consume extends PropertyTranslator {
447translateMember(): ts.ClassElement[] {
448return this.translateStateWithoutInitializer(this.property, ConsumeDecorator)
449}
450translateToInitialization() {
451return translateToInitializationFromInitializers(backingField(this.propertyName))
452}
453}
454
455class StorageLink extends PropertyTranslator {
456translateMember(): ts.ClassElement[] {
457return this.translateStateWithoutInitializer(this.property, StorageLinkDecorator)
458}
459
460translateToInitialization(): ts.Statement {
461return assignToBackingField(this.propertyName, this.createAppStorageState(StorageLinkDecorator))
462}
463
464}
465class LocalStorageLink extends PropertyTranslator {
466translateMember(): ts.ClassElement[] {
467return this.translateStateWithoutInitializer(this.property, LocalStorageLinkDecorator)
468}
469
470translateToInitialization(): ts.Statement {
471return assignToBackingField(this.propertyName, this.createLocalStorageState(LocalStorageLinkDecorator))
472}
473
474}
475class StorageProp extends PropertyTranslator {
476translateMember(): ts.ClassElement[] {
477return this.translateStateWithoutInitializer(this.property, StoragePropDecorator, true)
478}
479override translateToInitialization(): ts.Statement {
480return assignToBackingField(
481this.propertyName,
482this.translateInitializerOfSyncedProperty(SyncedPropertyConstructor.propState, this.createAppStorageValue()))
483}
484translateToUpdate(): ts.Statement {
485return this.translateToUpdateSyncedProperty(this.createAppStorageValue())
486}
487createAppStorageValue(): ts.Expression {
488return createValueAccessor(this.createAppStorageState(StoragePropDecorator))
489}
490
491}
492class LocalStorageProp extends PropertyTranslator {
493translateMember(): ts.ClassElement[] {
494return this.translateStateWithoutInitializer(this.property, LocalStoragePropDecorator, true)
495}
496override translateToInitialization(): ts.Statement {
497return assignToBackingField(
498this.propertyName,
499this.translateInitializerOfSyncedProperty(SyncedPropertyConstructor.propState, this.createLocalStorageValue()))
500}
501translateToUpdate(): ts.Statement {
502return this.translateToUpdateSyncedProperty(this.createLocalStorageValue())
503}
504createLocalStorageValue(): ts.Expression {
505return createValueAccessor(this.createLocalStorageState(LocalStoragePropDecorator))
506}
507}
508
509export class BuilderParam extends PropertyTranslator {
510constructor(protected property: ts.PropertyDeclaration, protected importer: Importer, private typechecker: ts.TypeChecker) {
511super(property, importer)
512}
513translateMember(): ts.ClassElement[] {
514return this.translatePlainMember(
515this.property,
516undefined,
517this.propertyType,
518BuilderParamDecorator,
519/* memo = */ true
520)
521}
522translateToInitialization(): ts.Statement | undefined {
523return initializePlainProperty(this.propertyName, this.property.initializer)
524}
525}
526
527class PlainProperty extends PropertyTranslator {
528constructor(protected property: ts.PropertyDeclaration, protected importer: Importer, private typechecker: ts.TypeChecker) {
529super(property, importer)
530}
531translateMember(): ts.ClassElement[] {
532return this.translatePlainMember(
533this.property,
534undefined,
535this.propertyType
536)
537}
538translateToInitialization(): ts.Statement | undefined {
539return initializePlainProperty(this.propertyName, this.property.initializer)
540}
541}
542
543function initializePlainProperty(name: string, initializer?: ts.Expression): ts.Statement {
544return initializer
545? assignToBackingField(name, createNullishCoalescing(createNullableAccessor(initializers(), name), initializer))
546: ts.factory.createIfStatement(
547createNullableAccessor(initializers(), name),
548createBlock(assignToBackingField(name, createNullableAccessor(initializers(), name)))
549)
550}
551
552function assignToBackingField(name: string, expression: ts.Expression): ts.Statement {
553return assignToField(backingField(name), expression)
554}
555
556function assignToField(name: string, expression: ts.Expression): ts.Statement {
557return assignment(createThisFieldAccess(name), expression)
558}
559
560function translateToInitializationFromInitializers(name: string): ts.Statement {
561return assignToField(name, createNotNullAccessor(initializers(), name))
562}
563
564function translatePropOrObjectLinkToBuildParameter(property: ts.PropertyDeclaration, importer: Importer): ts.ParameterDeclaration {
565return parameter(
566backingField(idTextOrError(property.name)),
567property.type
568)
569}
570
571export function haveProvidesOrConsumes(translators: PropertyTranslator[]): boolean {
572const providesOrConsumes = translators.filter(it => it instanceof Provide || it instanceof Consume)
573return providesOrConsumes.length > 0
574}
575
576export function classifyProperty(member: ts.ClassElement, importer: Importer, typechecker: ts.TypeChecker): PropertyTranslator | undefined {
577if (!ts.isPropertyDeclaration(member)) return undefined
578if (isState(member)) {
579return new State(member, importer)
580} else if (isStorageProp(member)) {
581return new StorageProp(member, importer)
582} else if (isStorageLink(member)) {
583return new StorageLink(member, importer)
584} else if (isLocalStorageLink(member)) {
585return new LocalStorageLink(member, importer)
586} else if (isLocalStorageProp(member)) {
587return new LocalStorageProp(member, importer)
588} else if (isLink(member)) {
589return new Link(member, importer)
590} else if (isProp(member)) {
591return new Prop(member, importer)
592} else if (isObjectLink(member)) {
593return new ObjectLink(member, importer)
594} else if (isProvide(member)) {
595return new Provide(member, importer)
596} else if (isConsume(member)) {
597return new Consume(member, importer)
598} else if (isBuilderParam(member)) {
599return new BuilderParam(member, importer, typechecker)
600} else {
601return new PlainProperty(member, importer, typechecker)
602}
603}
604