@futpib/parser 1.0.3 → 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 (262) 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 +89 -0
  20. package/build/bash.js +1 -0
  21. package/build/bashParser.d.ts +6 -0
  22. package/build/bashParser.js +335 -0
  23. package/build/bashParser.test.d.ts +1 -0
  24. package/build/bashParser.test.js +343 -0
  25. package/build/bashParserEdgeCases.test.d.ts +1 -0
  26. package/build/bashParserEdgeCases.test.js +117 -0
  27. package/build/dalvikBytecodeParser/addressConversion.d.ts +110 -0
  28. package/build/dalvikBytecodeParser/addressConversion.js +334 -0
  29. package/build/dalvikBytecodeParser/formatParsers.d.ts +7 -6
  30. package/build/dalvikBytecodeParser/formatParsers.js +13 -14
  31. package/build/dalvikBytecodeParser.d.ts +60 -31
  32. package/build/dalvikBytecodeParser.js +92 -35
  33. package/build/dalvikBytecodeParser.test-d.d.ts +1 -0
  34. package/build/dalvikBytecodeParser.test-d.js +268 -0
  35. package/build/dalvikBytecodeUnparser/formatUnparsers.d.ts +9 -8
  36. package/build/dalvikBytecodeUnparser/formatUnparsers.js +13 -12
  37. package/build/dalvikBytecodeUnparser.d.ts +2 -2
  38. package/build/dalvikBytecodeUnparser.js +23 -23
  39. package/build/dalvikBytecodeUnparser.test.js +7 -7
  40. package/build/dalvikExecutable.d.ts +3 -3
  41. package/build/dalvikExecutable.test-d.d.ts +1 -0
  42. package/build/dalvikExecutable.test-d.js +59 -0
  43. package/build/dalvikExecutableParser/typedNumbers.d.ts +18 -0
  44. package/build/dalvikExecutableParser/typedNumbers.js +3 -0
  45. package/build/dalvikExecutableParser.d.ts +2 -1
  46. package/build/dalvikExecutableParser.js +96 -77
  47. package/build/dalvikExecutableParser.test.js +24 -3
  48. package/build/dalvikExecutableParserAgainstSmaliParser.test.js +3 -0
  49. package/build/dalvikExecutableUnparser/poolScanners.d.ts +2 -2
  50. package/build/dalvikExecutableUnparser/sectionUnparsers.d.ts +3 -3
  51. package/build/dalvikExecutableUnparser/sectionUnparsers.js +26 -11
  52. package/build/dalvikExecutableUnparser.d.ts +2 -2
  53. package/build/dalvikExecutableUnparser.test.js +2 -1
  54. package/build/disjunctionParser.d.ts +5 -3
  55. package/build/disjunctionParser.js +79 -17
  56. package/build/disjunctionParser.test-d.d.ts +1 -0
  57. package/build/disjunctionParser.test-d.js +72 -0
  58. package/build/elementSwitchParser.d.ts +4 -0
  59. package/build/{exactElementSwitchParser.js → elementSwitchParser.js} +3 -4
  60. package/build/elementSwitchParser.test-d.d.ts +1 -0
  61. package/build/elementSwitchParser.test-d.js +44 -0
  62. package/build/exactSequenceParser.d.ts +4 -2
  63. package/build/exactSequenceParser.test-d.d.ts +1 -0
  64. package/build/exactSequenceParser.test-d.js +36 -0
  65. package/build/fetchCid.js +2 -66
  66. package/build/index.d.ts +25 -2
  67. package/build/index.js +23 -1
  68. package/build/index.test.js +16 -1
  69. package/build/inputReader.d.ts +10 -0
  70. package/build/inputReader.js +36 -0
  71. package/build/java.d.ts +502 -0
  72. package/build/java.js +2 -0
  73. package/build/javaKeyStoreParser.js +14 -17
  74. package/build/javaParser.d.ts +51 -0
  75. package/build/javaParser.js +1538 -0
  76. package/build/javaParser.test.d.ts +1 -0
  77. package/build/javaParser.test.js +1287 -0
  78. package/build/javaScript.d.ts +35 -0
  79. package/build/javaScript.js +1 -0
  80. package/build/javaScriptParser.d.ts +9 -0
  81. package/build/javaScriptParser.js +34 -0
  82. package/build/javaScriptUnparser.d.ts +3 -0
  83. package/build/javaScriptUnparser.js +4 -0
  84. package/build/javaScriptUnparser.test.d.ts +1 -0
  85. package/build/javaScriptUnparser.test.js +24 -0
  86. package/build/javaUnparser.d.ts +2 -0
  87. package/build/javaUnparser.js +519 -0
  88. package/build/javaUnparser.test.d.ts +1 -0
  89. package/build/javaUnparser.test.js +24 -0
  90. package/build/javascript.d.ts +35 -0
  91. package/build/javascript.js +1 -0
  92. package/build/javascriptParser.d.ts +9 -0
  93. package/build/javascriptParser.js +34 -0
  94. package/build/javascriptUnparser.d.ts +3 -0
  95. package/build/javascriptUnparser.js +4 -0
  96. package/build/javascriptUnparser.test.d.ts +1 -0
  97. package/build/javascriptUnparser.test.js +24 -0
  98. package/build/jsonParser.js +2 -12
  99. package/build/lazyMessageError.d.ts +3 -0
  100. package/build/lookaheadParser.js +60 -3
  101. package/build/negativeLookaheadParser.js +70 -11
  102. package/build/nonEmptyArrayParser.js +72 -13
  103. package/build/objectParser.d.ts +12 -0
  104. package/build/objectParser.js +31 -0
  105. package/build/objectParser.test-d.d.ts +1 -0
  106. package/build/objectParser.test-d.js +112 -0
  107. package/build/objectParser.test.d.ts +1 -0
  108. package/build/objectParser.test.js +55 -0
  109. package/build/optionalParser.js +69 -10
  110. package/build/parser.d.ts +4 -0
  111. package/build/parser.js +3 -1
  112. package/build/parser.test.js +114 -1
  113. package/build/parserConsumedSequenceParser.js +66 -7
  114. package/build/parserContext.d.ts +6 -0
  115. package/build/parserContext.js +20 -11
  116. package/build/parserError.d.ts +119 -27
  117. package/build/parserError.js +16 -8
  118. package/build/regexpParser.d.ts +2 -0
  119. package/build/regexpParser.js +101 -0
  120. package/build/regexpParser.test.d.ts +1 -0
  121. package/build/regexpParser.test.js +114 -0
  122. package/build/regularExpression.d.ts +63 -0
  123. package/build/regularExpression.js +1 -0
  124. package/build/regularExpressionParser.d.ts +3 -0
  125. package/build/regularExpressionParser.js +600 -0
  126. package/build/regularExpressionParser.test.d.ts +1 -0
  127. package/build/regularExpressionParser.test.js +89 -0
  128. package/build/separatedArrayParser.js +73 -14
  129. package/build/separatedNonEmptyArrayParser.js +73 -14
  130. package/build/sliceBoundedParser.js +62 -5
  131. package/build/smaliParser.d.ts +7 -7
  132. package/build/smaliParser.js +185 -268
  133. package/build/smaliParser.test.js +58 -0
  134. package/build/stringEscapes.d.ts +5 -0
  135. package/build/stringEscapes.js +244 -0
  136. package/build/symbolicExpression.d.ts +29 -0
  137. package/build/symbolicExpression.js +1 -0
  138. package/build/symbolicExpressionParser.d.ts +4 -0
  139. package/build/symbolicExpressionParser.js +123 -0
  140. package/build/symbolicExpressionParser.test.d.ts +1 -0
  141. package/build/symbolicExpressionParser.test.js +289 -0
  142. package/build/terminatedArrayParser.js +113 -38
  143. package/build/terminatedArrayParser.test.js +4 -2
  144. package/build/tupleParser.d.ts +7 -15
  145. package/build/tupleParser.js +1 -0
  146. package/build/unionParser.d.ts +5 -3
  147. package/build/unionParser.js +7 -2
  148. package/build/unionParser.test-d.d.ts +1 -0
  149. package/build/unionParser.test-d.js +72 -0
  150. package/build/unionParser.test.js +10 -11
  151. package/build/zig.d.ts +280 -0
  152. package/build/zig.js +2 -0
  153. package/build/zigParser.d.ts +3 -0
  154. package/build/zigParser.js +1119 -0
  155. package/build/zigParser.test.d.ts +1 -0
  156. package/build/zigParser.test.js +1590 -0
  157. package/build/zigUnparser.d.ts +2 -0
  158. package/build/zigUnparser.js +460 -0
  159. package/build/zigUnparser.test.d.ts +1 -0
  160. package/build/zigUnparser.test.js +24 -0
  161. package/build/zipParser.js +19 -32
  162. package/build/zipUnparser.js +19 -7
  163. package/build/zipUnparser.test.js +1 -1
  164. package/node_modules-@types/s-expression/index.d.ts +5 -0
  165. package/package.json +25 -6
  166. package/src/androidPackageParser.ts +33 -60
  167. package/src/arbitraryDalvikBytecode.ts +39 -31
  168. package/src/arbitraryDalvikExecutable.ts +65 -20
  169. package/src/arbitraryJava.ts +804 -0
  170. package/src/arbitraryJavaScript.ts +410 -0
  171. package/src/arbitraryZig.ts +380 -0
  172. package/src/arrayParser.ts +1 -3
  173. package/src/backsmali.ts +35 -4
  174. package/src/bash.ts +127 -0
  175. package/src/bashParser.test.ts +590 -0
  176. package/src/bashParser.ts +498 -0
  177. package/src/dalvikBytecodeParser/addressConversion.ts +496 -0
  178. package/src/dalvikBytecodeParser/formatParsers.ts +19 -29
  179. package/src/dalvikBytecodeParser.test-d.ts +310 -0
  180. package/src/dalvikBytecodeParser.ts +194 -69
  181. package/src/dalvikBytecodeUnparser/formatUnparsers.ts +27 -26
  182. package/src/dalvikBytecodeUnparser.test.ts +7 -7
  183. package/src/dalvikBytecodeUnparser.ts +31 -30
  184. package/src/dalvikExecutable.test-d.ts +132 -0
  185. package/src/dalvikExecutable.ts +3 -3
  186. package/src/dalvikExecutableParser/typedNumbers.ts +11 -0
  187. package/src/dalvikExecutableParser.test.ts +37 -3
  188. package/src/dalvikExecutableParser.test.ts.md +163 -2
  189. package/src/dalvikExecutableParser.test.ts.snap +0 -0
  190. package/src/dalvikExecutableParser.ts +121 -139
  191. package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +4 -0
  192. package/src/dalvikExecutableUnparser/poolScanners.ts +6 -6
  193. package/src/dalvikExecutableUnparser/sectionUnparsers.ts +38 -14
  194. package/src/dalvikExecutableUnparser.test.ts +3 -2
  195. package/src/dalvikExecutableUnparser.ts +4 -4
  196. package/src/disjunctionParser.test-d.ts +105 -0
  197. package/src/disjunctionParser.ts +18 -15
  198. package/src/elementSwitchParser.test-d.ts +74 -0
  199. package/src/elementSwitchParser.ts +51 -0
  200. package/src/exactSequenceParser.test-d.ts +43 -0
  201. package/src/exactSequenceParser.ts +13 -8
  202. package/src/fetchCid.ts +2 -76
  203. package/src/index.test.ts +22 -1
  204. package/src/index.ts +119 -2
  205. package/src/inputReader.ts +53 -0
  206. package/src/java.ts +708 -0
  207. package/src/javaKeyStoreParser.ts +18 -32
  208. package/src/javaParser.test.ts +1592 -0
  209. package/src/javaParser.ts +2640 -0
  210. package/src/javaScript.ts +36 -0
  211. package/src/javaScriptParser.ts +57 -0
  212. package/src/javaScriptUnparser.test.ts +37 -0
  213. package/src/javaScriptUnparser.ts +7 -0
  214. package/src/javaUnparser.test.ts +37 -0
  215. package/src/javaUnparser.ts +640 -0
  216. package/src/jsonParser.ts +6 -27
  217. package/src/lookaheadParser.ts +2 -6
  218. package/src/negativeLookaheadParser.ts +1 -3
  219. package/src/nonEmptyArrayParser.ts +1 -3
  220. package/src/objectParser.test-d.ts +152 -0
  221. package/src/objectParser.test.ts +71 -0
  222. package/src/objectParser.ts +69 -0
  223. package/src/optionalParser.ts +1 -3
  224. package/src/parser.test.ts +151 -4
  225. package/src/parser.ts +11 -1
  226. package/src/parserConsumedSequenceParser.ts +2 -4
  227. package/src/parserContext.ts +26 -11
  228. package/src/parserError.ts +17 -3
  229. package/src/regexpParser.test.ts +264 -0
  230. package/src/regexpParser.ts +126 -0
  231. package/src/regularExpression.ts +24 -0
  232. package/src/regularExpressionParser.test.ts +102 -0
  233. package/src/regularExpressionParser.ts +920 -0
  234. package/src/separatedArrayParser.ts +1 -3
  235. package/src/separatedNonEmptyArrayParser.ts +1 -3
  236. package/src/sliceBoundedParser.test.ts +2 -2
  237. package/src/sliceBoundedParser.ts +15 -19
  238. package/src/smaliParser.test.ts +64 -0
  239. package/src/smaliParser.test.ts.md +12 -12
  240. package/src/smaliParser.test.ts.snap +0 -0
  241. package/src/smaliParser.ts +246 -534
  242. package/src/stringEscapes.ts +253 -0
  243. package/src/symbolicExpression.ts +17 -0
  244. package/src/symbolicExpressionParser.test.ts +466 -0
  245. package/src/symbolicExpressionParser.ts +190 -0
  246. package/src/terminatedArrayParser.test.ts +9 -6
  247. package/src/terminatedArrayParser.ts +25 -29
  248. package/src/tupleParser.ts +21 -18
  249. package/src/unionParser.test-d.ts +105 -0
  250. package/src/unionParser.test.ts +18 -17
  251. package/src/unionParser.ts +28 -16
  252. package/src/zig.ts +411 -0
  253. package/src/zigParser.test.ts +1693 -0
  254. package/src/zigParser.ts +1745 -0
  255. package/src/zigUnparser.test.ts +37 -0
  256. package/src/zigUnparser.ts +615 -0
  257. package/src/zipParser.ts +20 -56
  258. package/src/zipUnparser.test.ts +1 -1
  259. package/src/zipUnparser.ts +22 -7
  260. package/tsconfig.json +2 -2
  261. package/build/exactElementSwitchParser.d.ts +0 -3
  262. package/src/exactElementSwitchParser.ts +0 -41
