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