• 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 { isGlobalDeclare } from "../strictMode";
22import {
23    Literal,
24    LiteralBuffer,
25    LiteralTag
26} from "./literal";
27import { hasAbstractModifier } from "./util";
28import { CmdOptions } from "../cmdOptions";
29import { getLiteralKey } from "./util";
30import { CompilerDriver } from "../compilerDriver";
31
32export enum PrimitiveType {
33    ANY,
34    NUMBER,
35    BOOLEAN,
36    VOID,
37    STRING,
38    SYMBOL,
39    NULL,
40    UNDEFINED,
41    INT,
42}
43
44export enum BuiltinType {
45    _HEAD = 20,
46    Function,
47    RangeError,
48    Error,
49    Object,
50    SyntaxError,
51    TypeError,
52    ReferenceError,
53    URIError,
54    Symbol,
55    EvalError,
56    Number,
57    parseFloat,
58    Date,
59    Boolean,
60    BigInt,
61    parseInt,
62    WeakMap,
63    RegExp,
64    Set,
65    Map,
66    WeakRef,
67    WeakSet,
68    FinalizationRegistry,
69    Array,
70    Uint8ClampedArray,
71    Uint8Array,
72    TypedArray,
73    Int8Array,
74    Uint16Array,
75    Uint32Array,
76    Int16Array,
77    Int32Array,
78    Float32Array,
79    Float64Array,
80    BigInt64Array,
81    BigUint64Array,
82    SharedArrayBuffer,
83    DataView,
84    String,
85    ArrayBuffer,
86    eval,
87    isFinite,
88    ArkPrivate,
89    print,
90    decodeURI,
91    decodeURIComponent,
92    isNaN,
93    encodeURI,
94    NaN,
95    globalThis,
96    encodeURIComponent,
97    Infinity,
98    Math,
99    JSON,
100    Atomics,
101    undefined,
102    Reflect,
103    Promise,
104    Proxy,
105    GeneratorFunction,
106    Intl,
107}
108
109export const userDefinedTypeStartIndex = 100;
110let literalBufferIndexShift = userDefinedTypeStartIndex;
111
112export enum L2Type {
113    _COUNTER,
114    CLASS,
115    CLASSINST,
116    FUNCTION,
117    UNION,
118    ARRAY,
119    OBJECT,
120    EXTERNAL,
121    INTERFACE,
122    BUILTINCONTAINER
123}
124
125
126export enum ClassModifierAbstract {
127    NONABSTRACT,
128    ABSTRACT
129}
130
131export enum MethodModifier {
132    STATIC = 1 << 2,
133    ASYNC = 1 << 3,
134    ASTERISK = 1 << 4,
135    ABSTRACT = 1 << 6,  // The fifth bit is held by GetOrSetAccessorFlag
136    DECLARE = 1 << 7
137}
138
139export enum ModifierReadonly {
140    NONREADONLY,
141    READONLY
142}
143
144export enum AccessFlag {
145    PUBLIC,
146    PRIVATE,
147    PROTECTED
148}
149
150export enum GetOrSetAccessorFlag {
151    FALSE = 0,  // Not GetAccessor and SetAccessor
152    TRUE = 1 << 5  // GetAccessor or SetAccessor
153}
154
155type ClassMemberFunction = ts.MethodDeclaration | ts.ConstructorDeclaration |
156                           ts.GetAccessorDeclaration | ts.SetAccessorDeclaration;
157
158export abstract class BaseType {
159
160    abstract transfer2LiteralBuffer(): LiteralBuffer;
161    protected typeChecker = TypeChecker.getInstance();
162    protected typeRecorder = TypeRecorder.getInstance();
163
164    protected transferType2Literal(type: number, literals: Array<Literal>) {
165        if (type >= literalBufferIndexShift) {
166            let litId = getLiteralKey(CompilerDriver.srcNode, type - literalBufferIndexShift);
167            literals.push(new Literal(LiteralTag.LITERALARRAY, litId));
168        } else {
169            literals.push(new Literal(LiteralTag.BUILTINTYPEINDEX, type));
170        }
171    }
172
173    protected addCurrentType(node: ts.Node, index: number): void {
174        this.typeRecorder.addType2Index(node, index);
175    }
176
177    protected setVariable2Type(variableNode: ts.Node, index: number): void {
178        this.typeRecorder.setVariable2Type(variableNode, index);
179    }
180
181    protected tryGetTypeIndex(typeNode: ts.Node): number {
182        return this.typeRecorder.tryGetTypeIndex(typeNode);
183    }
184
185    protected getOrCreateRecordForDeclNode(typeNode: ts.Node, variableNode?: ts.Node): PrimitiveType {
186        return this.typeChecker.getOrCreateRecordForDeclNode(typeNode, variableNode);
187    }
188
189    protected getOrCreateRecordForTypeNode(typeNode: ts.TypeNode | undefined, variableNode?: ts.Node): PrimitiveType {
190        return this.typeChecker.getOrCreateRecordForTypeNode(typeNode, variableNode);
191    }
192
193    protected getIndexFromTypeArrayBuffer(type: BaseType): number {
194        return PandaGen.appendTypeArrayBuffer(type);
195    }
196
197    protected setTypeArrayBuffer(type: BaseType, index: number): void {
198        PandaGen.setTypeArrayBuffer(type, index);
199    }
200
201    protected calculateIndex(builtinTypeIdx): { typeIndex: number; shiftedTypeIndex: number; } {
202        let typeIndex: number;
203        let shiftedTypeIndex: number;
204        let recordBuiltin = builtinTypeIdx && CmdOptions.needRecordBuiltinDtsType();
205        if (recordBuiltin) {
206            typeIndex = undefined;
207            shiftedTypeIndex = builtinTypeIdx;
208            if (isGlobalDeclare()) {
209                typeIndex = builtinTypeIdx - BuiltinType._HEAD;
210            }
211        } else {
212            typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType());
213            shiftedTypeIndex = typeIndex + literalBufferIndexShift;
214        }
215        return {typeIndex: typeIndex, shiftedTypeIndex: shiftedTypeIndex};
216    }
217
218}
219
220export class PlaceHolderType extends BaseType {
221    transfer2LiteralBuffer(): LiteralBuffer {
222        return new LiteralBuffer();
223    }
224}
225
226export class TypeSummary extends BaseType {
227    preservedIndex: number = 0;
228    userDefinedClassNum: number = 0;
229    anonymousRedirect: Array<string> = new Array<string>();
230    constructor() {
231        super();
232        this.preservedIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType());
233        if (isGlobalDeclare()) {
234            let builtinTypeSlotNum = userDefinedTypeStartIndex - BuiltinType._HEAD;
235            for (let i = 0; i < builtinTypeSlotNum; i++) {
236                this.getIndexFromTypeArrayBuffer(new PlaceHolderType());
237            }
238            literalBufferIndexShift = BuiltinType._HEAD;
239        }
240    }
241
242    public setInfo(userDefinedClassNum: number, anonymousRedirect: Array<string>): void {
243        this.userDefinedClassNum = userDefinedClassNum;
244        this.anonymousRedirect = anonymousRedirect;
245        this.setTypeArrayBuffer(this, this.preservedIndex);
246    }
247
248    public getPreservedIndex() {
249        return this.preservedIndex;
250    }
251
252    transfer2LiteralBuffer(): LiteralBuffer {
253        let countBuf = new LiteralBuffer();
254        let summaryLiterals: Array<Literal> = new Array<Literal>();
255        let definedTypeNum = this.userDefinedClassNum;
256        if (isGlobalDeclare()) {
257            definedTypeNum += userDefinedTypeStartIndex - BuiltinType._HEAD;
258        }
259        summaryLiterals.push(new Literal(LiteralTag.INTEGER, definedTypeNum));
260        let literalIdx = PandaGen.getLiteralArrayBuffer.length;
261        for (let i = 1; i <= definedTypeNum; i++) {
262            summaryLiterals.push(new Literal(LiteralTag.LITERALARRAY,
263                                             getLiteralKey(CompilerDriver.srcNode, literalIdx + i)));
264        }
265        summaryLiterals.push(new Literal(LiteralTag.INTEGER, this.anonymousRedirect.length));
266        for (let element of this.anonymousRedirect) {
267            summaryLiterals.push(new Literal(LiteralTag.STRING, element));
268        }
269        countBuf.addLiterals(...summaryLiterals);
270        return countBuf;
271    }
272}
273
274export class ClassType extends BaseType {
275    modifier: number = ClassModifierAbstract.NONABSTRACT; // 0 -> unabstract, 1 -> abstract;
276    extendsHeritage: number = PrimitiveType.ANY;
277    implementsHeritages: Array<number> = new Array<number>();
278    // fileds Array: [typeIndex] [public -> 0, private -> 1, protected -> 2] [readonly -> 1]
279    staticFields: Map<string, Array<number>> = new Map<string, Array<number>>();
280    staticMethods: Map<string, {typeIndex: number, isDeclare: boolean}> = new Map<string, {typeIndex: number, isDeclare: boolean}>();
281    fields: Map<string, Array<number>> = new Map<string, Array<number>>();
282    methods: Map<string, {typeIndex: number, isDeclare: boolean}> = new Map<string, {typeIndex: number, isDeclare: boolean}>();
283    typeIndex: number;
284    shiftedTypeIndex: number;
285    field_with_init_num: number = 0;
286    method_with_body_num: number = 0;
287
288    constructor(classNode: ts.ClassDeclaration | ts.ClassExpression, builtinTypeIdx: number = undefined) {
289        super();
290        let res = this.calculateIndex(builtinTypeIdx);
291        this.typeIndex = res.typeIndex;
292        this.shiftedTypeIndex = res.shiftedTypeIndex;
293
294        // record type before its initialization, so its index can be recorded
295        // in case there's recursive reference of this type
296        this.addCurrentType(classNode, this.shiftedTypeIndex);
297        this.fillInModifiers(classNode);
298        this.fillInHeritages(classNode);
299        this.fillInFieldsAndMethods(classNode);
300
301        if (!builtinTypeIdx || isGlobalDeclare()) {
302            this.setTypeArrayBuffer(this, this.typeIndex);
303        }
304
305        // create class instance type used by recording 'this' later
306        if ((this.method_with_body_num > 0 || this.field_with_init_num > 0) && !hasAbstractModifier(classNode)) {
307            let instTypeIdx = this.typeChecker.getOrCreateInstanceType(this.shiftedTypeIndex);
308            this.typeRecorder.addUserDefinedTypeSet(instTypeIdx);
309        }
310    }
311
312    private fillInModifiers(node: ts.ClassDeclaration | ts.ClassExpression): void {
313        if (node.modifiers) {
314            for (let modifier of node.modifiers) {
315                switch (modifier.kind) {
316                    case ts.SyntaxKind.AbstractKeyword: {
317                        this.modifier = ClassModifierAbstract.ABSTRACT;
318                        break;
319                    }
320                    default: {
321                        break;
322                    }
323                }
324            }
325        }
326    }
327
328    private fillInHeritages(node: ts.ClassDeclaration | ts.ClassExpression): void {
329        if (node.heritageClauses) {
330            for (let heritage of node.heritageClauses) {
331                let heritageFullName = heritage.getText();
332                for (let heritageType of heritage.types) {
333                    let heritageIdentifier = <ts.Identifier>heritageType.expression;
334                    let heritageTypeIndex = this.getOrCreateRecordForDeclNode(heritageIdentifier, heritageIdentifier);
335                    if (heritageFullName.startsWith("extends ")) {
336                        this.extendsHeritage = heritageTypeIndex;
337                    } else if (heritageFullName.startsWith("implements ")) {
338                        this.implementsHeritages.push(heritageTypeIndex);
339                    }
340                }
341            }
342        }
343    }
344
345    private fillInFields(member: ts.PropertyDeclaration): void {
346        let fieldName = jshelpers.getTextOfIdentifierOrLiteral(member.name);
347        let fieldInfo = Array<number>(PrimitiveType.ANY, AccessFlag.PUBLIC, ModifierReadonly.NONREADONLY);
348        let isStatic: boolean = false;
349        if (member.modifiers) {
350            for (let modifier of member.modifiers) {
351                switch (modifier.kind) {
352                    case ts.SyntaxKind.StaticKeyword: {
353                        isStatic = true;
354                        break;
355                    }
356                    case ts.SyntaxKind.PrivateKeyword: {
357                        fieldInfo[1] = AccessFlag.PRIVATE;
358                        break;
359                    }
360                    case ts.SyntaxKind.ProtectedKeyword: {
361                        fieldInfo[1] = AccessFlag.PROTECTED;
362                        break;
363                    }
364                    case ts.SyntaxKind.ReadonlyKeyword: {
365                        fieldInfo[2] = ModifierReadonly.READONLY;
366                        break;
367                    }
368                    default: {
369                        break;
370                    }
371                }
372            }
373        }
374
375        let typeNode = member.type;
376        let memberName = member.name;
377        fieldInfo[0] = this.getOrCreateRecordForTypeNode(typeNode, memberName);
378
379        if (isStatic) {
380            this.staticFields.set(fieldName, fieldInfo);
381        } else {
382            this.fields.set(fieldName, fieldInfo);
383            if (member.initializer != undefined) {
384                this.field_with_init_num++;
385            }
386        }
387    }
388
389    private fillInMethods(member: ClassMemberFunction): void {
390        /**
391         * a method like declaration in a new class must be a new type,
392         * create this type and add it into typeRecorder if it's not from tsc's library
393         */
394        if (this.typeChecker.isFromDefaultLib(member)) {
395            return;
396        }
397        // Keep the rule to get the name as the same as to get function's name in FunctionType
398        let funcName = member.name ? jshelpers.getTextOfIdentifierOrLiteral(member.name) : "constructor";
399        let isStatic = false;
400        if (member.modifiers) {
401            for (let modifier of member.modifiers) {
402                if (modifier.kind == ts.SyntaxKind.StaticKeyword) {
403                    isStatic = true;
404                }
405            }
406        }
407        let foundSameNameFuncRet = isStatic ? this.staticMethods.get(funcName) : this.methods.get(funcName);
408        if (foundSameNameFuncRet && !foundSameNameFuncRet.isDeclare) {
409            // A same named method with implementation has already been recorded
410            return;
411        }
412        let variableNode = member.name ? member.name : undefined;
413        let funcType = new FunctionType(<ts.FunctionLikeDeclaration>member);
414        if (variableNode) {
415            this.setVariable2Type(variableNode, funcType.shiftedTypeIndex);
416        }
417
418        // Then, get the typeIndex and fill in the methods array
419        let type = this.tryGetTypeIndex(member);
420        if (isStatic) {
421            this.staticMethods.set(funcType.getFunctionName(), {typeIndex: type!, isDeclare: member.body == undefined});
422        } else {
423            this.methods.set(funcType.getFunctionName(), {typeIndex: type!, isDeclare: member.body == undefined});
424            if (member.body != undefined) {
425                this.method_with_body_num++;
426            }
427        }
428    }
429
430    private fillInFieldsAndMethods(node: ts.ClassDeclaration | ts.ClassExpression): void {
431        if (node.members) {
432            for (let member of node.members) {
433                switch (member.kind) {
434                    case ts.SyntaxKind.MethodDeclaration:
435                    case ts.SyntaxKind.Constructor:
436                    case ts.SyntaxKind.GetAccessor:
437                    case ts.SyntaxKind.SetAccessor: {
438                        this.fillInMethods(<ClassMemberFunction>member);
439                        break;
440                    }
441                    case ts.SyntaxKind.PropertyDeclaration: {
442                        this.fillInFields(<ts.PropertyDeclaration>member);
443                        break;
444                    }
445                    default:
446                        break;
447                }
448            }
449        }
450    }
451
452    transfer2LiteralBuffer(): LiteralBuffer {
453        if (!this.typeIndex) {
454            return;
455        }
456        let classTypeBuf = new LiteralBuffer();
457        let classTypeLiterals: Array<Literal> = new Array<Literal>();
458        // the first element is to determine the L2 type
459        classTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.CLASS));
460        classTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.modifier));
461
462        this.transferType2Literal(this.extendsHeritage, classTypeLiterals);
463        classTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.implementsHeritages.length));
464        this.implementsHeritages.forEach(heritage => { // heritage types
465            this.transferType2Literal(heritage, classTypeLiterals);
466        });
467
468        // record unstatic fields and methods
469        this.transferFields2Literal(classTypeLiterals, false);
470        this.transferMethods2Literal(classTypeLiterals, false);
471
472        // record static methods and fields;
473        this.transferFields2Literal(classTypeLiterals, true);
474        this.transferMethods2Literal(classTypeLiterals, true);
475
476        classTypeBuf.addLiterals(...classTypeLiterals);
477        return classTypeBuf;
478    }
479
480    private transferFields2Literal(classTypeLiterals: Array<Literal>, isStatic: boolean): void {
481        let transferredTarget: Map<string, Array<number>> = isStatic ? this.staticFields : this.fields;
482
483        classTypeLiterals.push(new Literal(LiteralTag.INTEGER, transferredTarget.size));
484        transferredTarget.forEach((typeInfo, name) => {
485            classTypeLiterals.push(new Literal(LiteralTag.STRING, name));
486            this.transferType2Literal(typeInfo[0], classTypeLiterals);
487            classTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[1])); // accessFlag
488            classTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[2])); // readonly
489        });
490    }
491
492    private transferMethods2Literal(classTypeLiterals: Array<Literal>, isStatic: boolean): void {
493        let transferredTarget: Map<string, {typeIndex: number, isDeclare: boolean}> = isStatic ? this.staticMethods : this.methods;
494
495        classTypeLiterals.push(new Literal(LiteralTag.INTEGER, transferredTarget.size));
496        transferredTarget.forEach((typeInfo, name) => {
497            let typeIndex: number = <number>typeInfo.typeIndex;
498            classTypeLiterals.push(new Literal(LiteralTag.STRING, name));
499            this.transferType2Literal(typeIndex, classTypeLiterals);
500        });
501    }
502}
503
504export class ClassInstType extends BaseType {
505    shiftedReferredClassIndex: number; // the referred class in the type system;
506    typeIndex: number;
507    shiftedTypeIndex: number;
508    constructor(referredClassIndex: number) {
509        super();
510        this.shiftedReferredClassIndex = referredClassIndex;
511        this.typeIndex = this.getIndexFromTypeArrayBuffer(this);
512        this.shiftedTypeIndex = this.typeIndex + literalBufferIndexShift;
513        this.typeRecorder.setClass2InstanceMap(this.shiftedReferredClassIndex, this.shiftedTypeIndex);
514        if (this.shiftedReferredClassIndex > BuiltinType._HEAD) {
515            this.typeRecorder.addUserDefinedTypeSet(this.shiftedTypeIndex);
516        }
517    }
518
519    transfer2LiteralBuffer(): LiteralBuffer {
520        let classInstBuf = new LiteralBuffer();
521        let classInstLiterals: Array<Literal> = new Array<Literal>();
522
523        classInstLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.CLASSINST));
524        this.transferType2Literal(this.shiftedReferredClassIndex, classInstLiterals);
525        classInstBuf.addLiterals(...classInstLiterals);
526
527        return classInstBuf;
528    }
529}
530
531export class FunctionType extends BaseType {
532    name: string = '';
533    accessFlag: number = AccessFlag.PUBLIC; // 0 -> public, 1 -> private, 2 -> protected
534    modifiers: number = 0; // 0 -> non-static, 4 -> static, 8 -> async, 16-> asterisk
535    containThisParam: boolean = false;
536    parameters: Array<number> = new Array<number>();
537    returnType: number = PrimitiveType.ANY;
538    typeIndex: number;
539    shiftedTypeIndex: number;
540    getOrSetAccessorFlag: GetOrSetAccessorFlag = GetOrSetAccessorFlag.FALSE;
541
542    constructor(funcNode: ts.FunctionLikeDeclaration | ts.MethodSignature, builtinTypeIdx: number = undefined) {
543        super();
544        let res = this.calculateIndex(builtinTypeIdx);
545        this.typeIndex = res.typeIndex;
546        this.shiftedTypeIndex = res.shiftedTypeIndex;
547        if (funcNode.kind === ts.SyntaxKind.GetAccessor || funcNode.kind === ts.SyntaxKind.SetAccessor) {
548            this.getOrSetAccessorFlag = GetOrSetAccessorFlag.TRUE;
549        }
550
551        // record type before its initialization, so its index can be recorded
552        // in case there's recursive reference of this type
553        this.addCurrentType(funcNode, this.shiftedTypeIndex);
554
555        if (funcNode.name) {
556            this.name = jshelpers.getTextOfIdentifierOrLiteral(funcNode.name);
557        } else {
558            this.name = "constructor";
559        }
560        this.fillInModifiers(funcNode);
561        this.fillInParameters(funcNode);
562        this.fillInReturn(funcNode);
563        this.setTypeArrayBuffer(this, this.typeIndex);
564    }
565
566    public getFunctionName(): string {
567        return this.name;
568    }
569
570    private fillInModifiers(node: ts.FunctionLikeDeclaration | ts.MethodSignature): void {
571        if (node.modifiers) {
572            for (let modifier of node.modifiers) {
573                switch (modifier.kind) {
574                    case ts.SyntaxKind.PrivateKeyword: {
575                        this.accessFlag = AccessFlag.PRIVATE;
576                        break;
577                    }
578                    case ts.SyntaxKind.ProtectedKeyword: {
579                        this.accessFlag = AccessFlag.PROTECTED;
580                        break;
581                    }
582                    case ts.SyntaxKind.StaticKeyword: {
583                        this.modifiers = MethodModifier.STATIC;
584                        break;
585                    }
586                    case ts.SyntaxKind.AsyncKeyword: {
587                        this.modifiers += MethodModifier.ASYNC;
588                        break;
589                    }
590                    case ts.SyntaxKind.AbstractKeyword: {
591                        this.modifiers += MethodModifier.ABSTRACT;
592                        break;
593                    }
594                    default:
595                        break;
596                }
597            }
598        }
599
600        if (!ts.isMethodSignature(node) && node.asteriskToken) {
601            this.modifiers += MethodModifier.ASTERISK;
602        }
603        if (ts.isMethodSignature(node) || !<ts.FunctionLikeDeclaration>node.body) {
604            this.modifiers += MethodModifier.DECLARE;
605        }
606    }
607
608    private fillInParameters(node: ts.FunctionLikeDeclaration | ts.MethodSignature): void {
609        if (node.parameters) {
610            for (let parameter of node.parameters) {
611                let typeNode = parameter.type;
612                let variableNode = parameter.name;
613                let typeIndex = this.getOrCreateRecordForTypeNode(typeNode, variableNode);
614                this.parameters.push(typeIndex);
615                if (variableNode.getFullText() === 'this') {
616                    this.containThisParam = true;
617                }
618            }
619        }
620    }
621
622    private fillInReturn(node: ts.FunctionLikeDeclaration | ts.MethodSignature): void {
623        let typeNode = node.type;
624        let typeIndex = this.getOrCreateRecordForTypeNode(typeNode, typeNode);
625        this.returnType = typeIndex;
626    }
627
628    getModifier(): number {
629        return this.modifiers;
630    }
631
632    hasModifier(modifier: MethodModifier): boolean {
633        return (this.modifiers & modifier) ? true : false;
634    }
635
636    transfer2LiteralBuffer(): LiteralBuffer {
637        let funcTypeBuf = new LiteralBuffer();
638        let funcTypeLiterals: Array<Literal> = new Array<Literal>();
639        funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.FUNCTION));
640        funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.accessFlag + this.modifiers + this.getOrSetAccessorFlag));
641        funcTypeLiterals.push(new Literal(LiteralTag.STRING, this.name));
642        if (this.containThisParam) {
643            funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, 1)); // marker for having 'this' param
644            this.transferType2Literal(this.parameters[0], funcTypeLiterals);
645            funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.parameters.length - 1));
646            for (let i = 1; i < this.parameters.length; i++) { // normal parameter types
647                this.transferType2Literal(this.parameters[i], funcTypeLiterals);
648            }
649        } else {
650            funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, 0)); // marker for not having 'this' param
651            funcTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.parameters.length));
652            for (let i = 0; i < this.parameters.length; i++) {
653                this.transferType2Literal(this.parameters[i], funcTypeLiterals);
654            }
655        }
656
657        this.transferType2Literal(this.returnType, funcTypeLiterals);
658        funcTypeBuf.addLiterals(...funcTypeLiterals);
659        return funcTypeBuf;
660    }
661}
662
663export class ExternalType extends BaseType {
664    fullRedirectNath: string;
665    typeIndex: number;
666    shiftedTypeIndex: number;
667
668    constructor(importName: string, redirectPath: string) {
669        super();
670        this.fullRedirectNath = `#${importName}#${redirectPath}`;
671        this.typeIndex = this.getIndexFromTypeArrayBuffer(this);
672        this.shiftedTypeIndex = this.typeIndex + literalBufferIndexShift;
673    }
674
675    transfer2LiteralBuffer(): LiteralBuffer {
676        let impTypeBuf = new LiteralBuffer();
677        let impTypeLiterals: Array<Literal> = new Array<Literal>();
678        impTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.EXTERNAL));
679        impTypeLiterals.push(new Literal(LiteralTag.STRING, this.fullRedirectNath));
680        impTypeBuf.addLiterals(...impTypeLiterals);
681        return impTypeBuf;
682    }
683}
684
685export class UnionType extends BaseType {
686    unionedTypeArray: Array<number> = [];
687    typeIndex: number = PrimitiveType.ANY;
688    shiftedTypeIndex: number = PrimitiveType.ANY;
689
690    constructor(typeNode: ts.Node) {
691        super();
692        this.setOrReadFromArrayRecord(typeNode);
693    }
694
695    setOrReadFromArrayRecord(typeNode: ts.Node): void {
696        let unionStr = typeNode.getText();
697        if (this.hasUnionTypeMapping(unionStr)) {
698            this.shiftedTypeIndex = this.getFromUnionTypeMap(unionStr)!;
699            return;
700        }
701        this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType());
702        this.shiftedTypeIndex = this.typeIndex + literalBufferIndexShift;
703        this.fillInUnionArray(typeNode, this.unionedTypeArray);
704        this.setUnionTypeMap(unionStr, this.shiftedTypeIndex);
705        this.setTypeArrayBuffer(this, this.typeIndex);
706    }
707
708    hasUnionTypeMapping(unionStr: string): boolean {
709        return this.typeRecorder.hasUnionTypeMapping(unionStr);
710    }
711
712    getFromUnionTypeMap(unionStr: string): number {
713        return this.typeRecorder.getFromUnionTypeMap(unionStr);
714    }
715
716    setUnionTypeMap(unionStr: string, shiftedTypeIndex: number): void {
717        return this.typeRecorder.setUnionTypeMap(unionStr, shiftedTypeIndex);
718    }
719
720    fillInUnionArray(typeNode: ts.Node, unionedTypeArray: Array<number>): void {
721        for (let element of (<ts.UnionType><any>typeNode).types) {
722            let elementNode = <ts.TypeNode><any>element;
723            let typeIndex = this.getOrCreateRecordForTypeNode(elementNode, elementNode);
724            unionedTypeArray.push(typeIndex!);
725        }
726    }
727
728    transfer2LiteralBuffer(): LiteralBuffer {
729        let unionTypeBuf = new LiteralBuffer();
730        let unionTypeLiterals: Array<Literal> = new Array<Literal>();
731        unionTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.UNION));
732        unionTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.unionedTypeArray.length));
733        for (let type of this.unionedTypeArray) {
734            this.transferType2Literal(type, unionTypeLiterals);
735        }
736        unionTypeBuf.addLiterals(...unionTypeLiterals);
737        return unionTypeBuf;
738    }
739}
740
741export class ArrayType extends BaseType {
742    referedTypeIndex: number = PrimitiveType.ANY;
743    typeIndex: number = PrimitiveType.ANY;
744    shiftedTypeIndex: number = PrimitiveType.ANY;
745    constructor(typeNode: ts.Node) {
746        super();
747        let elementNode = (<ts.ArrayTypeNode><any>typeNode).elementType;
748        this.referedTypeIndex = this.getOrCreateRecordForTypeNode(elementNode, elementNode);
749        this.setOrReadFromArrayRecord();
750    }
751
752    setOrReadFromArrayRecord(): void {
753        if (this.hasArrayTypeMapping(this.referedTypeIndex)) {
754            this.shiftedTypeIndex = this.getFromArrayTypeMap(this.referedTypeIndex)!;
755        } else {
756            this.typeIndex = this.getIndexFromTypeArrayBuffer(this);
757            this.shiftedTypeIndex = this.typeIndex + literalBufferIndexShift;
758            this.setTypeArrayBuffer(this, this.typeIndex);
759            this.setArrayTypeMap(this.referedTypeIndex, this.shiftedTypeIndex);
760        }
761    }
762
763    hasArrayTypeMapping(referedTypeIndex: number): boolean {
764        return this.typeRecorder.hasArrayTypeMapping(referedTypeIndex);
765    }
766
767    getFromArrayTypeMap(referedTypeIndex: number): number {
768        return this.typeRecorder.getFromArrayTypeMap(referedTypeIndex);
769    }
770
771    setArrayTypeMap(referedTypeIndex: number, shiftedTypeIndex: number): void {
772        return this.typeRecorder.setArrayTypeMap(referedTypeIndex, shiftedTypeIndex);
773    }
774
775    transfer2LiteralBuffer(): LiteralBuffer {
776        let arrayBuf = new LiteralBuffer();
777        let arrayLiterals: Array<Literal> = new Array<Literal>();
778        arrayLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.ARRAY));
779        this.transferType2Literal(this.referedTypeIndex, arrayLiterals);
780        arrayBuf.addLiterals(...arrayLiterals);
781        return arrayBuf;
782    }
783}
784
785export class ObjectType extends BaseType {
786    private properties: Map<string, number> = new Map<string, number>();
787    typeIndex: number = PrimitiveType.ANY;
788    shiftedTypeIndex: number = PrimitiveType.ANY;
789
790    constructor(objNode: ts.TypeLiteralNode) {
791        super();
792        this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType());
793        this.shiftedTypeIndex = this.typeIndex + literalBufferIndexShift;
794        this.fillInMembers(objNode);
795        this.setTypeArrayBuffer(this, this.typeIndex);
796    }
797
798    fillInMembers(objNode: ts.TypeLiteralNode): void {
799        for (let member of objNode.members) {
800            let propertySig = <ts.PropertySignature>member;
801            let name = member.name ? member.name.getText() : "#undefined";
802            let typeIndex = this.getOrCreateRecordForTypeNode(propertySig.type, member.name);
803            this.properties.set(name, typeIndex);
804        }
805    }
806
807    transfer2LiteralBuffer(): LiteralBuffer {
808        let objTypeBuf = new LiteralBuffer();
809        let objLiterals: Array<Literal> = new Array<Literal>();
810        objLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.OBJECT));
811        objLiterals.push(new Literal(LiteralTag.INTEGER, this.properties.size));
812        this.properties.forEach((typeIndex, name) => {
813            objLiterals.push(new Literal(LiteralTag.STRING, name));
814            this.transferType2Literal(typeIndex, objLiterals);
815        });
816        objTypeBuf.addLiterals(...objLiterals);
817        return objTypeBuf;
818    }
819}
820
821export class InterfaceType extends BaseType {
822    heritages: Array<number> = new Array<number>();
823    // fileds Array: [typeIndex] [public -> 0, private -> 1, protected -> 2] [readonly -> 1]
824    fields: Map<string, Array<number>> = new Map<string, Array<number>>();
825    methods: Array<number> = new Array<number>();
826    typeIndex: number;
827    shiftedTypeIndex: number;
828
829    constructor(interfaceNode: ts.InterfaceDeclaration) {
830        super();
831        this.typeIndex = this.getIndexFromTypeArrayBuffer(new PlaceHolderType());
832        this.shiftedTypeIndex = this.typeIndex + literalBufferIndexShift;
833        // record type before its initialization, so its index can be recorded
834        // in case there's recursive reference of this type
835        this.addCurrentType(interfaceNode, this.shiftedTypeIndex);
836        this.fillInHeritages(interfaceNode);
837        this.fillInFieldsAndMethods(interfaceNode);
838        this.setTypeArrayBuffer(this, this.typeIndex);
839    }
840
841    private fillInHeritages(node: ts.InterfaceDeclaration): void {
842        if (node.heritageClauses) {
843            for (let heritage of node.heritageClauses) {
844                for (let heritageType of heritage.types) {
845                    let heritageIdentifier = <ts.Identifier>heritageType.expression;
846                    let heritageTypeIndex = this.getOrCreateRecordForDeclNode(heritageIdentifier, heritageIdentifier);
847                    this.heritages.push(heritageTypeIndex);
848                }
849            }
850        }
851    }
852
853    private fillInFields(member: ts.PropertySignature): void {
854        let fieldName = jshelpers.getTextOfIdentifierOrLiteral(member.name);
855        let fieldInfo = Array<number>(PrimitiveType.ANY, AccessFlag.PUBLIC, ModifierReadonly.NONREADONLY);
856        if (member.modifiers) {
857            for (let modifier of member.modifiers) {
858                switch (modifier.kind) {
859                    case ts.SyntaxKind.PrivateKeyword: {
860                        fieldInfo[1] = AccessFlag.PRIVATE;
861                        break;
862                    }
863                    case ts.SyntaxKind.ProtectedKeyword: {
864                        fieldInfo[1] = AccessFlag.PROTECTED;
865                        break;
866                    }
867                    case ts.SyntaxKind.ReadonlyKeyword: {
868                        fieldInfo[2] = ModifierReadonly.READONLY;
869                        break;
870                    }
871                    default:
872                        break;
873                }
874            }
875        }
876        let typeNode = member.type;
877        let memberName = member.name;
878        fieldInfo[0] = this.getOrCreateRecordForTypeNode(typeNode, memberName);
879        this.fields.set(fieldName, fieldInfo);
880    }
881
882    private fillInMethods(member: ts.MethodSignature): void {
883        /**
884         * a method like declaration in a new class must be a new type,
885         * create this type and add it into typeRecorder if it's not from tsc's library
886         */
887        if (this.typeChecker.isFromDefaultLib(member)) {
888            return;
889        }
890        let variableNode = member.name ? member.name : undefined;
891        let funcType = new FunctionType(<ts.MethodSignature>member);
892        if (variableNode) {
893            this.setVariable2Type(variableNode, funcType.shiftedTypeIndex);
894        }
895        // Then, get the typeIndex and fill in the methods array
896        let typeIndex = this.tryGetTypeIndex(member);
897        this.methods.push(typeIndex!);
898    }
899
900    private fillInFieldsAndMethods(node: ts.InterfaceDeclaration): void {
901        if (node.members) {
902            for (let member of node.members) {
903                switch (member.kind) {
904                    case ts.SyntaxKind.MethodSignature: {
905                        this.fillInMethods(<ts.MethodSignature>member);
906                        break;
907                    }
908                    case ts.SyntaxKind.PropertySignature: {
909                        this.fillInFields(<ts.PropertySignature>member);
910                        break;
911                    }
912                    default:
913                        break;
914                }
915            }
916        }
917    }
918
919    transfer2LiteralBuffer(): LiteralBuffer {
920        let interfaceTypeBuf = new LiteralBuffer();
921        let interfaceTypeLiterals: Array<Literal> = new Array<Literal>();
922        // the first element is to determine the L2 type
923        interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.INTERFACE));
924
925        interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, this.heritages.length));
926        this.heritages.forEach(heritage => {
927            this.transferType2Literal(heritage, interfaceTypeLiterals)
928        });
929
930        // record fields and methods
931        this.transferFields2Literal(interfaceTypeLiterals);
932        this.transferMethods2Literal(interfaceTypeLiterals);
933
934        interfaceTypeBuf.addLiterals(...interfaceTypeLiterals);
935        return interfaceTypeBuf;
936    }
937
938    private transferFields2Literal(interfaceTypeLiterals: Array<Literal>): void {
939        let transferredTarget: Map<string, Array<number>> = this.fields;
940
941        interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, transferredTarget.size));
942        transferredTarget.forEach((typeInfo, name) => {
943            interfaceTypeLiterals.push(new Literal(LiteralTag.STRING, name));
944            this.transferType2Literal(typeInfo[0], interfaceTypeLiterals);
945            interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[1])); // accessFlag
946            interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, typeInfo[2])); // readonly
947        });
948    }
949
950    private transferMethods2Literal(interfaceTypeLiterals: Array<Literal>): void {
951        let transferredTarget: Array<number> = this.methods;
952
953        interfaceTypeLiterals.push(new Literal(LiteralTag.INTEGER, transferredTarget.length));
954        transferredTarget.forEach(method => {
955            this.transferType2Literal(method, interfaceTypeLiterals);
956        });
957    }
958}
959
960export class BuiltinContainerType extends BaseType {
961    containerArray: Array<number> = [];
962    builtinTypeIndex: number;
963    typeIndex: number = PrimitiveType.ANY;
964    shiftedTypeIndex: number = PrimitiveType.ANY;
965
966    constructor(builtinContainerSignature: object) {
967        super();
968        this.builtinTypeIndex = builtinContainerSignature['typeIndex'];
969        this.containerArray = builtinContainerSignature['typeArgIdxs'];
970        this.typeIndex = this.getIndexFromTypeArrayBuffer(this);
971        this.shiftedTypeIndex = this.typeIndex + literalBufferIndexShift;
972        this.setBuiltinContainer2InstanceMap(builtinContainerSignature, this.shiftedTypeIndex);
973    }
974
975    setBuiltinContainer2InstanceMap(builtinContainerSignature: object, index: number): void {
976        return this.typeRecorder.setBuiltinContainer2InstanceMap(builtinContainerSignature, index);
977    }
978
979    transfer2LiteralBuffer(): LiteralBuffer {
980        let BuiltinContainerBuf = new LiteralBuffer();
981        let BuiltinContainerLiterals: Array<Literal> = new Array<Literal>();
982        BuiltinContainerLiterals.push(new Literal(LiteralTag.INTEGER, L2Type.BUILTINCONTAINER));
983        this.transferType2Literal(this.builtinTypeIndex, BuiltinContainerLiterals);
984        BuiltinContainerLiterals.push(new Literal(LiteralTag.INTEGER, this.containerArray.length));
985        for (let type of this.containerArray) {
986            BuiltinContainerLiterals.push(new Literal(LiteralTag.INTEGER, type));
987        }
988        BuiltinContainerBuf.addLiterals(...BuiltinContainerLiterals);
989        return BuiltinContainerBuf;
990    }
991}
992