@@ -1,4 +1,5 @@
1
1
  import { type Unparser } from '../unparser.js';
2
+ import { type CodeUnit, isoCodeUnit } from '../dalvikExecutableParser/typedNumbers.js';
2
3
 
3
4
  // Basic type unparsers
4
5
  export const ubyteUnparser: Unparser<number, Uint8Array> = async function * (input) {
@@ -55,13 +56,13 @@ export const nibblesUnparser: Unparser<[ number, number ], Uint8Array> = async f
55
56
  yield * ubyteUnparser(byte, unparserContext);
56
57
  };
57
58
 
58
- // Format 10t: branchOffset (1 byte signed)
59
+ // Format 10t: branchOffsetCodeUnit (1 byte signed)
59
60
  type DalvikBytecodeFormat10t = {
60
- branchOffset: number;
61
+ branchOffsetCodeUnit: CodeUnit;
61
62
  };
62
63
 
63
64
  export const dalvikBytecodeFormat10tUnparser: Unparser<DalvikBytecodeFormat10t, Uint8Array> = async function * (input, unparserContext) {
64
- yield * byteUnparser(input.branchOffset, unparserContext);
65
+ yield * byteUnparser(isoCodeUnit.unwrap(input.branchOffsetCodeUnit), unparserContext);
65
66
  };
