@futpib/parser 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/.claude/settings.local.json +24 -0
  2. package/.github/workflows/main.yml +1 -0
  3. package/build/androidPackageParser.js +30 -32
  4. package/build/arbitraryDalvikBytecode.d.ts +3 -3
  5. package/build/arbitraryDalvikBytecode.js +33 -27
  6. package/build/arbitraryDalvikExecutable.js +55 -17
  7. package/build/arbitraryJava.d.ts +31 -0
  8. package/build/arbitraryJava.js +532 -0
  9. package/build/arbitraryJavaScript.d.ts +3 -0
  10. package/build/arbitraryJavaScript.js +263 -0
  11. package/build/arbitraryJavascript.d.ts +3 -0
  12. package/build/arbitraryJavascript.js +263 -0
  13. package/build/arbitraryZig.d.ts +3 -0
  14. package/build/arbitraryZig.js +240 -0
  15. package/build/arbitraryZipStream.d.ts +1 -1
  16. package/build/arrayParser.js +72 -13
  17. package/build/backsmali.d.ts +4 -3
  18. package/build/backsmali.js +26 -6
  19. package/build/bash.d.ts +6 -1
  20. package/build/bashParser.js +131 -90
  21. package/build/bashParser.test.js +162 -0
  22. package/build/bashParserEdgeCases.test.d.ts +1 -0
  23. package/build/bashParserEdgeCases.test.js +117 -0
  24. package/build/dalvikBytecodeParser/addressConversion.d.ts +110 -0
  25. package/build/dalvikBytecodeParser/addressConversion.js +334 -0
  26. package/build/dalvikBytecodeParser/formatParsers.d.ts +7 -6
  27. package/build/dalvikBytecodeParser/formatParsers.js +13 -14
  28. package/build/dalvikBytecodeParser.d.ts +60 -31
  29. package/build/dalvikBytecodeParser.js +92 -35
  30. package/build/dalvikBytecodeParser.test-d.d.ts +1 -0
  31. package/build/dalvikBytecodeParser.test-d.js +268 -0
  32. package/build/dalvikBytecodeUnparser/formatUnparsers.d.ts +9 -8
  33. package/build/dalvikBytecodeUnparser/formatUnparsers.js +13 -12
  34. package/build/dalvikBytecodeUnparser.d.ts +2 -2
  35. package/build/dalvikBytecodeUnparser.js +23 -23
  36. package/build/dalvikBytecodeUnparser.test.js +7 -7
  37. package/build/dalvikExecutable.d.ts +3 -3
  38. package/build/dalvikExecutable.test-d.d.ts +1 -0
  39. package/build/dalvikExecutable.test-d.js +59 -0
  40. package/build/dalvikExecutableParser/typedNumbers.d.ts +18 -0
  41. package/build/dalvikExecutableParser/typedNumbers.js +3 -0
  42. package/build/dalvikExecutableParser.d.ts +2 -1
  43. package/build/dalvikExecutableParser.js +96 -77
  44. package/build/dalvikExecutableParser.test.js +24 -3
  45. package/build/dalvikExecutableParserAgainstSmaliParser.test.js +3 -0
  46. package/build/dalvikExecutableUnparser/poolScanners.d.ts +2 -2
  47. package/build/dalvikExecutableUnparser/sectionUnparsers.d.ts +3 -3
  48. package/build/dalvikExecutableUnparser/sectionUnparsers.js +26 -11
  49. package/build/dalvikExecutableUnparser.d.ts +2 -2
  50. package/build/dalvikExecutableUnparser.test.js +2 -1
  51. package/build/disjunctionParser.d.ts +5 -3
  52. package/build/disjunctionParser.js +79 -17
  53. package/build/disjunctionParser.test-d.d.ts +1 -0
  54. package/build/disjunctionParser.test-d.js +72 -0
  55. package/build/elementSwitchParser.d.ts +4 -0
  56. package/build/{exactElementSwitchParser.js → elementSwitchParser.js} +3 -4
  57. package/build/elementSwitchParser.test-d.d.ts +1 -0
  58. package/build/elementSwitchParser.test-d.js +44 -0
  59. package/build/exactSequenceParser.d.ts +4 -2
  60. package/build/exactSequenceParser.test-d.d.ts +1 -0
  61. package/build/exactSequenceParser.test-d.js +36 -0
  62. package/build/fetchCid.js +2 -66
  63. package/build/index.d.ts +3 -2
  64. package/build/index.js +2 -1
  65. package/build/index.test.js +16 -1
  66. package/build/inputReader.d.ts +10 -0
  67. package/build/inputReader.js +36 -0
  68. package/build/java.d.ts +502 -0
  69. package/build/java.js +2 -0
  70. package/build/javaKeyStoreParser.js +14 -17
  71. package/build/javaParser.d.ts +51 -0
  72. package/build/javaParser.js +1538 -0
  73. package/build/javaParser.test.d.ts +1 -0
  74. package/build/javaParser.test.js +1287 -0
  75. package/build/javaScript.d.ts +35 -0
  76. package/build/javaScript.js +1 -0
  77. package/build/javaScriptParser.d.ts +9 -0
  78. package/build/javaScriptParser.js +34 -0
  79. package/build/javaScriptUnparser.d.ts +3 -0
  80. package/build/javaScriptUnparser.js +4 -0
  81. package/build/javaScriptUnparser.test.d.ts +1 -0
  82. package/build/javaScriptUnparser.test.js +24 -0
  83. package/build/javaUnparser.d.ts +2 -0
  84. package/build/javaUnparser.js +519 -0
  85. package/build/javaUnparser.test.d.ts +1 -0
  86. package/build/javaUnparser.test.js +24 -0
  87. package/build/javascript.d.ts +35 -0
  88. package/build/javascript.js +1 -0
  89. package/build/javascriptParser.d.ts +9 -0
  90. package/build/javascriptParser.js +34 -0
  91. package/build/javascriptUnparser.d.ts +3 -0
  92. package/build/javascriptUnparser.js +4 -0
  93. package/build/javascriptUnparser.test.d.ts +1 -0
  94. package/build/javascriptUnparser.test.js +24 -0
  95. package/build/jsonParser.js +2 -12
  96. package/build/lazyMessageError.d.ts +3 -0
  97. package/build/lookaheadParser.js +60 -3
  98. package/build/negativeLookaheadParser.js +70 -11
  99. package/build/nonEmptyArrayParser.js +72 -13
  100. package/build/objectParser.d.ts +12 -0
  101. package/build/objectParser.js +31 -0
  102. package/build/objectParser.test-d.d.ts +1 -0
  103. package/build/objectParser.test-d.js +112 -0
  104. package/build/objectParser.test.d.ts +1 -0
  105. package/build/objectParser.test.js +55 -0
  106. package/build/optionalParser.js +69 -10
  107. package/build/parser.d.ts +4 -0
  108. package/build/parser.js +3 -1
  109. package/build/parser.test.js +114 -1
  110. package/build/parserConsumedSequenceParser.js +66 -7
  111. package/build/parserContext.d.ts +6 -0
  112. package/build/parserContext.js +20 -11
  113. package/build/parserError.d.ts +119 -27
  114. package/build/parserError.js +16 -8
  115. package/build/regexpParser.js +33 -3
  116. package/build/regexpParser.test.js +31 -0
  117. package/build/regularExpressionParser.js +35 -15
  118. package/build/separatedArrayParser.js +73 -14
  119. package/build/separatedNonEmptyArrayParser.js +73 -14
  120. package/build/sliceBoundedParser.js +62 -5
  121. package/build/smaliParser.d.ts +7 -7
  122. package/build/smaliParser.js +185 -268
  123. package/build/smaliParser.test.js +58 -0
  124. package/build/stringEscapes.d.ts +5 -0
  125. package/build/stringEscapes.js +244 -0
  126. package/build/symbolicExpression.d.ts +29 -0
  127. package/build/symbolicExpression.js +1 -0
  128. package/build/symbolicExpressionParser.d.ts +4 -0
  129. package/build/symbolicExpressionParser.js +123 -0
  130. package/build/symbolicExpressionParser.test.d.ts +1 -0
  131. package/build/symbolicExpressionParser.test.js +289 -0
  132. package/build/terminatedArrayParser.js +113 -38
  133. package/build/terminatedArrayParser.test.js +4 -2
  134. package/build/tupleParser.d.ts +7 -15
  135. package/build/tupleParser.js +1 -0
  136. package/build/unionParser.d.ts +5 -3
  137. package/build/unionParser.js +7 -2
  138. package/build/unionParser.test-d.d.ts +1 -0
  139. package/build/unionParser.test-d.js +72 -0
  140. package/build/unionParser.test.js +10 -11
  141. package/build/zig.d.ts +280 -0
  142. package/build/zig.js +2 -0
  143. package/build/zigParser.d.ts +3 -0
  144. package/build/zigParser.js +1119 -0
  145. package/build/zigParser.test.d.ts +1 -0
  146. package/build/zigParser.test.js +1590 -0
  147. package/build/zigUnparser.d.ts +2 -0
  148. package/build/zigUnparser.js +460 -0
  149. package/build/zigUnparser.test.d.ts +1 -0
  150. package/build/zigUnparser.test.js +24 -0
  151. package/build/zipParser.js +19 -32
  152. package/build/zipUnparser.js +19 -7
  153. package/build/zipUnparser.test.js +1 -1
  154. package/node_modules-@types/s-expression/index.d.ts +5 -0
  155. package/package.json +24 -6
  156. package/src/androidPackageParser.ts +33 -60
  157. package/src/arbitraryDalvikBytecode.ts +39 -31
  158. package/src/arbitraryDalvikExecutable.ts +65 -20
  159. package/src/arbitraryJava.ts +804 -0
  160. package/src/arbitraryJavaScript.ts +410 -0
  161. package/src/arbitraryZig.ts +380 -0
  162. package/src/arrayParser.ts +1 -3
  163. package/src/backsmali.ts +35 -4
  164. package/src/bash.ts +8 -1
  165. package/src/bashParser.test.ts +258 -0
  166. package/src/bashParser.ts +180 -143
  167. package/src/dalvikBytecodeParser/addressConversion.ts +496 -0
  168. package/src/dalvikBytecodeParser/formatParsers.ts +19 -29
  169. package/src/dalvikBytecodeParser.test-d.ts +310 -0
  170. package/src/dalvikBytecodeParser.ts +194 -69
  171. package/src/dalvikBytecodeUnparser/formatUnparsers.ts +27 -26
  172. package/src/dalvikBytecodeUnparser.test.ts +7 -7
  173. package/src/dalvikBytecodeUnparser.ts +31 -30
  174. package/src/dalvikExecutable.test-d.ts +132 -0
  175. package/src/dalvikExecutable.ts +3 -3
  176. package/src/dalvikExecutableParser/typedNumbers.ts +11 -0
  177. package/src/dalvikExecutableParser.test.ts +37 -3
  178. package/src/dalvikExecutableParser.test.ts.md +163 -2
  179. package/src/dalvikExecutableParser.test.ts.snap +0 -0
  180. package/src/dalvikExecutableParser.ts +121 -139
  181. package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +4 -0
  182. package/src/dalvikExecutableUnparser/poolScanners.ts +6 -6
  183. package/src/dalvikExecutableUnparser/sectionUnparsers.ts +38 -14
  184. package/src/dalvikExecutableUnparser.test.ts +3 -2
  185. package/src/dalvikExecutableUnparser.ts +4 -4
  186. package/src/disjunctionParser.test-d.ts +105 -0
  187. package/src/disjunctionParser.ts +18 -15
  188. package/src/elementSwitchParser.test-d.ts +74 -0
  189. package/src/elementSwitchParser.ts +51 -0
  190. package/src/exactSequenceParser.test-d.ts +43 -0
  191. package/src/exactSequenceParser.ts +13 -8
  192. package/src/fetchCid.ts +2 -76
  193. package/src/index.test.ts +22 -1
  194. package/src/index.ts +7 -1
  195. package/src/inputReader.ts +53 -0
  196. package/src/java.ts +708 -0
  197. package/src/javaKeyStoreParser.ts +18 -32
  198. package/src/javaParser.test.ts +1592 -0
  199. package/src/javaParser.ts +2640 -0
  200. package/src/javaScript.ts +36 -0
  201. package/src/javaScriptParser.ts +57 -0
  202. package/src/javaScriptUnparser.test.ts +37 -0
  203. package/src/javaScriptUnparser.ts +7 -0
  204. package/src/javaUnparser.test.ts +37 -0
  205. package/src/javaUnparser.ts +640 -0
  206. package/src/jsonParser.ts +6 -27
  207. package/src/lookaheadParser.ts +2 -6
  208. package/src/negativeLookaheadParser.ts +1 -3
  209. package/src/nonEmptyArrayParser.ts +1 -3
  210. package/src/objectParser.test-d.ts +152 -0
  211. package/src/objectParser.test.ts +71 -0
  212. package/src/objectParser.ts +69 -0
  213. package/src/optionalParser.ts +1 -3
  214. package/src/parser.test.ts +151 -4
  215. package/src/parser.ts +11 -1
  216. package/src/parserConsumedSequenceParser.ts +2 -4
  217. package/src/parserContext.ts +26 -11
  218. package/src/parserError.ts +17 -3
  219. package/src/regexpParser.test.ts +78 -0
  220. package/src/regexpParser.ts +35 -3
  221. package/src/regularExpressionParser.ts +36 -37
  222. package/src/separatedArrayParser.ts +1 -3
  223. package/src/separatedNonEmptyArrayParser.ts +1 -3
  224. package/src/sliceBoundedParser.test.ts +2 -2
  225. package/src/sliceBoundedParser.ts +15 -19
  226. package/src/smaliParser.test.ts +64 -0
  227. package/src/smaliParser.test.ts.md +12 -12
  228. package/src/smaliParser.test.ts.snap +0 -0
  229. package/src/smaliParser.ts +246 -534
  230. package/src/stringEscapes.ts +253 -0
  231. package/src/symbolicExpression.ts +17 -0
  232. package/src/symbolicExpressionParser.test.ts +466 -0
  233. package/src/symbolicExpressionParser.ts +190 -0
  234. package/src/terminatedArrayParser.test.ts +9 -6
  235. package/src/terminatedArrayParser.ts +25 -29
  236. package/src/tupleParser.ts +21 -18
  237. package/src/unionParser.test-d.ts +105 -0
  238. package/src/unionParser.test.ts +18 -17
  239. package/src/unionParser.ts +28 -16
  240. package/src/zig.ts +411 -0
  241. package/src/zigParser.test.ts +1693 -0
  242. package/src/zigParser.ts +1745 -0
  243. package/src/zigUnparser.test.ts +37 -0
  244. package/src/zigUnparser.ts +615 -0
  245. package/src/zipParser.ts +20 -56
  246. package/src/zipUnparser.test.ts +1 -1
  247. package/src/zipUnparser.ts +22 -7
  248. package/tsconfig.json +2 -2
  249. package/build/exactElementSwitchParser.d.ts +0 -3
  250. package/src/exactElementSwitchParser.ts +0 -41
