• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-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 { CmdOptions } from "./cmdOptions";
18import { SourceTextModuleRecord } from "./ecmaModule";
19import {
20    Createarraywithbuffer,
21    Createobjectwithbuffer,
22    Defineclasswithbuffer,
23    Imm,
24    IRNode,
25    IRNodeKind,
26    Label,
27    Newlexenvwithname,
28    OperandType,
29    VReg,
30    WideNewlexenvwithname,
31} from "./irnodes";
32import { LOGD } from "./log";
33import { PandaGen } from "./pandagen";
34import {
35    CatchTable,
36    DeclaredSymbol2Type,
37    ExportedSymbol2Type,
38    Function,
39    Ins,
40    IndirectExportEntry,
41    LocalExportEntry,
42    ModuleRecord,
43    NamespaceImportEntry,
44    RegularImportEntry,
45    Signature,
46    Record
47} from "./pandasm";
48import { generateCatchTables } from "./statement/tryStatement";
49import {
50    escapeUnicode,
51    isRangeInst,
52    getRangeStartVregPos,
53    getRecordTypeFlag
54} from "./base/util";
55import { LiteralBuffer } from "./base/literal";
56import { PrimitiveType, BuiltinType } from "./base/typeSystem";
57import { CompilerDriver } from "./compilerDriver";
58import { hasStaticModifier } from "./jshelpers";
59import { ModuleScope } from "./scope";
60import { TypeRecorder } from "./typeRecorder";
61import { isGlobalDeclare } from "./strictMode";
62import { isFunctionLikeDeclaration } from "./syntaxCheckHelper";
63import { getLiteralKey } from "./base/util";
64
65const dollarSign: RegExp = /\$/g;
66const starSign: RegExp = /\*/g;
67
68const jsonType = {
69    "function": 0,
70    "record": 1,
71    "string": 2,
72    "literal_arr": 3,
73    "module": 4,
74    "options": 5,
75    'type_info': 6,
76    'record_name': 7,
77    'output_filename': 8,
78    'input_json_file_content': 9
79};
80export class Ts2Panda {
81    static strings: Set<string> = new Set();
82    static labelPrefix = "L_";
83    static jsonString: string = "";
84    static moduleRecordlist: Array<ModuleRecord> = [];
85
86    constructor() {
87    }
88
89    static getFuncSignature(pg: PandaGen): Signature {
90        return new Signature(pg.getParametersCount());
91    }
92
93    static getFuncInsnsAndRegsNum(pg: PandaGen): { insns: Ins[]; regsNum: number; labels: string[]; } {
94        let insns: Array<Ins> = [];
95        let labels: Array<string> = [];
96
97        pg.getInsns().forEach((insn: IRNode) => {
98            let insOpcode = insn.kind >= IRNodeKind.VREG ? undefined : insn.kind;
99            let insRegs: Array<number> = [];
100            let insIds: Array<string> = [];
101            let insImms: Array<number> = [];
102            let insLabel: string = "";
103
104            if (insn instanceof Label) {
105                insLabel = Ts2Panda.labelPrefix + insn.id;
106                labels.push(insLabel);
107            } else if (isRangeInst(insn)) {
108                // For DynRange insn we only pass the first vreg of continuous vreg array
109                let operands = insn.operands;
110                for (let i = 0; i <= getRangeStartVregPos(insn); i++) {
111                    let operand = operands[i];
112                    if (operand instanceof VReg) {
113                        insRegs.push((<VReg>operand).num);
114                        continue;
115                    }
116
117                    if (operand instanceof Imm) {
118                        insImms.push((<Imm>operand).value);
119                        continue;
120                    }
121
122                    if (typeof (operand) === "string") {
123                        insIds.push(operand);
124                        continue;
125                    }
126
127                    if (operand instanceof Label) {
128                        let labelName = Ts2Panda.labelPrefix + operand.id;
129                        insIds.push(labelName);
130                        continue;
131                    }
132                }
133            } else {
134                insn.operands.forEach((operand: OperandType) => {
135                    if (operand instanceof VReg) {
136                        let v = <VReg>operand;
137                        insRegs.push(v.num);
138                    } else if (operand instanceof Imm) {
139                        let imm = <Imm>operand;
140                        insImms.push(imm.value);
141                    } else if (typeof (operand) === "string") {
142                        insIds.push(operand);
143                        if (!this.escapeLitIdString(insn, operand)) {
144                            Ts2Panda.strings.add(operand);
145                        }
146                    } else if (operand instanceof Label) {
147                        let labelName = Ts2Panda.labelPrefix + operand.id;
148                        insIds.push(labelName);
149                    }
150                });
151            }
152
153            insn.debugPosInfo.clearNodeKind();
154
155            insns.push(new Ins(
156                insOpcode,
157                insRegs.length === 0 ? undefined : insRegs,
158                insIds.length === 0 ? undefined : insIds,
159                insImms.length === 0 ? undefined : insImms,
160                insLabel === "" ? undefined : insLabel,
161                insn.debugPosInfo,
162            ));
163        });
164
165        return {
166            insns: insns,
167            regsNum: (pg.getTotalRegsNum() - pg.getParametersCount()),
168            labels: labels.length === 0 ? undefined : labels
169        };
170    }
171
172    // literalId parameters should not be included to string items of the abc file
173    static escapeLitIdString(insn: IRNode, operand: string): boolean {
174        if (insn instanceof Createarraywithbuffer || insn instanceof Createobjectwithbuffer ||
175            insn instanceof Newlexenvwithname || insn instanceof WideNewlexenvwithname) {
176            return true;
177        }
178
179        if ((insn instanceof Defineclasswithbuffer) && (insn.operands[2] === operand)) {
180            return true;
181        }
182
183        return false;
184    }
185
186    static dumpStringsArray(ts2abc: any) {
187        let strings_arr = Array.from(Ts2Panda.strings);
188
189        let strObject = {
190            "t": jsonType.string,
191            "s": strings_arr
192        }
193
194        let jsonStrUnicode = escapeUnicode(JSON.stringify(strObject, null, 2));
195        jsonStrUnicode = "$" + jsonStrUnicode.replace(dollarSign, '#$') + "$";
196        jsonStrUnicode = jsonStrUnicode.replace(starSign, '#*');
197        if (CmdOptions.isEnableDebugLog()) {
198            Ts2Panda.jsonString += jsonStrUnicode;
199        }
200        ts2abc.stdio[3].write(jsonStrUnicode + '\n');
201    }
202
203    static dumpTypeLiteralArrayBuffer(): string {
204        let literalArrays = PandaGen.getLiteralArrayBuffer();
205        let countType: LiteralBuffer = literalArrays[0];
206        let jsonTypeString: string = "";
207        let typeCount = countType.getLiteral(0)?.getValue();
208        if (typeCount) {
209            for (let i = 0; i < typeCount; i++) {
210                jsonTypeString += escapeUnicode(JSON.stringify(literalArrays[1+i], null, 2));
211            }
212        }
213        return jsonTypeString;
214    }
215
216    static dumpConstantPool(ts2abc: any): void {
217        let literalArrays = PandaGen.getLiteralArrayBuffer();
218        if (CmdOptions.enableTypeLog()) {
219            console.log("-------- LiteralArrayBuffer --------");
220            const util = require('util');
221            for (let e of PandaGen.getLiteralArrayBuffer()) {
222                console.log(util.inspect(JSON.parse(JSON.stringify(e)), { maxArrayLength: null }));
223            }
224        }
225
226        literalArrays.forEach(function(literalArray) {
227            let literalArrayObject = {
228                "t": jsonType.literal_arr,
229                "lit_arr": literalArray
230            }
231            let jsonLiteralArrUnicode = escapeUnicode(JSON.stringify(literalArrayObject, null, 2));
232            jsonLiteralArrUnicode = "$" + jsonLiteralArrUnicode.replace(dollarSign, '#$') + "$";
233            jsonLiteralArrUnicode = jsonLiteralArrUnicode.replace(starSign, '#*');
234            if (CmdOptions.isEnableDebugLog()) {
235                Ts2Panda.jsonString += jsonLiteralArrUnicode;
236            }
237            ts2abc.stdio[3].write(jsonLiteralArrUnicode + '\n');
238        });
239    }
240
241    static dumpCmdOptions(ts2abc: any): void {
242        let enableRecordType: boolean = CmdOptions.needRecordType() && CompilerDriver.isTsFile;
243        let options = {
244            "t": jsonType.options,
245            "merge_abc": CmdOptions.isMergeAbc(),
246            "module_mode": CmdOptions.isModules(),
247            "commonjs_module": CmdOptions.isCommonJs(),
248            "debug_mode": CmdOptions.isDebugMode(),
249            "log_enabled": CmdOptions.isEnableDebugLog(),
250            "opt_level": CmdOptions.getOptLevel(),
251            "opt_log_level": CmdOptions.getOptLogLevel(),
252            "display_typeinfo": CmdOptions.getDisplayTypeinfo(),
253            "is_dts_file": isGlobalDeclare(),
254            "output-proto": CmdOptions.isOutputproto(),
255            "record_type": enableRecordType,
256            "input-file": CmdOptions.getCompileFilesList()
257        };
258        let jsonOpt = JSON.stringify(options, null, 2);
259        jsonOpt = "$" + jsonOpt.replace(dollarSign, '#$') + "$";
260        jsonOpt = jsonOpt.replace(starSign, '#*');
261        if (CmdOptions.isEnableDebugLog()) {
262            Ts2Panda.jsonString += jsonOpt;
263        }
264        ts2abc.stdio[3].write(jsonOpt + '\n');
265    }
266
267    static dumpRecord(ts2abc: any, recordName: string): void {
268        let record = {
269            "t": jsonType.record,
270            "rb": new Record(recordName),
271            "pn": CmdOptions.getPackageName()
272        }
273        let jsonRecord = escapeUnicode(JSON.stringify(record, null, 2));
274        jsonRecord = "$" + jsonRecord.replace(dollarSign, '#$') + "$";
275        jsonRecord = jsonRecord.replace(starSign, '#*');
276        if (CmdOptions.isEnableDebugLog()) {
277            Ts2Panda.jsonString += jsonRecord;
278        }
279        ts2abc.stdio[3].write(jsonRecord + '\n');
280    }
281
282    // @ts-ignore
283    static dumpInstTypeMap(pg: PandaGen): any {
284        let insts = pg.getInsns();
285        let locals = pg.getLocals();
286
287        // build instidx - typeidx map
288        let handledSet: Set<number> = new Set<number>();
289        let instTypeMap: Map<number, number> = new Map<number, number>();
290        let paraCount = pg.getParametersCount();
291        let vregCount = pg.getTotalRegsNum() - paraCount;
292        for (let i = 0; i < insts.length; i++) {
293            let inst = insts[i];
294            let typeIdx = pg.getInstTypeMap().get(inst);
295            if (typeIdx != undefined) {
296                instTypeMap.set(i, typeIdx);
297                continue;
298            }
299
300            // get builtin type for tryloadglobal instruction
301            if (inst.kind === IRNodeKind.TRYLDGLOBALBYNAME) {
302                let name = inst.operands[1] as string;
303                if (name in BuiltinType) {
304                    typeIdx = BuiltinType[name];
305                    instTypeMap.set(i, typeIdx);
306                }
307                continue;
308            }
309
310            // skip arg type
311            if (i < paraCount && inst.kind === IRNodeKind.MOV) {
312                let vreg = (inst.operands[0] as VReg).num;
313                let arg = (inst.operands[1] as VReg).num;
314                if (vreg >= paraCount || arg < vregCount) {
315                    continue;  // not arg
316                }
317                // no need to record arg type
318                handledSet.add(vreg);
319                continue;
320            }
321
322            // local vreg -> inst
323            if (inst.kind === IRNodeKind.STA) {
324                let vreg = (inst.operands[0] as VReg).num;
325                if (vreg < locals.length && !handledSet.has(vreg)) {
326                    typeIdx = locals[vreg].getTypeIndex();
327                    instTypeMap.set(i, typeIdx);
328                    handledSet.add(vreg);
329                }
330            }
331        }
332
333        // add function/this type
334        let functionNode = pg.getNode();
335        let typeRecorder = TypeRecorder.getInstance();
336        if (typeRecorder != undefined && isFunctionLikeDeclaration(functionNode)) {
337            // -1 for function type
338            const functionTypeIndex = -1;
339            let typeIdx = typeRecorder.tryGetTypeIndex(ts.getOriginalNode(functionNode));
340            instTypeMap.set(functionTypeIndex, typeIdx);
341
342            // -2 for this type
343            let classNode = functionNode.parent;
344            if (ts.isClassLike(classNode)) {
345                const thisTypeIndex = -2;
346                typeIdx = typeRecorder.tryGetTypeIndex(ts.getOriginalNode(classNode));
347                if (!hasStaticModifier(functionNode)) {
348                    typeIdx = typeRecorder.getClass2InstanceMap(typeIdx);
349                }
350                if (typeIdx != undefined) {
351                    instTypeMap.set(thisTypeIndex, typeIdx);
352                }
353            }
354        }
355
356        // sort and save type info
357        let typeInfo = new Array<number>();
358        [...instTypeMap].sort((a, b) => a[0] - b[0]).forEach(([instIdx, typeIdx]) => {
359            if (typeIdx != PrimitiveType.ANY) {
360                typeInfo.push(instIdx);
361                typeInfo.push(typeIdx);
362            }
363        });
364
365        return typeInfo;
366    }
367
368    // @ts-ignore
369    static dumpPandaGen(pg: PandaGen, ts2abc: any, recordType?: boolean): void {
370        let funcName = pg.internalName;
371        let funcSignature = Ts2Panda.getFuncSignature(pg);
372        let funcInsnsAndRegsNum = Ts2Panda.getFuncInsnsAndRegsNum(pg);
373        let sourceFile = pg.getSourceFileDebugInfo();
374        let callType = pg.getCallType();
375        let typeRecord = pg.getLocals();
376        let typeInfo = undefined;
377        let exportedSymbol2Types: undefined | Array<ExportedSymbol2Type> = undefined;
378        let declaredSymbol2Types: undefined | Array<DeclaredSymbol2Type> = undefined;
379        if (CmdOptions.needRecordType() && CompilerDriver.isTsFile) {
380            if (CmdOptions.enableTypeLog()) {
381                console.log("[", funcName, "]");
382                typeRecord.forEach((vreg) => {
383                    console.log("---------------------------------------");
384                    console.log("- vreg name:", vreg.getVariableName());
385                    console.log("- vreg local num:", vreg.num);
386                    console.log("- vreg type:", vreg.getTypeIndex());
387                });
388            }
389            typeInfo = Ts2Panda.dumpInstTypeMap(pg);
390
391            if (funcName.endsWith("func_main_0")) {
392                let exportedTypes = PandaGen.getExportedTypes();
393                let declareddTypes = PandaGen.getDeclaredTypes();
394                if (exportedTypes.size != 0) {
395                    exportedSymbol2Types = new Array<ExportedSymbol2Type>();
396                    exportedTypes.forEach((type: number, symbol: string) => {
397                        let exportedSymbol2Type = new ExportedSymbol2Type(symbol, type);
398                        exportedSymbol2Types.push(exportedSymbol2Type);
399                    });
400                }
401                if (declareddTypes.size != 0) {
402                    declaredSymbol2Types = new Array<DeclaredSymbol2Type>();
403                    declareddTypes.forEach((type: number, symbol: string) => {
404                        let declaredSymbol2Type = new DeclaredSymbol2Type(symbol, type);
405                        declaredSymbol2Types.push(declaredSymbol2Type);
406                    });
407                }
408            }
409        }
410
411        if (pg.getScope() instanceof ModuleScope) {
412            Ts2Panda.moduleRecordlist.push(
413                makeModuleRecord((<ModuleScope>pg.getScope()).module())
414            );
415        }
416
417        let variables = undefined;
418        let sourceCode = undefined;
419        if (CmdOptions.needRecordSourceCode() || CmdOptions.isDebugMode()) {
420            // function's sourceCode will be undefined in debugMode
421            // if we don't need to record function-sourceCode
422            sourceCode = pg.getSourceCode();
423        }
424        if (CmdOptions.isDebugMode()) {
425            variables = pg.getVariableDebugInfoArray();
426        }
427
428        let catchTableArr;
429        let catchTables = generateCatchTables(pg.getCatchMap());
430        if (!catchTables) {
431            catchTableArr = undefined;
432        } else {
433            catchTableArr = [];
434            catchTables.forEach((catchTable) => {
435                let catchBeginLabel = catchTable.getCatchBeginLabel();
436                let labelPairs = catchTable.getLabelPairs();
437                labelPairs.forEach((labelPair) => {
438                    catchTableArr.push(new CatchTable(
439                        Ts2Panda.labelPrefix + labelPair.getBeginLabel().id,
440                        Ts2Panda.labelPrefix + labelPair.getEndLabel().id,
441                        Ts2Panda.labelPrefix + catchBeginLabel.id
442                    ));
443                });
444            });
445        }
446
447        let func = new Function(
448            funcName,
449            funcSignature,
450            funcInsnsAndRegsNum.regsNum,
451            funcInsnsAndRegsNum.insns,
452            funcInsnsAndRegsNum.labels,
453            variables,
454            catchTableArr,
455            sourceFile,
456            sourceCode,
457            callType,
458            typeInfo,
459            exportedSymbol2Types,
460            declaredSymbol2Types,
461            pg.getFunctionKind(),
462            pg.getIcSize()
463        );
464
465        LOGD(func);
466
467        let funcObject = {
468            "t": jsonType.function,
469            "fb": func
470        }
471        let jsonFuncUnicode = escapeUnicode(JSON.stringify(funcObject, null, 2));
472        jsonFuncUnicode = "$" + jsonFuncUnicode.replace(dollarSign, '#$') + "$";
473        jsonFuncUnicode = jsonFuncUnicode.replace(starSign, '#*');
474        if (CmdOptions.isEnableDebugLog()) {
475            Ts2Panda.jsonString += jsonFuncUnicode;
476        }
477        ts2abc.stdio[3].write(jsonFuncUnicode + '\n');
478    }
479
480    static dumpModuleRecords(ts2abc: any): void {
481        Ts2Panda.moduleRecordlist.forEach(function(module){
482            let moduleObject = {
483                "t": jsonType.module,
484                "mod": module
485            };
486            let jsonModuleUnicode = escapeUnicode(JSON.stringify(moduleObject, null, 2));
487            jsonModuleUnicode = "$" + jsonModuleUnicode.replace(dollarSign, '#$') + "$";
488            jsonModuleUnicode = jsonModuleUnicode.replace(starSign, '#*');
489            if (CmdOptions.isEnableDebugLog()) {
490                Ts2Panda.jsonString += jsonModuleUnicode;
491            }
492            ts2abc.stdio[3].write(jsonModuleUnicode + '\n');
493        });
494    }
495
496    static dumpTypeInfoRecord(ts2abc: any, recordType: boolean): void {
497        let enableTypeRecord = getRecordTypeFlag(recordType);
498        let typeSummaryIndex = getLiteralKey(CompilerDriver.srcNode, 0);
499        if (enableTypeRecord) {
500            typeSummaryIndex = getLiteralKey(CompilerDriver.srcNode, TypeRecorder.getInstance().getTypeSummaryIndex());
501        }
502        let typeInfo = {
503            'tf': enableTypeRecord,
504            'tsi': typeSummaryIndex
505        }
506        let typeInfoObject = {
507            't': jsonType.type_info,
508            'ti': typeInfo
509        };
510        let jsonTypeInfoUnicode = escapeUnicode(JSON.stringify(typeInfoObject, null, 2));
511        jsonTypeInfoUnicode = "$" + jsonTypeInfoUnicode.replace(dollarSign, '#$') + "$";
512        jsonTypeInfoUnicode = jsonTypeInfoUnicode.replace(starSign, '#*');
513        if (CmdOptions.isEnableDebugLog()) {
514            Ts2Panda.jsonString += jsonTypeInfoUnicode;
515        }
516        ts2abc.stdio[3].write(jsonTypeInfoUnicode + '\n');
517    }
518
519    static dumpInputJsonFileContent(ts2abc: any, inputJsonFileContent: string): void {
520        let inputJsonFileContentObject = {
521            "t": jsonType.input_json_file_content,
522            "ijfc": inputJsonFileContent
523        }
524
525        let jsonInputJsonFileContentUnicode = escapeUnicode(JSON.stringify(inputJsonFileContentObject, null, 2));
526        jsonInputJsonFileContentUnicode = "$" + jsonInputJsonFileContentUnicode.replace(dollarSign, '#$') + "$";
527        jsonInputJsonFileContentUnicode = jsonInputJsonFileContentUnicode.replace(starSign, '#*');
528        if (CmdOptions.isEnableDebugLog()) {
529            Ts2Panda.jsonString += jsonInputJsonFileContentUnicode;
530        }
531        ts2abc.stdio[3].write(jsonInputJsonFileContentUnicode + '\n');
532    }
533
534    static dumpOutputFileName(ts2abc: any, outputFileName: string): void {
535        let outputFileNameObject = {
536            "t": jsonType.output_filename,
537            "ofn": outputFileName
538        }
539
540        let jsonOutputFileNameUnicode = escapeUnicode(JSON.stringify(outputFileNameObject, null, 2));
541        jsonOutputFileNameUnicode = "$" + jsonOutputFileNameUnicode.replace(dollarSign, '#$') + "$";
542        jsonOutputFileNameUnicode = jsonOutputFileNameUnicode.replace(starSign, '#*');
543        if (CmdOptions.isEnableDebugLog()) {
544            Ts2Panda.jsonString += jsonOutputFileNameUnicode;
545        }
546        ts2abc.stdio[3].write(jsonOutputFileNameUnicode + '\n');
547        // seperator between program
548        ts2abc.stdio[3].write("*" + '\n');
549    }
550
551    static clearDumpData(): void {
552        Ts2Panda.strings.clear();
553        Ts2Panda.jsonString = "";
554        Ts2Panda.moduleRecordlist = [];
555    }
556}
557
558function makeModuleRecord(sourceTextModule: SourceTextModuleRecord): ModuleRecord {
559    let moduleRecord = new ModuleRecord();
560    moduleRecord.moduleName = sourceTextModule.getModuleName();
561
562    moduleRecord.moduleRequests = [...sourceTextModule.getModuleRequests()];
563
564    sourceTextModule.getRegularImportEntries().forEach(e => {
565        moduleRecord.regularImportEntries.push(new RegularImportEntry(e.localName!, e.importName!, e.moduleRequest!));
566    });
567
568    sourceTextModule.getNamespaceImportEntries().forEach(e => {
569        moduleRecord.namespaceImportEntries.push(new NamespaceImportEntry(e.localName!, e.moduleRequest!));
570    });
571
572    sourceTextModule.getLocalExportEntries().forEach(entries => {
573        entries.forEach(e => {
574            moduleRecord.localExportEntries.push(new LocalExportEntry(e.localName!, e.exportName!));
575        });
576    });
577
578    sourceTextModule.getIndirectExportEntries().forEach(e => {
579        moduleRecord.indirectExportEntries.push(new IndirectExportEntry(e.exportName!, e.importName!, e.moduleRequest!));
580    });
581
582    sourceTextModule.getStarExportEntries().forEach(e => {
583        moduleRecord.starExportEntries.push(e.moduleRequest!);
584    });
585
586    return moduleRecord;
587}
588