66
67
 
67
68
  // Format 10x: no data
@@ -104,14 +105,14 @@ export const dalvikBytecodeFormat12xReversedUnparser: Unparser<DalvikBytecodeFor
104
105
  yield * nibblesUnparser([ input.registers[1], input.registers[0] ], unparserContext);
105
106
  };
106
107
 
107
- // Format 20t: zero byte + branchOffset (2 bytes signed)
108
+ // Format 20t: zero byte + branchOffsetCodeUnit (2 bytes signed)
108
109
  type DalvikBytecodeFormat20t = {
109
- branchOffset: number;
110
+ branchOffsetCodeUnit: CodeUnit;
110
111
  };
111
112
 
112
113
  export const dalvikBytecodeFormat20tUnparser: Unparser<DalvikBytecodeFormat20t, Uint8Array> = async function * (input, unparserContext) {
113
114
  yield * ubyteUnparser(0, unparserContext);
114
- yield * shortUnparser(input.branchOffset, unparserContext);
115
+ yield * shortUnparser(isoCodeUnit.unwrap(input.branchOffsetCodeUnit), unparserContext);
115
116
  };
116
117
 
117
118
  // Format 21c: register + index (2 bytes)
@@ -136,15 +137,15 @@ export const dalvikBytecodeFormat21hUnparser: Unparser<DalvikBytecodeFormat21h,
136
137
  yield * ushortUnparser(input.value, unparserContext);
137
138
  };
138
139
 
139
- // Format 21t: register + branchOffset (2 bytes signed)
140
+ // Format 21t: register + branchOffsetCodeUnit (2 bytes signed)
140
141
  type DalvikBytecodeFormat21t = {
141
- branchOffset: number;
142
+ branchOffsetCodeUnit: CodeUnit;
142
143
  registers: number[];
143
144
  };
144
145
 
145
146
  export const dalvikBytecodeFormat21tUnparser: Unparser<DalvikBytecodeFormat21t, Uint8Array> = async function * (input, unparserContext) {
146
147
  yield * ubyteUnparser(input.registers[0], unparserContext);
147
- yield * shortUnparser(input.branchOffset, unparserContext);
148
+ yield * shortUnparser(isoCodeUnit.unwrap(input.branchOffsetCodeUnit), unparserContext);
148
149
  };
149
150
 
150
151
  // Format 21s: register + value (2 bytes signed)
@@ -192,22 +193,22 @@ export const dalvikBytecodeFormat22sUnparser: Unparser<DalvikBytecodeFormat22s,
192
193
  yield * shortUnparser(input.value, unparserContext);
193
194
  };
194
195
 
195
- // Format 22t: two registers in nibbles + branchOffset (2 bytes signed)
196
+ // Format 22t: two registers in nibbles + branchOffsetCodeUnit (2 bytes signed)
196
197
  type DalvikBytecodeFormat22t = {
197
- branchOffset: number;
198
+ branchOffsetCodeUnit: CodeUnit;
198
199
  registers: number[];
199
200
  };
200
201
 
