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