1/** 2 * @fileoverview Class MapParser will take in a Android build and construct 3 * several file name maps (physical address: file name) according to it. 4 * The map of each partitions is added by calling MapParser.add(partitionName). 5 * You can query the file name being operated by calling 6 * MapParser.query(address, datalength). 7 */ 8 9import * as zip from '@zip.js/zip.js/dist/zip-full.min.js' 10 11export class MapParser { 12 /** 13 * This class will take in a .zip Android build and construct a file type map 14 * @param {File} targetFile 15 */ 16 constructor(targetFile) { 17 this.build = new zip.ZipReader(new zip.BlobReader(targetFile)) 18 this.mapFiles = new Map() 19 this.maps = new Map() 20 } 21 22 /** 23 * Find the .map entries in the .zip build file. Store them as a map with 24 * pairs of (partition name: zip.js entry). 25 */ 26 async init() { 27 let /** Array<Entry> */ entries = await this.build.getEntries() 28 const /** RegExp*/ regexPath = /IMAGES\/[a-z_]*\.map/g; 29 const /** RegExp*/ regexName = /[\w_]+(?=\.map)/g 30 entries.forEach((entry) => { 31 if (entry.filename.match(regexPath)) { 32 this.mapFiles.set(entry.filename.match(regexName)[0], entry) 33 } 34 }); 35 } 36 37 /** 38 * According to the .map in the build, build a map for later query. 39 * @param {String} partitionName 40 * @param {Number} totalLength 41 */ 42 async add(partitionName, totalLength) { 43 let /** Array<String> */ map = [] 44 const /** RegExp */ regexNumber = /\d+/g 45 const /** Reg */ regexRange = /\d+\-\d+/g 46 for (let i = 0; i < totalLength; i++) map[i] = 'unknown' 47 if (this.mapFiles.get(partitionName)) { 48 let /** String */mapText = 49 await this.mapFiles.get(partitionName).getData( 50 new zip.TextWriter() 51 ) 52 let /** Array<String> */fileEntries = mapText.split('\n') 53 // Each line of the .map file in Android build starts with the filename 54 // Followed by the block address, either a number or a range, for example: 55 // //system/apex/com.android.adbd.apex 54-66 66 66-2663 56 for (let entry of fileEntries) { 57 let /** Array<String> */ elements = entry.split(' ') 58 for (let j = 1; j < elements.length; j++) { 59 let /** Number */ left = 0 60 let /** Number */ right = 0 61 if (elements[j].match(regexRange)) { 62 left = parseInt(elements[j].match(/\d+/g)[0]) 63 right = parseInt(elements[j].match(/\d+/g)[1]) 64 } else { 65 left = parseInt(elements[j].match(regexNumber)) 66 right = parseInt(elements[j].match(regexNumber)) 67 } 68 InsertMap(map, elements[0], left, right) 69 } 70 } 71 this.maps.set(partitionName, map) 72 } 73 else { 74 this.maps.set(partitionName, map) 75 } 76 } 77 78 /** 79 * Return the filename of given address. 80 * @param {String} partitionName 81 * @param {Array<PartitionUpdate>} extents 82 * @return {Array<String>} 83 */ 84 query(partitionName, extents) { 85 let /** Array<String> */ names = [] 86 let /** Array<String> */ map = this.maps.get(partitionName) 87 for (let ext of extents) { 88 names.push(queryMap(map, 89 ext.startBlock, 90 ext.startBlock + ext.numBlocks)) 91 } 92 return names 93 } 94} 95 96/** 97 * Fill in the hashtable from <left> to <right> using <name>. 98 * @param {Array<String>} map 99 * @param {String} name 100 * @param {Number} left 101 * @param {Number} right 102 */ 103function InsertMap(map, name, left, right) { 104 for (let i = left; i <= right; i++) { 105 map[i] = name 106 } 107} 108 109/** 110 * Query the hashtable <map> using index <address>. 111 * @param {Array<String>} map 112 * @param {Number} left 113 * @param {Number} right 114 */ 115function queryMap(map, left, right) { 116 // Assuming the consecutive blocks belong to the same file 117 // Only the start block is queried here. 118 if (!map[left]) { 119 return 'unknown' 120 } 121 return map[left] 122}