@@ -0,0 +1,110 @@
1
+ import { type RawDalvikBytecodeOperation, type IndexResolvedOperation } from '../dalvikBytecodeParser.js';
2
+ import { type CodeUnit, type InstructionIndex } from '../dalvikExecutableParser/typedNumbers.js';
3
+ /**
4
+ * Map from code unit offset to instruction index.
5
+ * Keys are raw code unit offsets, values are instruction indices.
6
+ */
7
+ export type CodeUnitToIndexMap = Map<CodeUnit, InstructionIndex>;
8
+ /**
9
+ * Map from instruction index to code unit offset.
10
+ * Keys are instruction indices, values are raw code unit offsets.
11
+ */
12
+ export type IndexToCodeUnitMap = Map<InstructionIndex, CodeUnit>;
13
+ /**
14
+ * Calculate the size of an operation in code units (16-bit words).
15
+ * Works with any tier of operation (Tier 1, 2, 3, or SmaliCodeOperation).
16
+ */
17
+ export declare function getOperationSizeInCodeUnits(operation: {
18
+ operation: string;
19
+ data?: Uint8Array | number[];
20
+ }): number;
21
+ /**
22
+ * Build mapping from code unit offset to instruction index.
23
+ * Also maps the end position (total code units) to instructions.length.
24
+ */
25
+ export declare function buildCodeUnitToIndexMap(instructions: RawDalvikBytecodeOperation[]): CodeUnitToIndexMap;
26
+ /**
27
+ * Build mapping from instruction index to code unit offset.
28
+ * Also maps instructions.length to the total code units.
29
+ */
30
+ export declare function buildIndexToCodeUnitMap(instructions: RawDalvikBytecodeOperation[]): IndexToCodeUnitMap;
31
+ /**
32
+ * Convert a code unit offset to an instruction index.
33
+ */
34
+ export declare function codeUnitToInstructionIndex(codeUnitOffset: CodeUnit, codeUnitToIndexMap: CodeUnitToIndexMap): InstructionIndex;
35
+ /**
36
+ * Convert an instruction index to a code unit offset.
37
+ */
38
+ export declare function instructionIndexToCodeUnit(instructionIndex: InstructionIndex, indexToCodeUnitMap: IndexToCodeUnitMap): CodeUnit;
39
+ /**
40
+ * Convert a relative branch offset (in code units) to a relative instruction offset.
41
+ * The offset is relative to the source instruction.
42
+ */
43
+ export declare function convertBranchOffsetToInstructionOffset(branchOffsetInCodeUnits: CodeUnit, sourceInstructionIndex: InstructionIndex, indexToCodeUnitMap: IndexToCodeUnitMap, codeUnitToIndexMap: CodeUnitToIndexMap): InstructionIndex;
44
+ /**
45
+ * Convert a relative instruction offset back to a code unit offset.
46
+ * The offset is relative to the source instruction.
47
+ */
48
+ export declare function convertInstructionOffsetToBranchOffset(instructionOffset: InstructionIndex, sourceInstructionIndex: InstructionIndex, indexToCodeUnitMap: IndexToCodeUnitMap): CodeUnit;
49
+ /**
50
+ * Find the switch instruction that references a payload at the given index.
51
+ * Returns the index of the switch instruction.
52
+ */
53
+ export declare function findSwitchInstructionForPayload(instructions: RawDalvikBytecodeOperation[], payloadIndex: InstructionIndex, indexToCodeUnitMap: IndexToCodeUnitMap): InstructionIndex;
54
+ export type ConvertedBranchOffsetOperation<T> = T extends {
55
+ branchOffsetCodeUnit: CodeUnit;
56
+ } ? Omit<T, 'branchOffsetCodeUnit'> & {
57
+ branchOffsetIndex: InstructionIndex;
58
+ } : T extends {
59
+ branchOffsetsCodeUnit: CodeUnit[];
60
+ } ? Omit<T, 'branchOffsetsCodeUnit'> & {
61
+ branchOffsetsIndex: InstructionIndex[];
62
+ } : T;
63
+ export type ConvertedRawDalvikBytecodeOperation = ConvertedBranchOffsetOperation<RawDalvikBytecodeOperation>;
64
+ export type ResolvedBranchOffsetOperation<T> = T extends {
65
+ branchOffsetIndex: InstructionIndex;
66
+ } ? Omit<T, 'branchOffsetIndex'> & {
67
+ targetInstructionIndex: number;
68
+ } : T extends {
69
+ branchOffsetsIndex: InstructionIndex[];
70
+ } ? Omit<T, 'branchOffsetsIndex'> & {
71
+ targetInstructionIndices: number[];
72
+ } : T;
73
+ export type DalvikBytecodeOperation = IndexResolvedOperation<ResolvedBranchOffsetOperation<ConvertedRawDalvikBytecodeOperation>>;
74
+ export type DalvikBytecode = DalvikBytecodeOperation[];
75
+ /**
76
+ * Convert all branch offsets in instructions from code units to instruction offsets.
77
+ * Tier 1 (CodeUnit) -> Tier 2 (InstructionIndex)
78
+ */
79
+ export declare function convertBranchOffsetsToInstructionOffsets(instructions: RawDalvikBytecodeOperation[]): ConvertedRawDalvikBytecodeOperation[];
80
+ /**
81
+ * Unwrap instruction indices to plain numbers and convert relative to absolute.
82
+ * Tier 2 (InstructionIndex, relative) -> Tier 3 (plain number, absolute)
83
+ */
84
+ export declare function unwrapBranchOffsets(instructions: ConvertedRawDalvikBytecodeOperation[]): DalvikBytecodeOperation[];
85
+ /**
86
+ * Wrap plain numbers to instruction indices and convert absolute to relative.
87
+ * Tier 3 (plain number, absolute) -> Tier 2 (InstructionIndex, relative)
88
+ */
89
+ export declare function wrapBranchOffsets(instructions: DalvikBytecodeOperation[]): ConvertedRawDalvikBytecodeOperation[];
90
+ /**
91
+ * Build mapping from instruction index to code unit offset.
92
+ * Works with any tier of operations.
93
+ */
94
+ export declare function buildIndexToCodeUnitMapGeneric(instructions: Array<{
95
+ operation: string;
96
+ data?: Uint8Array | number[];
97
+ }>): IndexToCodeUnitMap;
98
+ /**
99
+ * Build mapping from instruction index to code unit offset for Tier 2 operations.
100
+ */
101
+ export declare function buildIndexToCodeUnitMapFromConverted(instructions: ConvertedRawDalvikBytecodeOperation[]): IndexToCodeUnitMap;
102
+ /**
103
+ * Build mapping from instruction index to code unit offset for Tier 3 operations.
104
+ */
105
+ export declare function buildIndexToCodeUnitMapFromResolved(instructions: DalvikBytecodeOperation[]): IndexToCodeUnitMap;
106
+ /**
107
+ * Convert all branch offsets in instructions from instruction offsets back to code units.
108
+ * Tier 2 (InstructionIndex) -> Tier 1 (CodeUnit)
109
+ */
110
+ export declare function convertInstructionOffsetsToBranchOffsets(instructions: ConvertedRawDalvikBytecodeOperation[]): RawDalvikBytecodeOperation[];
@@ -0,0 +1,334 @@
1
+ import { isoCodeUnit, isoInstructionIndex, } from '../dalvikExecutableParser/typedNumbers.js';
2
+ import { operationFormats } from './operationFormats.js';
3
+ import { formatSizes } from './formatSizes.js';
4
+ /**
5
+ * Get the branch offsets array length from an operation, regardless of tier.
6
+ * Works with all field name variants: branchOffsetsCodeUnit, branchOffsetsIndex, targetInstructionIndices, branchOffsetIndices.
7
+ */
8
+ function getBranchOffsetsLength(operation) {
9
+ const op = operation;
10
+ return op.branchOffsetsCodeUnit?.length
11
+ ?? op.branchOffsetsIndex?.length
12
+ ?? op.targetInstructionIndices?.length
13
+ ?? op.branchOffsetIndices?.length;
14
+ }
15
+ /**
16
+ * Calculate the size of an operation in code units (16-bit words).
17
+ * Works with any tier of operation (Tier 1, 2, 3, or SmaliCodeOperation).
18
+ */
19
+ export function getOperationSizeInCodeUnits(operation) {
20
+ if (operation.operation === 'packed-switch-payload') {
21
+ const length = getBranchOffsetsLength(operation);
22
+ if (length === undefined) {
23
+ throw new Error('packed-switch-payload missing branch offsets array');
24
+ }
25
+ // Header (4 code units) + targets (2 code units each)
26
+ return (length * 2) + 4;
27
+ }
28
+ if (operation.operation === 'sparse-switch-payload') {
29
+ const length = getBranchOffsetsLength(operation);
30
+ if (length === undefined) {
31
+ throw new Error('sparse-switch-payload missing branch offsets array');
32
+ }
33
+ // Header (2 code units) + keys (2 code units each) + targets (2 code units each)
34
+ return (length * 4) + 2;
35
+ }
36
+ if (operation.operation === 'fill-array-data-payload') {
37
+ // data.length is already the total number of bytes (size * elementWidth)
38
+ const dataSize = operation.data.length;
39
+ const paddingSize = dataSize % 2;
40
+ const totalBytes = 8 + dataSize + paddingSize; // header (8 bytes) + data + padding
41
+ return totalBytes / 2; // Convert to code units
42
+ }
43
+ const format = operationFormats[operation.operation];
44
+ if (!format) {
45
+ throw new Error(`Unknown operation format: ${operation.operation}`);
46
+ }
47
+ return formatSizes[format];
48
+ }
49
+ /**
50
+ * Build mapping from code unit offset to instruction index.
51
+ * Also maps the end position (total code units) to instructions.length.
52
+ */
53
+ export function buildCodeUnitToIndexMap(instructions) {
54
+ const map = new Map();
55
+ let codeUnitOffset = 0;
56
+ for (let index = 0; index < instructions.length; index++) {
57
+ map.set(isoCodeUnit.wrap(codeUnitOffset), isoInstructionIndex.wrap(index));
58
+ codeUnitOffset += getOperationSizeInCodeUnits(instructions[index]);
59
+ }
60
+ // Map the end position
61
+ map.set(isoCodeUnit.wrap(codeUnitOffset), isoInstructionIndex.wrap(instructions.length));
62
+ return map;
63
+ }
64
+ /**
65
+ * Build mapping from instruction index to code unit offset.
66
+ * Also maps instructions.length to the total code units.
67
+ */
68
+ export function buildIndexToCodeUnitMap(instructions) {
69
+ const map = new Map();
70
+ let codeUnitOffset = 0;
71
+ for (let index = 0; index < instructions.length; index++) {
72
+ map.set(isoInstructionIndex.wrap(index), isoCodeUnit.wrap(codeUnitOffset));
73
+ codeUnitOffset += getOperationSizeInCodeUnits(instructions[index]);
74
+ }
75
+ // Map the end position
76
+ map.set(isoInstructionIndex.wrap(instructions.length), isoCodeUnit.wrap(codeUnitOffset));
77
+ return map;
78
+ }
79
+ /**
80
+ * Convert a code unit offset to an instruction index.
81
+ */
82
+ export function codeUnitToInstructionIndex(codeUnitOffset, codeUnitToIndexMap) {
83
+ const index = codeUnitToIndexMap.get(codeUnitOffset);
84
+ if (index === undefined) {
85
+ throw new Error(`Invalid code unit offset: ${isoCodeUnit.unwrap(codeUnitOffset)}. Valid offsets: ${[...codeUnitToIndexMap.keys()].map(k => isoCodeUnit.unwrap(k)).join(', ')}`);
86
+ }
87
+ return index;
88
+ }
89
+ /**
90
+ * Convert an instruction index to a code unit offset.
91
+ */
92
+ export function instructionIndexToCodeUnit(instructionIndex, indexToCodeUnitMap) {
93
+ const offset = indexToCodeUnitMap.get(instructionIndex);
94
+ if (offset === undefined) {
95
+ throw new Error(`Invalid instruction index: ${isoInstructionIndex.unwrap(instructionIndex)}. Valid indices: ${[...indexToCodeUnitMap.keys()].map(k => isoInstructionIndex.unwrap(k)).join(', ')}`);
96
+ }
97
+ return offset;
98
+ }
99
+ /**
100
+ * Convert a relative branch offset (in code units) to a relative instruction offset.
101
+ * The offset is relative to the source instruction.
102
+ */
103
+ export function convertBranchOffsetToInstructionOffset(branchOffsetInCodeUnits, sourceInstructionIndex, indexToCodeUnitMap, codeUnitToIndexMap) {
104
+ const sourceCodeUnit = indexToCodeUnitMap.get(sourceInstructionIndex);
105
+ if (sourceCodeUnit === undefined) {
106
+ throw new Error(`Invalid source instruction index: ${isoInstructionIndex.unwrap(sourceInstructionIndex)}`);
107
+ }
108
+ const targetCodeUnit = isoCodeUnit.wrap(isoCodeUnit.unwrap(sourceCodeUnit) + isoCodeUnit.unwrap(branchOffsetInCodeUnits));
109
+ const targetIndex = codeUnitToIndexMap.get(targetCodeUnit);
110
+ if (targetIndex === undefined) {
111
+ throw new Error(`Invalid branch target code unit: ${isoCodeUnit.unwrap(targetCodeUnit)} (from source ${isoCodeUnit.unwrap(sourceCodeUnit)} + offset ${isoCodeUnit.unwrap(branchOffsetInCodeUnits)})`);
112
+ }
113
+ return isoInstructionIndex.wrap(isoInstructionIndex.unwrap(targetIndex) - isoInstructionIndex.unwrap(sourceInstructionIndex));
114
+ }
115
+ /**
116
+ * Convert a relative instruction offset back to a code unit offset.
117
+ * The offset is relative to the source instruction.
118
+ */
119
+ export function convertInstructionOffsetToBranchOffset(instructionOffset, sourceInstructionIndex, indexToCodeUnitMap) {
120
+ const sourceCodeUnit = indexToCodeUnitMap.get(sourceInstructionIndex);
121
+ if (sourceCodeUnit === undefined) {
122
+ throw new Error(`Invalid source instruction index: ${isoInstructionIndex.unwrap(sourceInstructionIndex)}`);
123
+ }
124
+ const targetInstructionIndex = isoInstructionIndex.wrap(isoInstructionIndex.unwrap(sourceInstructionIndex) + isoInstructionIndex.unwrap(instructionOffset));
125
+ const targetCodeUnit = indexToCodeUnitMap.get(targetInstructionIndex);
126
+ if (targetCodeUnit === undefined) {
127
+ throw new Error(`Invalid target instruction index: ${isoInstructionIndex.unwrap(targetInstructionIndex)}`);
128
+ }
129
+ return isoCodeUnit.wrap(isoCodeUnit.unwrap(targetCodeUnit) - isoCodeUnit.unwrap(sourceCodeUnit));
130
+ }
131
+ /**
132
+ * Find the switch instruction that references a payload at the given index.
133
+ * Returns the index of the switch instruction.
134
+ */
135
+ export function findSwitchInstructionForPayload(instructions, payloadIndex, indexToCodeUnitMap) {
136
+ const payloadCodeUnit = indexToCodeUnitMap.get(payloadIndex);
137
+ if (payloadCodeUnit === undefined) {
138
+ throw new Error(`Invalid payload index: ${isoInstructionIndex.unwrap(payloadIndex)}`);
139
+ }
140
+ for (let i = 0; i < instructions.length; i++) {
141
+ const inst = instructions[i];
142
+ if ((inst.operation === 'packed-switch' || inst.operation === 'sparse-switch')
143
+ && 'branchOffsetCodeUnit' in inst) {
144
+ const sourceCodeUnit = indexToCodeUnitMap.get(isoInstructionIndex.wrap(i));
145
+ if (sourceCodeUnit !== undefined && isoCodeUnit.unwrap(sourceCodeUnit) + isoCodeUnit.unwrap(inst.branchOffsetCodeUnit) === isoCodeUnit.unwrap(payloadCodeUnit)) {
146
+ return isoInstructionIndex.wrap(i);
147
+ }
148
+ }
149
+ }
150
+ throw new Error(`No switch instruction found for payload at index ${isoInstructionIndex.unwrap(payloadIndex)}`);
151
+ }
152
+ /**
153
+ * Convert all branch offsets in instructions from code units to instruction offsets.
154
+ * Tier 1 (CodeUnit) -> Tier 2 (InstructionIndex)
155
+ */
156
+ export function convertBranchOffsetsToInstructionOffsets(instructions) {
157
+ const codeUnitToIndexMap = buildCodeUnitToIndexMap(instructions);
158
+ const indexToCodeUnitMap = buildIndexToCodeUnitMap(instructions);
159
+ return instructions.map((instruction, index) => {
160
+ // Handle single branchOffsetCodeUnit (goto, if-*, packed-switch, sparse-switch, fill-array-data)
161
+ if ('branchOffsetCodeUnit' in instruction) {
162
+ const { branchOffsetCodeUnit, ...rest } = instruction;
163
+ return {
164
+ ...rest,
165
+ branchOffsetIndex: convertBranchOffsetToInstructionOffset(branchOffsetCodeUnit, isoInstructionIndex.wrap(index), indexToCodeUnitMap, codeUnitToIndexMap),
166
+ };
167
+ }
168
+ // Handle branchOffsetsCodeUnit array (packed-switch-payload, sparse-switch-payload)
169
+ if ('branchOffsetsCodeUnit' in instruction) {
170
+ const { branchOffsetsCodeUnit, ...rest } = instruction;
171
+ // For payload instructions, find the referring switch instruction
172
+ const sourceIndex = findSwitchInstructionForPayload(instructions, isoInstructionIndex.wrap(index), indexToCodeUnitMap);
173
+ return {
174
+ ...rest,
175
+ branchOffsetsIndex: branchOffsetsCodeUnit.map(offset => convertBranchOffsetToInstructionOffset(offset, sourceIndex, indexToCodeUnitMap, codeUnitToIndexMap)),
176
+ };
177
+ }
178
+ return instruction;
179
+ });
180
+ }
181
+ /**
182
+ * Unwrap instruction indices to plain numbers and convert relative to absolute.
183
+ * Tier 2 (InstructionIndex, relative) -> Tier 3 (plain number, absolute)
184
+ */
185
+ export function unwrapBranchOffsets(instructions) {
186
+ // First pass: find switch instruction indices for payloads
187
+ const payloadToSwitchIndex = new Map();
188
+ for (let i = 0; i < instructions.length; i++) {
189
+ const inst = instructions[i];
190
+ if ((inst.operation === 'packed-switch' || inst.operation === 'sparse-switch')
191
+ && 'branchOffsetIndex' in inst) {
192
+ const payloadIndex = i + isoInstructionIndex.unwrap(inst.branchOffsetIndex);
193
+ payloadToSwitchIndex.set(payloadIndex, i);
194
+ }
195
+ }
196
+ return instructions.map((instruction, index) => {
197
+ if ('branchOffsetIndex' in instruction) {
198
+ const { branchOffsetIndex, ...rest } = instruction;
199
+ // Convert relative offset to absolute index
200
+ const targetInstructionIndex = index + isoInstructionIndex.unwrap(branchOffsetIndex);
201
+ return {
202
+ ...rest,
203
+ targetInstructionIndex,
204
+ };
205
+ }
206
+ if ('branchOffsetsIndex' in instruction) {
207
+ const { branchOffsetsIndex, ...rest } = instruction;
208
+ // For payloads, the offsets are relative to the switch instruction, not the payload
209
+ const switchIndex = payloadToSwitchIndex.get(index);
210
+ if (switchIndex === undefined) {
211
+ throw new Error(`No switch instruction found for payload at index ${index}`);
212
+ }
213
+ // Convert relative offsets to absolute indices
214
+ const targetInstructionIndices = branchOffsetsIndex.map(offset => switchIndex + isoInstructionIndex.unwrap(offset));
215
+ return {
216
+ ...rest,
217
+ targetInstructionIndices,
218
+ };
219
+ }
220
+ return instruction;
221
+ });
222
+ }
223
+ /**
224
+ * Wrap plain numbers to instruction indices and convert absolute to relative.
225
+ * Tier 3 (plain number, absolute) -> Tier 2 (InstructionIndex, relative)
226
+ */
227
+ export function wrapBranchOffsets(instructions) {
228
+ // First pass: find switch instruction indices for payloads
229
+ const payloadToSwitchIndex = new Map();
230
+ for (let i = 0; i < instructions.length; i++) {
231
+ const inst = instructions[i];
232
+ if ((inst.operation === 'packed-switch' || inst.operation === 'sparse-switch')
233
+ && 'targetInstructionIndex' in inst) {
234
+ const payloadIndex = inst.targetInstructionIndex;
235
+ payloadToSwitchIndex.set(payloadIndex, i);
236
+ }
237
+ }
238
+ return instructions.map((instruction, index) => {
239
+ if ('targetInstructionIndex' in instruction) {
240
+ const { targetInstructionIndex, ...rest } = instruction;
241
+ // Convert absolute index to relative offset
242
+ const branchOffsetIndex = isoInstructionIndex.wrap(targetInstructionIndex - index);
243
+ return {
244
+ ...rest,
245
+ branchOffsetIndex,
246
+ };
247
+ }
248
+ if ('targetInstructionIndices' in instruction) {
249
+ const { targetInstructionIndices, ...rest } = instruction;
250
+ // For payloads, the offsets are relative to the switch instruction, not the payload
251
+ const switchIndex = payloadToSwitchIndex.get(index);
252
+ if (switchIndex === undefined) {
253
+ throw new Error(`No switch instruction found for payload at index ${index}`);
254
+ }
255
+ // Convert absolute indices to relative offsets from switch instruction
256
+ const branchOffsetsIndex = targetInstructionIndices.map(target => isoInstructionIndex.wrap(target - switchIndex));
257
+ return {
258
+ ...rest,
259
+ branchOffsetsIndex,
260
+ };
261
+ }
262
+ return instruction;
263
+ });
264
+ }
265
+ /**
266
+ * Build mapping from instruction index to code unit offset.
267
+ * Works with any tier of operations.
268
+ */
269
+ export function buildIndexToCodeUnitMapGeneric(instructions) {
270
+ const map = new Map();
271
+ let codeUnitOffset = 0;
272
+ for (let index = 0; index < instructions.length; index++) {
273
+ map.set(isoInstructionIndex.wrap(index), isoCodeUnit.wrap(codeUnitOffset));
274
+ codeUnitOffset += getOperationSizeInCodeUnits(instructions[index]);
275
+ }
276
+ // Map the end position
277
+ map.set(isoInstructionIndex.wrap(instructions.length), isoCodeUnit.wrap(codeUnitOffset));
278
+ return map;
279
+ }
280
+ /**
281
+ * Build mapping from instruction index to code unit offset for Tier 2 operations.
282
+ */
283
+ export function buildIndexToCodeUnitMapFromConverted(instructions) {
284
+ return buildIndexToCodeUnitMapGeneric(instructions);
285
+ }
286
+ /**
287
+ * Build mapping from instruction index to code unit offset for Tier 3 operations.
288
+ */
289
+ export function buildIndexToCodeUnitMapFromResolved(instructions) {
290
+ return buildIndexToCodeUnitMapGeneric(instructions);
291
+ }
292
+ /**
293
+ * Find the switch instruction that references a payload at the given index,
294
+ * when branch offsets are in instruction indices (Tier 2).
295
+ */
296
+ function findSwitchInstructionForPayloadByInstructionIndex(instructions, payloadIndex) {
297
+ for (let i = 0; i < instructions.length; i++) {
298
+ const inst = instructions[i];
299
+ if ((inst.operation === 'packed-switch' || inst.operation === 'sparse-switch')
300
+ && 'branchOffsetIndex' in inst
301
+ && i + isoInstructionIndex.unwrap(inst.branchOffsetIndex) === isoInstructionIndex.unwrap(payloadIndex)) {
302
+ return isoInstructionIndex.wrap(i);
303
+ }
304
+ }
305
+ throw new Error(`No switch instruction found for payload at index ${isoInstructionIndex.unwrap(payloadIndex)}`);
306
+ }
307
+ /**
308
+ * Convert all branch offsets in instructions from instruction offsets back to code units.
309
+ * Tier 2 (InstructionIndex) -> Tier 1 (CodeUnit)
310
+ */
311
+ export function convertInstructionOffsetsToBranchOffsets(instructions) {
312
+ const indexToCodeUnitMap = buildIndexToCodeUnitMapFromConverted(instructions);
313
+ return instructions.map((instruction, index) => {
314
+ // Handle single branchOffsetIndex
315
+ if ('branchOffsetIndex' in instruction) {
316
+ const { branchOffsetIndex, ...rest } = instruction;
317
+ return {
318
+ ...rest,
319
+ branchOffsetCodeUnit: convertInstructionOffsetToBranchOffset(branchOffsetIndex, isoInstructionIndex.wrap(index), indexToCodeUnitMap),
320
+ };
321
+ }
322
+ // Handle branchOffsetsIndex array
323
+ if ('branchOffsetsIndex' in instruction) {
324
+ const { branchOffsetsIndex, ...rest } = instruction;
325
+ // For payload instructions, find the referring switch instruction
326
+ const sourceIndex = findSwitchInstructionForPayloadByInstructionIndex(instructions, isoInstructionIndex.wrap(index));
327
+ return {
328
+ ...rest,
329
+ branchOffsetsCodeUnit: branchOffsetsIndex.map(offset => convertInstructionOffsetToBranchOffset(offset, sourceIndex, indexToCodeUnitMap)),
330
+ };
331
+ }
332
+ return instruction;
333
+ });
334
+ }
@@ -1,8 +1,9 @@
1
1
  import { type Iso } from 'monocle-ts';