201
202
  export const dalvikBytecodeFormat22tUnparser: Unparser<DalvikBytecodeFormat22t, Uint8Array> = async function * (input, unparserContext) {
202
203
  yield * nibblesUnparser([ input.registers[1], input.registers[0] ], unparserContext);
203
- yield * shortUnparser(input.branchOffset, unparserContext);
204
+ yield * shortUnparser(isoCodeUnit.unwrap(input.branchOffsetCodeUnit), unparserContext);
204
205
  };
205
206
 
206
207
  // Format 22t for commutative operations (if-eq, if-ne): registers are already in sorted/canonical order
207
208
  // so we don't reverse them
208
209
  export const dalvikBytecodeFormat22tCommutativeUnparser: Unparser<DalvikBytecodeFormat22t, Uint8Array> = async function * (input, unparserContext) {
209
210
  yield * nibblesUnparser([ input.registers[0], input.registers[1] ], unparserContext);
210
- yield * shortUnparser(input.branchOffset, unparserContext);
211
+ yield * shortUnparser(isoCodeUnit.unwrap(input.branchOffsetCodeUnit), unparserContext);
211
212
  };
212
213
 
213
214
  // Format 22x: register + register (2 bytes)
@@ -231,13 +232,13 @@ export const dalvikBytecodeFormat23xUnparser: Unparser<DalvikBytecodeFormat23x,
231
232
  yield * ubyteUnparser(input.registers[2], unparserContext);
232
233
  };
233
234
 
234
- // Format 30t: zero byte + branchOffset (4 bytes signed)
235
+ // Format 30t: zero byte + branchOffsetCodeUnit (4 bytes signed)
235
236
  type DalvikBytecodeFormat30t = {
236
- branchOffset: number;
237
+ branchOffsetCodeUnit: CodeUnit;
237
238
  };
238
239
 
239
240
  export const dalvikBytecodeFormat30tUnparser: Unparser<DalvikBytecodeFormat30t, Uint8Array> = async function * (input, unparserContext) {
240
- yield * intUnparser(input.branchOffset, unparserContext);
241
+ yield * intUnparser(isoCodeUnit.unwrap(input.branchOffsetCodeUnit), unparserContext);
241
242
  };
242
243
 
243
244
  // Format 31i: register + value (4 bytes signed)
@@ -262,15 +263,15 @@ export const dalvikBytecodeFormat31cUnparser: Unparser<DalvikBytecodeFormat31c,
262
263
  yield * uintUnparser(input.index, unparserContext);
263
264
  };
264
265
 
265
- // Format 31t: register + branchOffset (4 bytes signed)
266
+ // Format 31t: register + branchOffsetCodeUnit (4 bytes signed)
266
267
  type DalvikBytecodeFormat31t = {
267
- branchOffset: number;
268
+ branchOffsetCodeUnit: CodeUnit;
268
269
  registers: number[];
269
270
  };
270
271
 
271
272
  export const dalvikBytecodeFormat31tUnparser: Unparser<DalvikBytecodeFormat31t, Uint8Array> = async function * (input, unparserContext) {
272
273
  yield * ubyteUnparser(input.registers[0], unparserContext);
273
- yield * intUnparser(input.branchOffset, unparserContext);
274
+ yield * intUnparser(isoCodeUnit.unwrap(input.branchOffsetCodeUnit), unparserContext);
274
275
  };
275
276
 
276
277
  // Format 32x: two registers (2 bytes each)
@@ -374,19 +375,19 @@ export const dalvikBytecodeFormat4rccUnparser: Unparser<DalvikBytecodeFormat4rcc
374
375
  type DalvikBytecodeOperationPackedSwitchPayload = {
375
376
  operation: 'packed-switch-payload';
376
377
  value: number;
377
- branchOffsets: number[];
378
+ branchOffsetsCodeUnit: CodeUnit[];
378
379
  };
379
380
 
