• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022 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 * as jshelpers from "../jshelpers";
18import { PandaGen } from "../pandagen";
19import { TypeChecker } from "../typeChecker";
20import { TypeRecorder } from "../typeRecorder";
21import {
22    Literal,
23    LiteralBuffer,
24    LiteralTag
25} from "./literal";
26
27export enum PrimitiveType {
28    ANY,
29    NUMBER,
30    BOOLEAN,
31    VOID,
32    STRING,
33    SYMBOL,
34    NULL,
35    UNDEFINED,
36    INT,
37    _LENGTH = 50
38}
39
40export enum L2Type {
41    _COUNTER,
42    CLASS,
43    CLASSINST,
44    FUNCTION,
45    UNION,
46    ARRAY,
47    OBJECT,
48    EXTERNAL,
49    INTERFACE
50}
51
52export enum ModifierAbstract {
53    NONABSTRACT,
54    ABSTRACT
55}
56
57export enum ModifierStatic {
58    NONSTATIC,
59    STATIC
60}
61
62export enum ModifierReadonly {
63    NONREADONLY,
64    READONLY
65}
66
67export enum AccessFlag {
68    PUBLIC,
69    PRIVATE,
70    PROTECTED
71}
72
73type ClassMemberFunction = ts.MethodDeclaration | ts.ConstructorDeclaration | ts.GetAccessorDeclaration | ts.SetAccessorDeclaration;
74
75export abstract class BaseType {
76
77    abstract transfer2LiteralBuffer(): LiteralBuffer;
78    protected typeChecker = TypeChecker.getInstance();
79    protected typeRecorder = TypeRecorder.getInstance();
80
81    protected addCurrentType(node: ts.Node, index: number) {
82        this.typeRecorder.addType2Index(node, index);
83    }
84
85    protected setVariable2Type(variableNode: ts.Node, index: number) {
86        this.typeRecorder.setVariable2Type(variableNode, index);
87    }
88
89    protected tryGetTypeIndex(typeNode: ts.Node) {
90        return this.typeRecorder.tryGetTypeIndex(typeNode);
91    }
92
93    protected getOrCreateRecordForDeclNode(typeNode: ts.Node, variableNode?: ts.Node) {
94        return this.typeChecker.getOrCreateRecordForDeclNode(typeNode, variableNode);
95    }
96
97    protected getOrCreateRecordForTypeNode(typeNode: ts.TypeNode | undefined, variableNode?: ts.Node) {
98        return this.typeChecker.getOrCreateRecordForTypeNode(typeNode, variableNode);
99    }
100
101    protected getIndexFromTypeArrayBuffer(type: BaseType): number {
102        return PandaGen.appendTypeArrayBuffer(type);
103    }
104
105    protected setTypeArrayBuffer(type: BaseType, index: number) {
106        PandaGen.setTypeArrayBuffer(type, index);
107    }
108
109}
110
111export class PlaceHolderType extends BaseType {
112    transfer2LiteralBuffer(): LiteralBuffer {
113        return new LiteralBuffer();
114    }
115}
116
117export class TypeSummary extends BaseType {
118    preservedIndex: number = 0;
119    userDefinedClassNum: number = 0;
120    anonymousRedirect: Array<string> = new Array<string>();
121    constructor() {
122        super();
123        this.preservedIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType());
124    }
125
126    public setInfo(userDefinedClassNum: number, anonymousRedirect: Array<string>) {
127        this.userDefinedClassNum = userDefinedClassNum;
128        this.anonymousRedirect = anonymousRedirect;
129        this.setTypeArrayBuffer(this, this.preservedIndex);
130    }
131
132    transfer2LiteralBuffer(): LiteralBuffer {
133        let countBuf = new LiteralBuffer();
134        let summaryLiterals: Array<Literal> = new Array<Literal>();
135        summaryLiterals.push(new Literal(LiteralTag.INTEGER, L2Type._COUNTER));
136        summaryLiterals.push(new Literal(LiteralTag.INTEGER, this.userDefinedClassNum));
137        summaryLiterals.push(new Literal(LiteralTag.INTEGER, this.anonymousRedirect.length));
138        for (let element of this.anonymousRedirect) {
139            summaryLiterals.push(new Literal(LiteralTag.STRING, element));
140        }
141        countBuf.addLiterals(...summaryLiterals);
142        return countBuf;
143    }
144}
145
146export class ClassType extends BaseType {
147    modifier: number = ModifierAbstract.NONABSTRACT; // 0 -> unabstract, 1 -> abstract;
148    extendsHeritage: number = PrimitiveType.ANY;
149    implementsHeritages: Array<number> = new Array<number>();
150    // fileds Array: [typeIndex] [public -> 0, private -> 1, protected -> 2] [readonly -> 1]
151    staticFields: Map<string, Array<number>> = new Map<string, Array<number>>();
152    staticMethods: Map<string, number> = new Map<string, number>();
153    fields: Map<string, Array<number>> = new Map<string, Array<number>>();
154    methods: Map<string, number> = new Map<string, number>();
155    typeIndex: number;
156    shiftedTypeIndex: number;
157
158    constructor(classNode: ts.ClassDeclaration | ts.ClassExpression) {
159        super();
160        this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType());
161        this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH;
162        // record type before its initialization, so its index can be recorded
163        // in case there's recursive reference of this type
164        this.addCurrentType(classNode, this.shiftedTypeIndex);
165        this.fillInModifiers(classNode);
166        this.fillInHeritages(classNode);
167        this.fillInFieldsAndMethods(classNode);
168        this.setTypeArrayBuffer(this, this.typeIndex);
169    }
170
171    private fillInModifiers(node: ts.ClassDeclaration | ts.ClassExpression) {
172        if (node.modifiers) {
173            for (let modifier of node.modifiers) {
174                switch (modifier.kind) {
175                    case ts.SyntaxKind.AbstractKeyword: {
176                        this.modifier = ModifierAbstract.ABSTRACT;
177                        break;
178                    }
179                    default: {
180                        break;
181                    }
182                }
183            }
184        }
185    }
186
187    private fillInHeritages(node: ts.ClassDeclaration | ts.ClassExpression) {
188        if (node.heritageClauses) {
189            for (let heritage of node.heritageClauses) {
190                let heritageFullName = heritage.getText();
191                for (let heritageType of heritage.types) {
192                    let heritageIdentifier = <ts.Identifier>heritageType.expression;
193                    let heritageTypeIndex = this.getOrCreateRecordForDeclNode(heritageIdentifier, heritageIdentifier);
194                    if (heritageFullName.startsWith("extends ")) {
195                        this.extendsHeritage = heritageTypeIndex;
196                    } else if (heritageFullName.startsWith("implements ")) {
197                        this.implementsHeritages.push(heritageTypeIndex);
198                    }
199                }
200            }
201        }
202    }
203
204    private fillInFields(member: ts.PropertyDeclaration) {
205        let fieldName = jshelpers.getTextOfIdentifierOrLiteral(member.name);
206        let fieldInfo = Array<number>(PrimitiveType.ANY, AccessFlag.PUBLIC, ModifierReadonly.NONREADONLY);
207        let isStatic: boolean = false;
208        if (member.modifiers) {
209            for (let modifier of member.modifiers) {
210                switch (modifier.kind) {
211                    case ts.SyntaxKind.StaticKeyword: {
212                        isStatic = true;
213                        break;
214                    }
215                    case ts.SyntaxKind.PrivateKeyword: {
216                        fieldInfo[1] = AccessFlag.PRIVATE;
217                        break;
218                    }
219                    case ts.SyntaxKind.ProtectedKeyword: {
220                        fieldInfo[1] = AccessFlag.PROTECTED;
221                        break;
222                    }
223                    case ts.SyntaxKind.ReadonlyKeyword: {
224                        fieldInfo[2] = ModifierReadonly.READONLY;
225                        break;
226                    }
227                    default: {
228                        break;
229                    }
230                }
231            }
232        }
233
234        let typeNode = member.type
235        let memberName = member.name
236        fieldInfo[0] = this.getOrCreateRecordForTypeNode(typeNode, memberName);
237
238        if (isStatic) {
239            this.staticFields.set(fieldName, fieldInfo);
240        } else {
241            this.fields.set(fieldName, fieldInfo);
242        }
243    }
244
245    private fillInMethods(member: ClassMemberFunction) {
246        /**
247         * a method like declaration in a new class must be a new type,
248         * create this type and add it into typeRecorder
249         */
250        let variableNode = member.name ? member.name : undefined;
251        let funcType = new FunctionType(<ts.FunctionLikeDeclaration>member);
252        if (variableNode) {
253            this.setVariable2Type(variableNode, funcType.shiftedTypeIndex);
254        }
255
256        // Then, get the typeIndex and fill in the methods array
257        let typeIndex = this.tryGetTypeIndex(member);
258        let funcModifier = funcType.getModifier();
259        if (funcModifier) {
260            this.staticMethods.set(funcType.getFunctionName(), typeIndex!);
261        } else {
262            this.methods.set(funcType.getFunctionName(), typeIndex!);
263        }
264    }
265
266    private fillInFieldsAndMethods(node: ts.ClassDeclaration | ts.ClassExpression) {
267        if (node.members) {
268            for (let member of node.members) {
269                switch (member.kind) {
270                    case ts.SyntaxKind.MethodDeclaration:
271                    case ts.SyntaxKind.Constructor:
272                    case ts.SyntaxKind.GetAccessor:
273                    case ts.SyntaxKind.SetAccessor: {
274                        this.fillInMethods(<ClassMemberFunction>member);
275                        break;
276                    }
277                    case ts.SyntaxKind.PropertyDeclaration: {
278                        this.fillInFields(<ts.PropertyDeclaration>member);
279                        break;
280                    }
281                    default:
282                        break;
283                }
284            }
285        }
286    }
287
288    transfer2LiteralBuffer() {
289        let classTypeBuf = new LiteralBuffer();
290        let classTypeLiterals: Array<Literal> = new Array<Literal>();
291        // the first element is to determine the L2 type
292        classTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.CLASS));
293        classTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.modifier));
294
295        classTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.extendsHeritage));
296        classTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.implementsHeritages.length));
297        this.implementsHeritages.forEach(heritage => {
298            classTypeLiterals.push(new Literal(LiteralTag.INTEGER, heritage));
299        });
300
301        // record unstatic fields and methods
302        this.transferFields2Literal(classTypeLiterals, false);
303        this.transferMethods2Literal(classTypeLiterals, false);
304
305        // record static methods and fields;
306        this.transferFields2Literal(classTypeLiterals, true);
307        this.transferMethods2Literal(classTypeLiterals, true);
308
309        classTypeBuf.addLiterals(...classTypeLiterals);
310        return classTypeBuf;
311    }
312
313    private transferFields2Literal(classTypeLiterals: Array<Literal>, isStatic: boolean) {
314        let transferredTarget: Map<string, Array<number>> = isStatic ? this.staticFields : this.fields;
315
316        classTypeLiterals.push(new Literal(LiteralTag.INTEGER, transferredTarget.size));
317        transferredTarget.forEach((typeInfo, name) => {
318            classTypeLiterals.push(new Literal(LiteralTag.STRING, name));
319            classTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[0])); // typeIndex
320            classTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[1])); // accessFlag
321            classTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[2])); // readonly
322        });
323    }
324
325    private transferMethods2Literal(classTypeLiterals: Array<Literal>, isStatic: boolean) {
326        let transferredTarget: Map<string, number> = isStatic ? this.staticMethods : this.methods;
327
328        classTypeLiterals.push(new Literal(LiteralTag.INTEGER, transferredTarget.size));
329        transferredTarget.forEach((typeInfo, name) => {
330            classTypeLiterals.push(new Literal(LiteralTag.STRING, name));
331            classTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo));
332        });
333    }
334}
335
336export class ClassInstType extends BaseType {
337    shiftedReferredClassIndex: number; // the referred class in the type system;
338    typeIndex: number;
339    shiftedTypeIndex: number;
340    constructor(referredClassIndex: number) {
341        super();
342        this.shiftedReferredClassIndex = referredClassIndex;
343        this.typeIndex = this.getIndexFromTypeArrayBuffer(this);
344        this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH;
345        this.typeRecorder.setClass2InstanceMap(this.shiftedReferredClassIndex, this.shiftedTypeIndex);
346    }
347
348    transfer2LiteralBuffer(): LiteralBuffer {
349        let classInstBuf = new LiteralBuffer();
350        let classInstLiterals: Array<Literal> = new Array<Literal>();
351
352        classInstLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.CLASSINST));
353        classInstLiterals.push(new Literal(LiteralTag.INTEGER, this.shiftedReferredClassIndex));
354        classInstBuf.addLiterals(...classInstLiterals);
355
356        return classInstBuf;
357    }
358}
359
360export class FunctionType extends BaseType {
361    name: string = '';
362    accessFlag: number = AccessFlag.PUBLIC; // 0 -> public -> 0, private -> 1, protected -> 2
363    modifierStatic: number = ModifierStatic.NONSTATIC; // 0 -> unstatic, 1 -> static
364    parameters: Array<number> = new Array<number>();
365    returnType: number = PrimitiveType.ANY;
366    typeIndex: number;
367    shiftedTypeIndex: number;
368
369    constructor(funcNode: ts.FunctionLikeDeclaration | ts.MethodSignature) {
370        super();
371        this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType());
372        this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH;
373        // record type before its initialization, so its index can be recorded
374        // in case there's recursive reference of this type
375        this.addCurrentType(funcNode, this.shiftedTypeIndex);
376
377        if (funcNode.name) {
378            this.name = jshelpers.getTextOfIdentifierOrLiteral(funcNode.name);
379        } else {
380            this.name = "constructor";
381        }
382        this.fillInModifiers(funcNode);
383        this.fillInParameters(funcNode);
384        this.fillInReturn(funcNode);
385        this.setTypeArrayBuffer(this, this.typeIndex);
386    }
387
388    public getFunctionName() {
389        return this.name;
390    }
391
392    private fillInModifiers(node: ts.FunctionLikeDeclaration | ts.MethodSignature) {
393        if (node.modifiers) {
394            for (let modifier of node.modifiers) {
395                switch (modifier.kind) {
396                    case ts.SyntaxKind.PrivateKeyword: {
397                        this.accessFlag = AccessFlag.PRIVATE;
398                        break;
399                    }
400                    case ts.SyntaxKind.ProtectedKeyword: {
401                        this.accessFlag = AccessFlag.PROTECTED;
402                        break;
403                    }
404                    case ts.SyntaxKind.StaticKeyword: {
405                        this.modifierStatic = ModifierStatic.STATIC;
406                        break;
407                    }
408                    default:
409                        break;
410                }
411            }
412        }
413    }
414
415    private fillInParameters(node: ts.FunctionLikeDeclaration | ts.MethodSignature) {
416        if (node.parameters) {
417            for (let parameter of node.parameters) {
418                let typeNode = parameter.type;
419                let variableNode = parameter.name;
420                let typeIndex = this.getOrCreateRecordForTypeNode(typeNode, variableNode);
421                this.parameters.push(typeIndex);
422            }
423        }
424    }
425
426    private fillInReturn(node: ts.FunctionLikeDeclaration | ts.MethodSignature) {
427        let typeNode = node.type;
428        let typeIndex = this.getOrCreateRecordForTypeNode(typeNode, typeNode);
429        this.returnType = typeIndex;
430    }
431
432    getModifier() {
433        return this.modifierStatic;
434    }
435
436    transfer2LiteralBuffer(): LiteralBuffer {
437        let funcTypeBuf = new LiteralBuffer();
438        let funcTypeLiterals: Array<Literal> = new Array<Literal>();
439        funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.FUNCTION));
440        funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.accessFlag));
441        funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.modifierStatic));
442        funcTypeLiterals.push(new Literal(LiteralTag.STRING, this.name));
443        funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.parameters.length));
444        this.parameters.forEach((type) => {
445            funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, type));
446        });
447
448        funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.returnType));
449        funcTypeBuf.addLiterals(...funcTypeLiterals);
450        return funcTypeBuf;
451    }
452}
453
454export class ExternalType extends BaseType {
455    fullRedirectNath: string;
456    typeIndex: number;
457    shiftedTypeIndex: number;
458
459    constructor(importName: string, redirectPath: string) {
460        super();
461        this.fullRedirectNath = `#${importName}#${redirectPath}`;
462        this.typeIndex = this.getIndexFromTypeArrayBuffer(this);
463        this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH;
464    }
465
466    transfer2LiteralBuffer(): LiteralBuffer {
467        let ImpTypeBuf = new LiteralBuffer();
468        let ImpTypeLiterals: Array<Literal> = new Array<Literal>();
469        ImpTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.EXTERNAL));
470        ImpTypeLiterals.push(new Literal(LiteralTag.STRING, this.fullRedirectNath));
471        ImpTypeBuf.addLiterals(...ImpTypeLiterals);
472        return ImpTypeBuf;
473    }
474}
475
476export class UnionType extends BaseType {
477    unionedTypeArray: Array<number> = [];
478    typeIndex: number = PrimitiveType.ANY;
479    shiftedTypeIndex: number = PrimitiveType.ANY;
480
481    constructor(typeNode: ts.Node) {
482        super();
483        this.setOrReadFromArrayRecord(typeNode);
484    }
485
486    setOrReadFromArrayRecord(typeNode: ts.Node) {
487        let unionStr = typeNode.getText();
488        if (this.hasUnionTypeMapping(unionStr)) {
489            this.shiftedTypeIndex = this.getFromUnionTypeMap(unionStr)!;
490            return;
491        }
492        this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType());
493        this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH;
494        this.fillInUnionArray(typeNode, this.unionedTypeArray);
495        this.setUnionTypeMap(unionStr, this.shiftedTypeIndex);
496        this.setTypeArrayBuffer(this, this.typeIndex);
497    }
498
499    hasUnionTypeMapping(unionStr: string) {
500        return this.typeRecorder.hasUnionTypeMapping(unionStr);
501    }
502
503    getFromUnionTypeMap(unionStr: string) {
504        return this.typeRecorder.getFromUnionTypeMap(unionStr);
505    }
506
507    setUnionTypeMap(unionStr: string, shiftedTypeIndex: number) {
508        return this.typeRecorder.setUnionTypeMap(unionStr, shiftedTypeIndex);
509    }
510
511    fillInUnionArray(typeNode: ts.Node, unionedTypeArray: Array<number>) {
512        for (let element of (<ts.UnionType><any>typeNode).types) {
513            let elementNode = <ts.TypeNode><any>element;
514            let typeIndex = this.getOrCreateRecordForTypeNode(elementNode, elementNode);
515            unionedTypeArray.push(typeIndex!);
516        }
517    }
518
519    transfer2LiteralBuffer(): LiteralBuffer {
520        let UnionTypeBuf = new LiteralBuffer();
521        let UnionTypeLiterals: Array<Literal> = new Array<Literal>();
522        UnionTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.UNION));
523        UnionTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.unionedTypeArray.length));
524        for (let type of this.unionedTypeArray) {
525            UnionTypeLiterals.push(new Literal(LiteralTag.INTEGER, type));
526        }
527        UnionTypeBuf.addLiterals(...UnionTypeLiterals);
528        return UnionTypeBuf;
529    }
530}
531
532export class ArrayType extends BaseType {
533    referedTypeIndex: number = PrimitiveType.ANY;
534    typeIndex: number = PrimitiveType.ANY;
535    shiftedTypeIndex: number = PrimitiveType.ANY;
536    constructor(typeNode: ts.Node) {
537        super();
538        let elementNode = (<ts.ArrayTypeNode><any>typeNode).elementType;
539        this.referedTypeIndex = this.getOrCreateRecordForTypeNode(elementNode, elementNode);
540        this.setOrReadFromArrayRecord();
541    }
542
543    setOrReadFromArrayRecord() {
544        if (this.hasArrayTypeMapping(this.referedTypeIndex)) {
545            this.shiftedTypeIndex = this.getFromArrayTypeMap(this.referedTypeIndex)!;
546        } else {
547            this.typeIndex = this.getIndexFromTypeArrayBuffer(this);
548            this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH;
549            this.setTypeArrayBuffer(this, this.typeIndex);
550            this.setArrayTypeMap(this.referedTypeIndex, this.shiftedTypeIndex);
551        }
552    }
553
554    hasArrayTypeMapping(referedTypeIndex: number) {
555        return this.typeRecorder.hasArrayTypeMapping(referedTypeIndex);
556    }
557
558    getFromArrayTypeMap(referedTypeIndex: number) {
559        return this.typeRecorder.getFromArrayTypeMap(referedTypeIndex);
560    }
561
562    setArrayTypeMap(referedTypeIndex: number, shiftedTypeIndex: number) {
563        return this.typeRecorder.setArrayTypeMap(referedTypeIndex, shiftedTypeIndex);
564    }
565
566    transfer2LiteralBuffer(): LiteralBuffer {
567        let arrayBuf = new LiteralBuffer();
568        let arrayLiterals: Array<Literal> = new Array<Literal>();
569        arrayLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.ARRAY));
570        arrayLiterals.push(new Literal(LiteralTag.INTEGER, this.referedTypeIndex));
571        arrayBuf.addLiterals(...arrayLiterals);
572        return arrayBuf;
573    }
574}
575
576export class ObjectType extends BaseType {
577    private properties: Map<string, number> = new Map<string, number>();
578    typeIndex: number = PrimitiveType.ANY;
579    shiftedTypeIndex: number = PrimitiveType.ANY;
580
581    constructor(objNode: ts.TypeLiteralNode) {
582        super();
583        this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType());
584        this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH;
585        this.fillInMembers(objNode);
586        this.setTypeArrayBuffer(this, this.typeIndex);
587    }
588
589    fillInMembers(objNode: ts.TypeLiteralNode) {
590        for (let member of objNode.members) {
591            let propertySig = <ts.PropertySignature>member;
592            let name = member.name ? member.name.getText() : "#undefined";
593            let typeIndex = this.getOrCreateRecordForTypeNode(propertySig.type, member.name);
594            this.properties.set(name, typeIndex);
595        }
596    }
597
598    transfer2LiteralBuffer(): LiteralBuffer {
599        let objTypeBuf = new LiteralBuffer();
600        let objLiterals: Array<Literal> = new Array<Literal>();
601        objLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.OBJECT));
602        objLiterals.push(new Literal(LiteralTag.INTEGER, this.properties.size));
603        this.properties.forEach((typeIndex, name) => {
604            objLiterals.push(new Literal(LiteralTag.STRING, name));
605            objLiterals.push(new Literal(LiteralTag.INTEGER, typeIndex));
606        });
607        objTypeBuf.addLiterals(...objLiterals);
608        return objTypeBuf;
609    }
610}
611
612export class InterfaceType extends BaseType {
613    heritages: Array<number> = new Array<number>();
614    // fileds Array: [typeIndex] [public -> 0, private -> 1, protected -> 2] [readonly -> 1]
615    fields: Map<string, Array<number>> = new Map<string, Array<number>>();
616    methods: Array<number> = new Array<number>();
617    typeIndex: number;
618    shiftedTypeIndex: number;
619
620    constructor(interfaceNode: ts.InterfaceDeclaration) {
621        super();
622        this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType());
623        this.shiftedTypeIndex = this.typeIndex + PrimitiveType._LENGTH;
624        // record type before its initialization, so its index can be recorded
625        // in case there's recursive reference of this type
626        this.addCurrentType(interfaceNode, this.shiftedTypeIndex);
627        this.fillInHeritages(interfaceNode);
628        this.fillInFieldsAndMethods(interfaceNode);
629        this.setTypeArrayBuffer(this, this.typeIndex);
630    }
631
632    private fillInHeritages(node: ts.InterfaceDeclaration) {
633        if (node.heritageClauses) {
634            for (let heritage of node.heritageClauses) {
635                for (let heritageType of heritage.types) {
636                    let heritageIdentifier = <ts.Identifier>heritageType.expression;
637                    let heritageTypeIndex = this.getOrCreateRecordForDeclNode(heritageIdentifier, heritageIdentifier);
638                    this.heritages.push(heritageTypeIndex);
639                }
640            }
641        }
642    }
643
644    private fillInFields(member: ts.PropertySignature) {
645        let fieldName = jshelpers.getTextOfIdentifierOrLiteral(member.name);
646        let fieldInfo = Array<number>(PrimitiveType.ANY, AccessFlag.PUBLIC, ModifierReadonly.NONREADONLY);
647        if (member.modifiers) {
648            for (let modifier of member.modifiers) {
649                switch (modifier.kind) {
650                    case ts.SyntaxKind.PrivateKeyword: {
651                        fieldInfo[1] = AccessFlag.PRIVATE;
652                        break;
653                    }
654                    case ts.SyntaxKind.ProtectedKeyword: {
655                        fieldInfo[1] = AccessFlag.PROTECTED;
656                        break;
657                    }
658                    case ts.SyntaxKind.ReadonlyKeyword: {
659                        fieldInfo[2] = ModifierReadonly.READONLY;
660                        break;
661                    }
662                    default:
663                        break;
664                }
665            }
666        }
667        let typeNode = member.type;
668        let memberName = member.name;
669        fieldInfo[0] = this.getOrCreateRecordForTypeNode(typeNode, memberName);
670        this.fields.set(fieldName, fieldInfo);
671    }
672
673    private fillInMethods(member: ts.MethodSignature) {
674        /**
675         * a method like declaration in a new class must be a new type,
676         * create this type and add it into typeRecorder
677         */
678        let variableNode = member.name ? member.name : undefined;
679        let funcType = new FunctionType(<ts.MethodSignature>member);
680        if (variableNode) {
681            this.setVariable2Type(variableNode, funcType.shiftedTypeIndex);
682        }
683        // Then, get the typeIndex and fill in the methods array
684        let typeIndex = this.tryGetTypeIndex(member);
685        this.methods.push(typeIndex!);
686    }
687
688    private fillInFieldsAndMethods(node: ts.InterfaceDeclaration) {
689        if (node.members) {
690            for (let member of node.members) {
691                switch (member.kind) {
692                    case ts.SyntaxKind.MethodSignature: {
693                        this.fillInMethods(<ts.MethodSignature>member);
694                        break;
695                    }
696                    case ts.SyntaxKind.PropertySignature: {
697                        this.fillInFields(<ts.PropertySignature>member);
698                        break;
699                    }
700                    default:
701                        break;
702                }
703            }
704        }
705    }
706
707    transfer2LiteralBuffer() {
708        let interfaceTypeBuf = new LiteralBuffer();
709        let interfaceTypeLiterals: Array<Literal> = new Array<Literal>();
710        // the first element is to determine the L2 type
711        interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.INTERFACE));
712
713        interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.heritages.length));
714        this.heritages.forEach(heritage => {
715            interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, heritage));
716        });
717
718        // record fields and methods
719        this.transferFields2Literal(interfaceTypeLiterals);
720        this.transferMethods2Literal(interfaceTypeLiterals);
721
722        interfaceTypeBuf.addLiterals(...interfaceTypeLiterals);
723        return interfaceTypeBuf;
724    }
725
726    private transferFields2Literal(interfaceTypeLiterals: Array<Literal>) {
727        let transferredTarget: Map<string, Array<number>> = this.fields;
728
729        interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, transferredTarget.size));
730        transferredTarget.forEach((typeInfo, name) => {
731            interfaceTypeLiterals.push(new Literal(LiteralTag.STRING, name));
732            interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[0])); // typeIndex
733            interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[1])); // accessFlag
734            interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[2])); // readonly
735        });
736    }
737
738    private transferMethods2Literal(interfaceTypeLiterals: Array<Literal>) {
739        let transferredTarget: Array<number> = this.methods;
740
741        interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, transferredTarget.length));
742        transferredTarget.forEach(method => {
743            interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, method));
744        });
745    }
746}
747