2
+ import { type CodeUnit } from '../dalvikExecutableParser/typedNumbers.js';
2
3
  import { type Parser } from '../parser.js';
3
4
  export declare const nibblesParser: Parser<[number, number], Uint8Array>;
4
5
  type DalvikBytecodeFormat10t = {
5
- branchOffset: number;
6
+ branchOffsetCodeUnit: CodeUnit;
6
7
  };
7
8
  export declare const dalvikBytecodeFormat10tParser: Parser<DalvikBytecodeFormat10t, Uint8Array>;
8
9
  type DalvikBytecodeFormat10x = void;
@@ -21,7 +22,7 @@ type DalvikBytecodeFormat12x = {
21
22
  };
22
23
  export declare const dalvikBytecodeFormat12xParser: Parser<DalvikBytecodeFormat12x, Uint8Array>;
23
24
  type DalvikBytecodeFormat20t = {
24
- branchOffset: number;
25
+ branchOffsetCodeUnit: CodeUnit;
25
26
  };
26
27
  export declare const dalvikBytecodeFormat20tParser: Parser<DalvikBytecodeFormat20t, Uint8Array>;
27
28
  type DalvikBytecodeFormat21c<Index> = {
@@ -37,7 +38,7 @@ type DalvikBytecodeFormat21h = {
37
38
  };
38
39
  export declare const dalvikBytecodeFormat21hParser: Parser<DalvikBytecodeFormat21h, Uint8Array>;
39
40
  type DalvikBytecodeFormat21t = {
40
- branchOffset: number;
41
+ branchOffsetCodeUnit: CodeUnit;
41
42
  registers: number[];
42
43
  };
43
44
  export declare const createDalvikBytecodeFormat21tParser: () => Parser<DalvikBytecodeFormat21t, Uint8Array>;
@@ -64,7 +65,7 @@ type DalvikBytecodeFormat22s = {
64
65
  };
65
66
  export declare const createDalvikBytecodeFormat22sParser: () => Parser<DalvikBytecodeFormat22s, Uint8Array>;
66
67
  type DalvikBytecodeFormat22t = {
67
- branchOffset: number;
68
+ branchOffsetCodeUnit: CodeUnit;
68
69
  registers: number[];
69
70
  };
70
71
  export declare const createDalvikBytecodeFormat22tParser: () => Parser<DalvikBytecodeFormat22t, Uint8Array>;
@@ -77,7 +78,7 @@ type DalvikBytecodeFormat23x = {
77
78
  };
78
79
  export declare const dalvikBytecodeFormat23xParser: Parser<DalvikBytecodeFormat23x, Uint8Array>;
79
80
  type DalvikBytecodeFormat30t = {
80
- branchOffset: number;
81
+ branchOffsetCodeUnit: CodeUnit;
81
82
  };
82
83
  export declare const dalvikBytecodeFormat30tParser: Parser<DalvikBytecodeFormat30t, Uint8Array>;
83
84
  type DalvikBytecodeFormat31i = {
@@ -93,7 +94,7 @@ export declare const createDalvikBytecodeFormat31cParser: <Index>({ isoIndex, }:
93
94
  isoIndex: Iso<Index, number>;
94
95
  }) => Parser<DalvikBytecodeFormat31c<Index>, Uint8Array>;
95
96
  type DalvikBytecodeFormat31t = {
96
- branchOffset: number;
97
+ branchOffsetCodeUnit: CodeUnit;
97
98
  registers: number[];
98
99
  };
99
100
  export declare const dalvikBytecodeFormat31tParser: Parser<DalvikBytecodeFormat31t, Uint8Array>;
@@ -1,5 +1,7 @@
1
1
  import { byteParser, intParser, longParser, shortParser, ubyteParser, uintParser, ushortParser, } from '../dalvikExecutableParser/typeParsers.js';
2
+ import { isoCodeUnit } from '../dalvikExecutableParser/typedNumbers.js';
2
3
  import { createElementParser } from '../elementParser.js';
4
+ import { createObjectParser } from '../objectParser.js';
3
5
  import { setParserName } from '../parser.js';
4
6
  import { promiseCompose } from '../promiseCompose.js';
5
7
  import { createTupleParser } from '../tupleParser.js';
@@ -10,7 +12,7 @@ export const nibblesParser = promiseCompose(createElementParser(), byte => [
10
12
  ]);
11
13
  setParserName(nibblesParser, 'nibblesParser');
12
14
  export const dalvikBytecodeFormat10tParser = promiseCompose(byteParser, branchOffset => ({
13
- branchOffset,
15
+ branchOffsetCodeUnit: isoCodeUnit.wrap(branchOffset),
14
16
  }));
15
17
  export const dalvikBytecodeFormat10xParser = promiseCompose(createElementParser(), () => undefined);
16
18
  export const dalvikBytecodeFormat11xParser = promiseCompose(ubyteParser, register0 => ({
@@ -33,8 +35,8 @@ export const dalvikBytecodeFormat12xParser = promiseCompose(nibblesParser, ([reg
33
35
  export const dalvikBytecodeFormat20tParser = promiseCompose(createTupleParser([
34
36
  ubyteParser,
35
37
  shortParser,
36
- ]), ([_zero, branchOffset,]) => ({
37
- branchOffset,
38
+ ]), ([_zero, branchOffset]) => ({
39
+ branchOffsetCodeUnit: isoCodeUnit.wrap(branchOffset),
38
40
  }));
39
41
  export const createDalvikBytecodeFormat21cParser = ({ isoIndex, }) => promiseCompose(createTupleParser([
40
42
  ubyteParser,
@@ -58,7 +60,7 @@ export const createDalvikBytecodeFormat21tParser = () => promiseCompose(createTu
58
60
  ubyteParser,
59
61
  shortParser,
60
62
  ]), ([register0, branchOffset,]) => ({
61
- branchOffset,
63
+ branchOffsetCodeUnit: isoCodeUnit.wrap(branchOffset),
62
64
  registers: [
63
65
  register0,
64
66
  ],
@@ -107,7 +109,7 @@ export const createDalvikBytecodeFormat22tParser = () => promiseCompose(createTu
107
109
  nibblesParser,
108
110
  shortParser,
109
111
  ]), ([[register0, register1,], branchOffset,]) => ({
110
- branchOffset,
112
+ branchOffsetCodeUnit: isoCodeUnit.wrap(branchOffset),
111
113
  registers: [
112
114
  register0,
113
115
  register1,
@@ -134,7 +136,7 @@ export const dalvikBytecodeFormat23xParser = promiseCompose(createTupleParser([
134
136
  ],
135
137
  }));
136
138
  export const dalvikBytecodeFormat30tParser = promiseCompose(intParser, branchOffset => ({
137
- branchOffset,
139
+ branchOffsetCodeUnit: isoCodeUnit.wrap(branchOffset),
138
140
  }));
139
141
  export const dalvikBytecodeFormat31iParser = promiseCompose(createTupleParser([
140
142
  ubyteParser,
@@ -158,7 +160,7 @@ export const dalvikBytecodeFormat31tParser = promiseCompose(createTupleParser([
158
160
  ubyteParser,
159
161
  intParser,
160
162
  ]), ([register0, branchOffset,]) => ({
161
- branchOffset,
163
+ branchOffsetCodeUnit: isoCodeUnit.wrap(branchOffset),
162
164
  registers: [
163
165
  register0,
164
166
  ],
@@ -206,13 +208,10 @@ export const dalvikBytecodeFormat51lParser = promiseCompose(createTupleParser([
206
208
  register0,
207
209
  ],
208
210
  }));
209
- export const dalvikBytecodeFormat20bcParser = promiseCompose(createTupleParser([
210
- ubyteParser,
211
- ushortParser,
212
- ]), ([kind, index,]) => ({
213
- kind,
214
- index,
215
- }));
211
+ export const dalvikBytecodeFormat20bcParser = createObjectParser({
212
+ kind: ubyteParser,
213
+ index: ushortParser,
214
+ });
216
215
  export const dalvikBytecodeFormat22csParser = promiseCompose(createTupleParser([
217
216
  nibblesParser,
218
217
  ushortParser,