380
381
  export const dalvikBytecodeOperationPackedSwitchPayloadUnparser: Unparser<DalvikBytecodeOperationPackedSwitchPayload, Uint8Array> = async function * (input, unparserContext) {
381
382
  // Ident (0x0100) - little-endian
382
383
  yield * ushortUnparser(0x01_00, unparserContext);
383
384
  // Size
384
- yield * ushortUnparser(input.branchOffsets.length, unparserContext);
385
+ yield * ushortUnparser(input.branchOffsetsCodeUnit.length, unparserContext);
385
386
  // First key value
386
387
  yield * intUnparser(input.value, unparserContext);
387
388
  // Branch offsets
388
- for (const offset of input.branchOffsets) {
389
- yield * intUnparser(offset, unparserContext);
389
+ for (const offset of input.branchOffsetsCodeUnit) {
390
+ yield * intUnparser(isoCodeUnit.unwrap(offset), unparserContext);
390
391
  }
391
392
  };
392
393
 
@@ -394,7 +395,7 @@ export const dalvikBytecodeOperationPackedSwitchPayloadUnparser: Unparser<Dalvik
394
395
  type DalvikBytecodeOperationSparseSwitchPayload = {
395
396
  operation: 'sparse-switch-payload';
396
397
  keys: number[];
397
- branchOffsets: number[];
398
+ branchOffsetsCodeUnit: CodeUnit[];
398
399
  };
399
400
 
400
401
  export const dalvikBytecodeOperationSparseSwitchPayloadUnparser: Unparser<DalvikBytecodeOperationSparseSwitchPayload, Uint8Array> = async function * (input, unparserContext) {
@@ -407,8 +408,8 @@ export const dalvikBytecodeOperationSparseSwitchPayloadUnparser: Unparser<Dalvik
407
408
  yield * intUnparser(key, unparserContext);
408
409
  }
409
410
  // Branch offsets
410
- for (const offset of input.branchOffsets) {
411
- yield * intUnparser(offset, unparserContext);
411
+ for (const offset of input.branchOffsetsCodeUnit) {
412
+ yield * intUnparser(isoCodeUnit.unwrap(offset), unparserContext);
412
413
  }
413
414
  };
414
415
 
@@ -1,22 +1,22 @@
1
1
  import { testProp } from '@fast-check/ava';
2
2
  import { runUnparser } from './unparser.js';
3
- import { dalvikBytecodeUnparser } from './dalvikBytecodeUnparser.js';
3
+ import { rawDalvikBytecodeUnparser } from './dalvikBytecodeUnparser.js';
4
4
  import { uint8ArrayUnparserOutputCompanion } from './unparserOutputCompanion.js';
5
5
  import { runParser } from './parser.js';
6
- import { createDalvikBytecodeParser } from './dalvikBytecodeParser.js';
6
+ import { createRawDalvikBytecodeParser } from './dalvikBytecodeParser.js';
7
7
  import { uint8ArrayParserInputCompanion } from './parserInputCompanion.js';
8
- import { arbitraryDalvikBytecode } from './arbitraryDalvikBytecode.js';
8
+ import { arbitraryRawDalvikBytecode } from './arbitraryDalvikBytecode.js';
9
9
  import { uint8ArrayAsyncIterableToUint8Array } from './uint8Array.js';
10
10
 
11
11
  const seed = process.env.SEED ? Number(process.env.SEED) : undefined;
12
12
 
13
13
  testProp(
14
14
  'dalvik bytecode roundtrip',
15
- [arbitraryDalvikBytecode],
15
+ [arbitraryRawDalvikBytecode],
16
16
  async (t, bytecode) => {
17
17
  // Unparse the bytecode to bytes
18
18
  const unparsedStreamIterable = runUnparser(
19
- dalvikBytecodeUnparser,
19
+ rawDalvikBytecodeUnparser,
20
20
  bytecode,
21
21
  uint8ArrayUnparserOutputCompanion
22
22
  );
@@ -25,11 +25,11 @@ testProp(
25
25
  const unparsedStream = await uint8ArrayAsyncIterableToUint8Array(unparsedStreamIterable);
26
26
 
27
27
  // Create parser with the correct size
28
- const dalvikBytecodeParser = createDalvikBytecodeParser(unparsedStream.length);
28
+ const rawDalvikBytecodeParser = createRawDalvikBytecodeParser(unparsedStream.length);
29
29
 
30
30
  // Re-parse the unparsed bytes
31
31
  const actual = await runParser(
32
- dalvikBytecodeParser,
32
+ rawDalvikBytecodeParser,
33
33
  unparsedStream,
34
34
  uint8ArrayParserInputCompanion
35
35
  );
@@ -1,7 +1,8 @@
1
1
  import { type Unparser, type UnparserResult } from './unparser.js';
2
2
  import { type UnparserContext } from './unparserContext.js';
3
- import { type DalvikBytecode, type DalvikBytecodeOperation } from './dalvikBytecodeParser.js';
3
+ import { type RawDalvikBytecode, type RawDalvikBytecodeOperation } from './dalvikBytecodeParser.js';
4
4
  import {
5
+ type CodeUnit,
5
6
  isoIndexIntoStringIds,
6
7
  isoIndexIntoTypeIds,
7
8
  isoIndexIntoMethodIds,
@@ -312,26 +313,26 @@ const operationToOpcodeMap: Map<string, number> = new Map([
312
313
  // They use multi-byte identifiers: 0x0100, 0x0200, 0x0300
313
314
  ]);
314
315
 
315
- export const dalvikBytecodeUnparser: Unparser<DalvikBytecode, Uint8Array> = async function * (input, unparserContext) {
316
+ export const rawDalvikBytecodeUnparser: Unparser<RawDalvikBytecode, Uint8Array> = async function * (input, unparserContext) {
316
317
  for (const operation of input) {
317
- yield * dalvikBytecodeOperationUnparser(operation, unparserContext);
318
+ yield * rawDalvikBytecodeOperationUnparser(operation, unparserContext);
318
319
  }
319
320
  };
320
321
 
321
322
  // Type guards for payload operations
322
- function isPackedSwitchPayload(op: DalvikBytecodeOperation): op is { operation: 'packed-switch-payload'; value: number; branchOffsets: number[] } {
323
+ function isPackedSwitchPayload(op: RawDalvikBytecodeOperation): op is RawDalvikBytecodeOperation & { operation: 'packed-switch-payload' } {
323
324
  return typeof op === 'object' && op !== null && 'operation' in op && op.operation === 'packed-switch-payload';
324
325
  }
325
326
 
326
- function isSparseSwitchPayload(op: DalvikBytecodeOperation): op is { operation: 'sparse-switch-payload'; keys: number[]; branchOffsets: number[] } {
327
+ function isSparseSwitchPayload(op: RawDalvikBytecodeOperation): op is RawDalvikBytecodeOperation & { operation: 'sparse-switch-payload' } {
327
328
  return typeof op === 'object' && op !== null && 'operation' in op && op.operation === 'sparse-switch-payload';
328
329
  }
329
330
 
330
- function isFillArrayDataPayload(op: DalvikBytecodeOperation): op is { operation: 'fill-array-data-payload'; elementWidth: number; data: number[] } {
331
+ function isFillArrayDataPayload(op: RawDalvikBytecodeOperation): op is RawDalvikBytecodeOperation & { operation: 'fill-array-data-payload' } {
331
332
  return typeof op === 'object' && op !== null && 'operation' in op && op.operation === 'fill-array-data-payload';
332
333
  }
333
334
 
334
- const dalvikBytecodeOperationUnparser: Unparser<DalvikBytecodeOperation, Uint8Array> = async function * (operation, unparserContext) {
335
+ const rawDalvikBytecodeOperationUnparser: Unparser<RawDalvikBytecodeOperation, Uint8Array> = async function * (operation, unparserContext) {
335
336
  if (!operation || typeof operation !== 'object' || !('operation' in operation)) {
336
337
  throw new Error('Invalid operation');
337
338
  }
@@ -374,19 +375,19 @@ const dalvikBytecodeOperationUnparser: Unparser<DalvikBytecodeOperation, Uint8Ar
374
375
  };
375
376
 
376
377
  // Helper to check if operation has specific fields
377
- function hasRegisters(op: DalvikBytecodeOperation): op is DalvikBytecodeOperation & { registers: number[] } {
378
+ function hasRegisters(op: RawDalvikBytecodeOperation): op is RawDalvikBytecodeOperation & { registers: number[] } {
378
379
  return 'registers' in op;
379
380
  }
380
381
 
381
- function hasValue(op: DalvikBytecodeOperation): op is DalvikBytecodeOperation & { value: number | bigint } {
382
+ function hasValue(op: RawDalvikBytecodeOperation): op is RawDalvikBytecodeOperation & { value: number | bigint } {
382
383
  return 'value' in op;
383
384
  }
384
385
 
385
- function hasBranchOffset(op: DalvikBytecodeOperation): op is DalvikBytecodeOperation & { branchOffset: number } {
386
- return 'branchOffset' in op;
386
+ function hasBranchOffsetCodeUnit(op: RawDalvikBytecodeOperation): op is RawDalvikBytecodeOperation & { branchOffsetCodeUnit: CodeUnit } {
387
+ return 'branchOffsetCodeUnit' in op;
387
388
  }
388
389
 
389
- async function * unparseOperationData(operation: DalvikBytecodeOperation, unparserContext: UnparserContext<Uint8Array, number>): UnparserResult<Uint8Array, number> {
390
+ async function * unparseOperationData(operation: RawDalvikBytecodeOperation, unparserContext: UnparserContext<Uint8Array, number>): UnparserResult<Uint8Array, number> {
390
391
  if (!('operation' in operation)) {
391
392
  throw new Error('Invalid operation structure');
392
393
  }
@@ -601,59 +602,59 @@ async function * unparseOperationData(operation: DalvikBytecodeOperation, unpars
601
602
 
602
603
  // Format 10t (goto)
603
604
  if (operationName === 'goto') {
604
- if (!hasBranchOffset(operation)) {
605
- throw new Error(`Operation ${operationName} missing branchOffset field`);
605
+ if (!hasBranchOffsetCodeUnit(operation)) {
606
+ throw new Error(`Operation ${operationName} missing branchOffsetCodeUnit field`);
606
607
  }
607
- yield * dalvikBytecodeFormat10tUnparser({ branchOffset: operation.branchOffset }, unparserContext);
608
+ yield * dalvikBytecodeFormat10tUnparser({ branchOffsetCodeUnit: operation.branchOffsetCodeUnit }, unparserContext);
608
609
  return;
609
610
  }
610
611
 
611
612
  // Format 20t (goto/16)
612
613
  if (operationName === 'goto/16') {
613
- if (!hasBranchOffset(operation)) {
614
- throw new Error(`Operation ${operationName} missing branchOffset field`);
614
+ if (!hasBranchOffsetCodeUnit(operation)) {
615
+ throw new Error(`Operation ${operationName} missing branchOffsetCodeUnit field`);
615
616
  }
616
- yield * dalvikBytecodeFormat20tUnparser({ branchOffset: operation.branchOffset }, unparserContext);
617
+ yield * dalvikBytecodeFormat20tUnparser({ branchOffsetCodeUnit: operation.branchOffsetCodeUnit }, unparserContext);
617
618
  return;
618
619
  }
619
620
 
620
621
  // Format 30t (goto/32)
621
622
  if (operationName === 'goto/32') {
622
- if (!hasBranchOffset(operation)) {
623
- throw new Error(`Operation ${operationName} missing branchOffset field`);
623
+ if (!hasBranchOffsetCodeUnit(operation)) {
624
+ throw new Error(`Operation ${operationName} missing branchOffsetCodeUnit field`);
624
625
  }
625
- yield * dalvikBytecodeFormat30tUnparser({ branchOffset: operation.branchOffset }, unparserContext);
626
+ yield * dalvikBytecodeFormat30tUnparser({ branchOffsetCodeUnit: operation.branchOffsetCodeUnit }, unparserContext);
626
627
  return;
627
628
  }
628
629
 
629
630
  // Format 22t (if-* operations)
630
631
  if (operationName.startsWith('if-') && !operationName.endsWith('z')) {
631
- if (!hasBranchOffset(operation) || !hasRegisters(operation)) {
632
- throw new Error(`Operation ${operationName} missing branchOffset or registers field`);
632
+ if (!hasBranchOffsetCodeUnit(operation) || !hasRegisters(operation)) {
633
+ throw new Error(`Operation ${operationName} missing branchOffsetCodeUnit or registers field`);
633
634
  }
634
635
  // Commutative operations (if-eq, if-ne) have sorted registers, so don't reverse
635
636
  const isCommutative = operationName === 'if-eq' || operationName === 'if-ne';
636
637
  const unparser = isCommutative ? dalvikBytecodeFormat22tCommutativeUnparser : dalvikBytecodeFormat22tUnparser;
637
- yield * unparser({ branchOffset: operation.branchOffset, registers: operation.registers }, unparserContext);
638
+ yield * unparser({ branchOffsetCodeUnit: operation.branchOffsetCodeUnit, registers: operation.registers }, unparserContext);
638
639
  return;
639
640
  }
640
641
 
641
642
  // Format 21t (if-*z operations)
642
643
  if (operationName.startsWith('if-') && operationName.endsWith('z')) {
643
- if (!hasBranchOffset(operation) || !hasRegisters(operation)) {
644
- throw new Error(`Operation ${operationName} missing branchOffset or registers field`);
644
+ if (!hasBranchOffsetCodeUnit(operation) || !hasRegisters(operation)) {
645
+ throw new Error(`Operation ${operationName} missing branchOffsetCodeUnit or registers field`);
645
646
  }
646
- yield * dalvikBytecodeFormat21tUnparser({ branchOffset: operation.branchOffset, registers: operation.registers }, unparserContext);
647
+ yield * dalvikBytecodeFormat21tUnparser({ branchOffsetCodeUnit: operation.branchOffsetCodeUnit, registers: operation.registers }, unparserContext);
647
648
  return;
648
649
  }
649
650
 
650
651
  // Format 31t (packed-switch, sparse-switch, fill-array-data)
651
652
  if (operationName === 'packed-switch' || operationName === 'sparse-switch' ||
652
653
  operationName === 'fill-array-data') {
653
- if (!hasBranchOffset(operation) || !hasRegisters(operation)) {
654
- throw new Error(`Operation ${operationName} missing branchOffset or registers field`);
654
+ if (!hasBranchOffsetCodeUnit(operation) || !hasRegisters(operation)) {
655
+ throw new Error(`Operation ${operationName} missing branchOffsetCodeUnit or registers field`);
655
656
  }
656
- yield * dalvikBytecodeFormat31tUnparser({ branchOffset: operation.branchOffset, registers: operation.registers }, unparserContext);
657
+ yield * dalvikBytecodeFormat31tUnparser({ branchOffsetCodeUnit: operation.branchOffsetCodeUnit, registers: operation.registers }, unparserContext);
657
658
  return;
658
659
  }
659
660
 
@@ -0,0 +1,132 @@
1
+ import { expectType } from 'tsd';
2
+ import { type ParserOutput } from './parser.js';
3
+ import { dalvikExecutableParser } from './dalvikExecutableParser.js';
4
+
5
+ // Test that DalvikExecutable instruction index fields use plain number types
6
+ // This ensures typed numbers (CodeUnit, InstructionIndex) do not leak into the public API
7
+
8
+ // Derive types from the actual parser output
9
+ type ParsedDalvikExecutable = ParserOutput<typeof dalvikExecutableParser>;
10
+ type ParsedClassDefinition = ParsedDalvikExecutable['classDefinitions'][number];
11
+ type ParsedClassData = NonNullable<ParsedClassDefinition['classData']>;
12
+ type ParsedMethod = ParsedClassData['directMethods'][number];
13
+ type ParsedCode = NonNullable<ParsedMethod['code']>;
14
+ type ParsedInstructions = ParsedCode['instructions'];
15
+ type ParsedOperation = ParsedInstructions[number];
16
+
17
+ // Test try/catch block types
18
+ declare const dex: ParsedDalvikExecutable;
19
+ const classDef = dex.classDefinitions[0];
20
+ const classData = classDef.classData!;
21
+ const method = classData.directMethods[0];
22
+ const code = method.code!;
23
+
24
+ // Try block field types
25
+ const tryBlock = code.tries[0];
26
+ expectType<number>(tryBlock.startInstructionIndex);
27
+ expectType<number>(tryBlock.instructionCount);
28
+
29
+ // Handler field types
30
+ const handler = tryBlock.handler;
31
+ expectType<number | undefined>(handler.catchAllInstructionIndex);
32
+
33
+ // Type-address pair field types
34
+ const typeAddressPair = handler.handlers[0];
35
+ expectType<number>(typeAddressPair.handlerInstructionIndex);
36
+
37
+ // Test bytecode operation types - branch offsets should be plain numbers
38
+ type GotoOperation = Extract<ParsedOperation, { operation: 'goto' }>;
39
+ declare const gotoOp: GotoOperation;
40
+ expectType<number>(gotoOp.targetInstructionIndex);
41
+
42
+ type Goto16Operation = Extract<ParsedOperation, { operation: 'goto/16' }>;
43
+ declare const goto16Op: Goto16Operation;
44
+ expectType<number>(goto16Op.targetInstructionIndex);
45
+
46
+ type Goto32Operation = Extract<ParsedOperation, { operation: 'goto/32' }>;
47
+ declare const goto32Op: Goto32Operation;
48
+ expectType<number>(goto32Op.targetInstructionIndex);
49
+
50
+ type PackedSwitchOperation = Extract<ParsedOperation, { operation: 'packed-switch' }>;
51
+ declare const packedSwitchOp: PackedSwitchOperation;
52
+ expectType<number>(packedSwitchOp.targetInstructionIndex);
53
+
54
+ type SparseSwitchOperation = Extract<ParsedOperation, { operation: 'sparse-switch' }>;
55
+ declare const sparseSwitchOp: SparseSwitchOperation;
56
+ expectType<number>(sparseSwitchOp.targetInstructionIndex);
57
+
58
+ type PackedSwitchPayloadOperation = Extract<ParsedOperation, { operation: 'packed-switch-payload' }>;
59
+ declare const packedSwitchPayloadOp: PackedSwitchPayloadOperation;
60
+ expectType<number[]>(packedSwitchPayloadOp.targetInstructionIndices);
61
+
62
+ type SparseSwitchPayloadOperation = Extract<ParsedOperation, { operation: 'sparse-switch-payload' }>;
63
+ declare const sparseSwitchPayloadOp: SparseSwitchPayloadOperation;
64
+ expectType<number[]>(sparseSwitchPayloadOp.targetInstructionIndices);
65
+
66
+ type FillArrayDataOperation = Extract<ParsedOperation, { operation: 'fill-array-data' }>;
67
+ declare const fillArrayDataOp: FillArrayDataOperation;
68
+ expectType<number>(fillArrayDataOp.targetInstructionIndex);
69
+
70
+ type IfEqOperation = Extract<ParsedOperation, { operation: 'if-eq' }>;
71
+ declare const ifEqOp: IfEqOperation;
72
+ expectType<number>(ifEqOp.targetInstructionIndex);
73
+
74
+ type IfEqzOperation = Extract<ParsedOperation, { operation: 'if-eqz' }>;
75
+ declare const ifEqzOp: IfEqzOperation;
76
+ expectType<number>(ifEqzOp.targetInstructionIndex);
77
+
78
+ // Test that index fields are resolved to their actual types (not raw indices)
79
+ // Operations with methodIndex should have method: DalvikExecutableMethod
80
+ type InvokeVirtualOperation = Extract<ParsedOperation, { operation: 'invoke-virtual' }>;
81
+ declare const invokeVirtualOp: InvokeVirtualOperation;
82
+ expectType<{ class: string; prototype: { shorty: string; returnType: string; parameters: string[] }; name: string }>(invokeVirtualOp.method);
83
+
84
+ // Operations with fieldIndex should have field: DalvikExecutableField
85
+ type IgetOperation = Extract<ParsedOperation, { operation: 'iget' }>;
86
+ declare const igetOp: IgetOperation;
87
+ expectType<{ class: string; type: string; name: string }>(igetOp.field);
88
+
89
+ // Operations with typeIndex should have type: string
90
+ type CheckCastOperation = Extract<ParsedOperation, { operation: 'check-cast' }>;
91
+ declare const checkCastOp: CheckCastOperation;
92
+ expectType<string>(checkCastOp.type);
93
+
94
+ // Operations with stringIndex should have string: string
95
+ type ConstStringOperation = Extract<ParsedOperation, { operation: 'const-string' }>;
96
+ declare const constStringOp: ConstStringOperation;
97
+ expectType<string>(constStringOp.string);
98
+
99
+ // Test that raw index fields do NOT exist on resolved types
100
+ // @ts-expect-error - methodIndex should not exist, use method instead
101
+ invokeVirtualOp.methodIndex;
102
+ // @ts-expect-error - fieldIndex should not exist, use field instead
103
+ igetOp.fieldIndex;
104
+ // @ts-expect-error - typeIndex should not exist, use type instead
105
+ checkCastOp.typeIndex;
106
+ // @ts-expect-error - stringIndex should not exist, use string instead
107
+ constStringOp.stringIndex;
108
+
109
+ // Test that values can be constructed with resolved fields
110
+ const _invokeVirtual: InvokeVirtualOperation = {
111
+ operation: 'invoke-virtual',
112
+ registers: [0],
113
+ method: { class: 'Lfoo/Bar;', prototype: { shorty: 'V', returnType: 'V', parameters: [] }, name: 'baz' },
114
+ };
115
+
116
+ const _iget: IgetOperation = {
117
+ operation: 'iget',
118
+ registers: [0, 1],
119
+ field: { class: 'Lfoo/Bar;', type: 'I', name: 'x' },
120
+ };
121
+
122
+ const _checkCast: CheckCastOperation = {
123
+ operation: 'check-cast',
124
+ registers: [0],
125
+ type: 'Lfoo/Bar;',
126
+ };
127
+
128
+ const _constString: ConstStringOperation = {
129
+ operation: 'const-string',
130
+ registers: [0],
131
+ string: 'hello',
132
+ };
@@ -67,19 +67,19 @@ export type DalvikExecutableEncodedValue =
67
67
  | { type: 'boolean'; value: boolean };
68
68
 
69
69
  type DalvikExecutableTry = {
70
- startAddress: number;
70
+ startInstructionIndex: number;
71
71
  instructionCount: number;
72
72
  handler: DalvikExecutableEncodedCatchHandler;
73
73
  };
74
74
 
75
75
  type DalvikExecutableEncodedTypeAddressPair = {
76
76
  type: string;
77
- address: number;
77
+ handlerInstructionIndex: number;
78
78
  };
79
79
 
80
80
  type DalvikExecutableEncodedCatchHandler = {
81
81
  handlers: DalvikExecutableEncodedTypeAddressPair[];
82
- catchAllAddress: undefined | number;
82
+ catchAllInstructionIndex: undefined | number;
83
83
  };
84
84
 
85
85
  export type DalvikExecutableCode<Instructions> = {
@@ -15,6 +15,17 @@ export const isoIndexIntoFieldIds = iso<IndexIntoFieldIds>();
15
15
  export type IndexIntoMethodIds = {} & Newtype<{ readonly IndexIntoMethodIds: unique symbol }, number>;
16
16
  export const isoIndexIntoMethodIds = iso<IndexIntoMethodIds>();
17
17
 
18
+ export type IndexIntoCallSiteIds = {} & Newtype<{ readonly IndexIntoCallSiteIds: unique symbol }, number>;
19
+ export const isoIndexIntoCallSiteIds = iso<IndexIntoCallSiteIds>();
20
+
21
+ // Code unit addresses/offsets (raw DEX format, 16-bit code units)
22
+ export type CodeUnit = {} & Newtype<{ readonly CodeUnit: unique symbol }, number>;
23
+ export const isoCodeUnit = iso<CodeUnit>();
24
+
25
+ // Instruction index addresses/offsets (converted format, instruction-relative)
26
+ export type InstructionIndex = {} & Newtype<{ readonly InstructionIndex: unique symbol }, number>;
27
+ export const isoInstructionIndex = iso<InstructionIndex>();
28
+
18
29
  export type OffsetToStringDataItem = {} & Newtype<{ readonly OffsetToStringDataItem: unique symbol }, number>;
19
30
  export const isoOffsetToStringDataItem = iso<OffsetToStringDataItem>();
20
31
 
@@ -70,8 +70,10 @@ const dexWithParsedInstructionsMacro = test.macro({
70
70
  return;
71
71
  }
72
72
 
73
- t.false(key.endsWith('Offset') && key !== 'branchOffset', 'All offsets should be resolved: ' + path.join('.'));
74
- t.false(key.endsWith('Index'), 'All indexes should be resolved: ' + path.join('.'));
73
+ t.false(key.endsWith('Offset'), 'All offsets should be resolved: ' + path.join('.'));
74
+ // InstructionIndex fields are expected in Tier 3
75
+ const allowedIndexFields = ['targetInstructionIndex', 'targetInstructionIndices', 'startInstructionIndex', 'handlerInstructionIndex', 'catchAllInstructionIndex'];
76
+ t.false(key.endsWith('Index') && !allowedIndexFields.includes(key) && key !== 'targetInstructionIndices', 'All indexes should be resolved: ' + path.join('.'));
75
77
  });
76
78
 
77
79
  if (shouldSnapshot) {
@@ -83,7 +85,39 @@ const dexWithParsedInstructionsMacro = test.macro({
83
85
  },
84
86
  });
85
87
 
86
- test.serial.only(dexWithParsedInstructionsMacro, 'bafkreibb4gsprc3fvmnyqx6obswvm7e7wngnfj64gz65ey72r7xgyzymt4', true);
88
+ test.serial(dexWithParsedInstructionsMacro, 'bafkreibb4gsprc3fvmnyqx6obswvm7e7wngnfj64gz65ey72r7xgyzymt4', true);
87
89
  test.serial.skip(dexWithParsedInstructionsMacro, 'bafybeiebe27ylo53trgitu6fqfbmba43c4ivxj3nt4kumsilkucpbdxtqq', false);
88
90
  test.serial.skip(dexWithParsedInstructionsMacro, 'bafybeibbupm7uzhuq4pa674rb2amxsenbdaoijigmaf4onaodaql4mh7yy', false);
89
91
  test.serial.skip(dexWithParsedInstructionsMacro, 'bafybeicb3qajmwy6li7hche2nkucvytaxcyxhwhphmi73tgydjzmyoqoda', false);
92
+
93
+ const methodMacro = test.macro({
94
+ title: (providedTitle, dexCid: string, className: string, methodName: string) =>
95
+ providedTitle ?? `method ${className}.${methodName} from ${dexCid}`,
96
+ async exec(t, dexCid: string, className: string, methodName: string) {
97
+ const dexStream = await fetchCid(dexCid);
98
+
99
+ const dex = await runParser(dalvikExecutableParser, dexStream, uint8ArrayParserInputCompanion, {
100
+ errorJoinMode: 'all',
101
+ });
102
+
103
+ const classDef = dex.classDefinitions.find(c => c.class === className);
104
+ t.truthy(classDef, `Class ${className} not found`);
105
+
106
+ const allMethods = [
107
+ ...(classDef!.classData?.directMethods ?? []),
108
+ ...(classDef!.classData?.virtualMethods ?? []),
109
+ ];
110
+
111
+ const method = allMethods.find(m => m.method.name === methodName);
112
+ t.truthy(method, `Method ${methodName} not found in ${className}`);
113
+
114
+ t.snapshot(method);
115
+ },
116
+ });
117
+
118
+ test.serial(
119
+ methodMacro,
120
+ 'bafkreifycfnx4xf3nlml4qavlyxr6bes66nxsow3iaqjghewfsozoj2h3q',
121
+ 'Lpl/czak/minimal/MainActivity;',
122
+ 'getPackedSwitchResult',
123
+ );