idlize
220 строк · 9.9 Кб
1/*
2* Copyright (c) 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 'typescript'
17import { Language } from "../../util";
18import { DeclarationTable, DeclarationTarget, PrimitiveType } from "../DeclarationTable";
19import { LanguageWriter, Method, NamedMethodSignature, Type } from "../LanguageWriters";
20import { PeerGeneratorConfig } from '../PeerGeneratorConfig';
21import { checkDeclarationTargetMaterialized } from '../Materialized';
22import { ImportsCollector } from '../ImportsCollector';
23import { PeerLibrary } from '../PeerLibrary';
24import { collectDtsImports } from '../DtsImportsGenerator';
25
26function collectAllInterfacesImports(library: PeerLibrary, imports: ImportsCollector) {
27for (const file of library.files)
28file.importFeatures.forEach(it => imports.addFeature(it.feature, it.module))
29}
30
31function printSerializerImports(library: PeerLibrary, writer: LanguageWriter) {
32if (writer.language === Language.TS) {
33const collector = new ImportsCollector()
34collectAllInterfacesImports(library, collector)
35collector.print(writer)
36} else if (writer.language === Language.ARKTS) {
37writer.print(collectDtsImports().trim())
38}
39}
40
41function canSerializeTarget(declaration: ts.ClassDeclaration | ts.InterfaceDeclaration): boolean {
42// we can not generate serializer/deserializer for targets, where
43// type parameters are in signature and some of this parameters has not
44// default value. At all we should not generate even classes with default values,
45// but they are at least compilable.
46// See class TransitionEffect declared at common.d.ts and used at CommonMethod.transition
47return (declaration.typeParameters ?? []).every(it => {
48return it.default !== undefined
49})
50}
51
52function ignoreSerializeTarget(table: DeclarationTable, target: DeclarationTarget): target is PrimitiveType | ts.EnumDeclaration {
53const name = table.computeTargetName(target, false)
54if (PeerGeneratorConfig.ignoreSerialization.includes(name)) return true
55if (target instanceof PrimitiveType) return true
56if (ts.isEnumDeclaration(target)) return true
57if (ts.isFunctionTypeNode(target)) return true
58if (ts.isImportTypeNode(target)) return true
59if (ts.isTemplateLiteralTypeNode(target)) return true
60if (checkDeclarationTargetMaterialized(target)) return true
61return false
62}
63
64class SerializerPrinter {
65constructor(
66private readonly library: PeerLibrary,
67private readonly writer: LanguageWriter,
68) {}
69
70private get table(): DeclarationTable {
71return this.library.declarationTable
72}
73
74private translateSerializerType(name: string, target: DeclarationTarget): string {
75if (target instanceof PrimitiveType) throw new Error("Unexpected")
76if (ts.isInterfaceDeclaration(target) && target.typeParameters != undefined) {
77if (target.typeParameters.length != 1) throw new Error("Unexpected")
78return `${name}<object>`
79} else {
80return name
81}
82}
83
84private generateSerializer(target: ts.ClassDeclaration | ts.InterfaceDeclaration) {
85const name = this.table.computeTargetName(target, false)
86this.table.setCurrentContext(`write${name}()`)
87
88this.writer.writeMethodImplementation(
89new Method(`write${name}`,
90new NamedMethodSignature(Type.Void, [new Type(this.translateSerializerType(name, target))], ["value"])),
91writer => {
92writer.writeStatement(
93writer.makeAssign("valueSerializer", new Type(writer.makeRef("Serializer")), writer.makeThis(), true, false))
94let struct = this.table.targetStruct(target)
95struct.getFields().forEach(it => {
96let field = `value_${it.name}`
97writer.writeStatement(writer.makeAssign(field, undefined, writer.makeString(`value.${it.name}`), true))
98let typeConvertor = this.table.typeConvertor(`value`, it.type!, it.optional)
99typeConvertor.convertorSerialize(`value`, field, writer)
100})
101})
102this.table.setCurrentContext(undefined)
103}
104
105print() {
106const className = "Serializer"
107const superName = `${className}Base`
108let ctorSignature: NamedMethodSignature | undefined = undefined
109switch (this.writer.language) {
110case Language.ARKTS:
111ctorSignature = new NamedMethodSignature(Type.Void, [], [])
112break;
113case Language.CPP:
114ctorSignature = new NamedMethodSignature(Type.Void, [new Type("uint8_t*")], ["data"])
115break;
116case Language.JAVA:
117ctorSignature = new NamedMethodSignature(Type.Void, [], [])
118break;
119}
120let seenNames = new Set<string>()
121printSerializerImports(this.library, this.writer)
122this.writer.writeClass(className, writer => {
123if (ctorSignature) {
124const ctorMethod = new Method(superName, ctorSignature)
125writer.writeConstructorImplementation(className, ctorSignature, writer => {}, ctorMethod)
126}
127for (let declaration of this.table.orderedDependenciesToGenerate) {
128if (ignoreSerializeTarget(this.table, declaration))
129continue
130let name = this.table.computeTargetName(declaration, false)
131if (seenNames.has(name)) continue
132seenNames.add(name)
133if (ts.isInterfaceDeclaration(declaration) || ts.isClassDeclaration(declaration))
134if (canSerializeTarget(declaration))
135this.generateSerializer(declaration)
136}
137if (this.writer.language == Language.JAVA) {
138// TODO: somewhat ugly.
139this.writer.print(`static Serializer createSerializer() { return new Serializer(); }`)
140}
141}, superName)
142}
143}
144
145class DeserializerPrinter {
146constructor(
147private readonly library: PeerLibrary,
148private readonly writer: LanguageWriter,
149) {}
150
151private get table(): DeclarationTable {
152return this.library.declarationTable
153}
154
155private generateDeserializer(target: ts.ClassDeclaration | ts.InterfaceDeclaration) {
156const name = this.table.computeTargetName(target, false)
157this.table.setCurrentContext(`read${name}()`)
158const type = new Type(name)
159this.writer.writeMethodImplementation(new Method(`read${name}`, new NamedMethodSignature(type, [], [])), writer => {
160writer.writeStatement(
161writer.makeAssign("valueDeserializer", new Type(writer.makeRef("Deserializer")), writer.makeThis(), true, false))
162// using list initialization to prevent uninitialized value errors
163writer.writeStatement(writer.makeObjectDeclare("value", type, this.table.targetStruct(target).getFields()))
164if (ts.isInterfaceDeclaration(target) || ts.isClassDeclaration(target)) {
165let struct = this.table.targetStruct(target)
166struct.getFields().forEach(it => {
167let typeConvertor = this.table.typeConvertor(`value`, it.type!, it.optional)
168writer.writeStatement(typeConvertor.convertorDeserialize(`value`, `value.${it.name}`, writer))
169})
170} else {
171if (writer.language === Language.CPP) {
172let typeConvertor = this.table.typeConvertor("value", target, false)
173writer.writeStatement(typeConvertor.convertorDeserialize(`value`, `value`, writer))
174}
175}
176writer.writeStatement(writer.makeReturn(
177writer.makeCast(writer.makeString("value"), new Type(name))))
178})
179this.table.setCurrentContext(undefined)
180}
181
182print() {
183const className = "Deserializer"
184const superName = `${className}Base`
185let ctorSignature = this.writer.language == Language.CPP
186? new NamedMethodSignature(Type.Void, [new Type("uint8_t*"), Type.Int32], ["data", "length"])
187: undefined
188printSerializerImports(this.library, this.writer)
189this.writer.writeClass(className, writer => {
190if (ctorSignature) {
191const ctorMethod = new Method(`${className}Base`, ctorSignature)
192writer.writeConstructorImplementation(className, ctorSignature, writer => {}, ctorMethod)
193}
194const seenNames = new Set<string>()
195for (let declaration of this.table.orderedDependenciesToGenerate) {
196if (ignoreSerializeTarget(this.table, declaration))
197continue
198
199let name = this.table.computeTargetName(declaration, false)
200if (seenNames.has(name)) continue
201seenNames.add(name)
202
203if (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) {
204if (canSerializeTarget(declaration))
205this.generateDeserializer(declaration)
206}
207}
208}, superName)
209}
210}
211
212export function writeSerializer(library: PeerLibrary, writer: LanguageWriter) {
213const printer = new SerializerPrinter(library, writer)
214printer.print()
215}
216
217export function writeDeserializer(library: PeerLibrary, writer: LanguageWriter) {
218const printer = new DeserializerPrinter(library, writer)
219printer.print()
220}