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