@futpib/parser 1.0.2 → 1.0.3

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 (284) hide show
  1. package/.github/copilot-instructions.md +149 -0
  2. package/.github/workflows/copilot-setup-steps.yml +18 -0
  3. package/.github/workflows/main.yml +29 -8
  4. package/.yarn/releases/yarn-4.9.4.cjs +942 -0
  5. package/.yarnrc.yml +1 -1
  6. package/build/allSettledStream.js +1 -1
  7. package/build/allSettledStream.test.js +2 -2
  8. package/build/androidPackageParser.d.ts +1 -1
  9. package/build/androidPackageParser.js +5 -3
  10. package/build/androidPackageParser.test.js +7 -7
  11. package/build/androidPackageUnparser.d.ts +2 -2
  12. package/build/androidPackageUnparser.js +18 -14
  13. package/build/androidPackageUnparser.test.js +7 -7
  14. package/build/arbitrarilySlicedAsyncInterator.js +2 -1
  15. package/build/arbitraryDalvikBytecode.d.ts +4 -0
  16. package/build/arbitraryDalvikBytecode.js +640 -0
  17. package/build/arbitraryDalvikExecutable.d.ts +3 -0
  18. package/build/arbitraryDalvikExecutable.js +282 -0
  19. package/build/arbitraryDosDateTime.js +1 -0
  20. package/build/arbitraryZipStream.js +1 -1
  21. package/build/arrayParser.js +2 -2
  22. package/build/arrayUnparser.d.ts +1 -1
  23. package/build/backsmali.d.ts +3 -1
  24. package/build/backsmali.js +31 -3
  25. package/build/customInvariant.d.ts +2 -1
  26. package/build/customInvariant.js +4 -6
  27. package/build/dalvikBytecodeParser/formatParsers.d.ts +76 -2
  28. package/build/dalvikBytecodeParser/formatParsers.js +146 -11
  29. package/build/dalvikBytecodeParser/formatSizes.d.ts +34 -0
  30. package/build/dalvikBytecodeParser/formatSizes.js +34 -0
  31. package/build/dalvikBytecodeParser/operationFormats.d.ts +225 -0
  32. package/build/dalvikBytecodeParser/operationFormats.js +225 -0
  33. package/build/dalvikBytecodeParser.d.ts +1105 -5
  34. package/build/dalvikBytecodeParser.js +658 -205
  35. package/build/dalvikBytecodeUnparser/formatUnparsers.d.ts +152 -0
  36. package/build/dalvikBytecodeUnparser/formatUnparsers.js +225 -0
  37. package/build/dalvikBytecodeUnparser.d.ts +3 -0
  38. package/build/dalvikBytecodeUnparser.js +642 -0
  39. package/build/dalvikBytecodeUnparser.test.d.ts +1 -0
  40. package/build/dalvikBytecodeUnparser.test.js +25 -0
  41. package/build/dalvikExecutable.d.ts +65 -8
  42. package/build/dalvikExecutable.js +36 -0
  43. package/build/dalvikExecutableParser/stringSyntaxParser.d.ts +1 -1
  44. package/build/dalvikExecutableParser/stringSyntaxParser.js +17 -17
  45. package/build/dalvikExecutableParser/typeParsers.d.ts +2 -1
  46. package/build/dalvikExecutableParser/typeParsers.js +16 -11
  47. package/build/dalvikExecutableParser/typedNumbers.d.ts +85 -69
  48. package/build/dalvikExecutableParser/typedNumbers.js +0 -1
  49. package/build/dalvikExecutableParser.d.ts +2 -2
  50. package/build/dalvikExecutableParser.js +655 -337
  51. package/build/dalvikExecutableParser.test.js +24 -22
  52. package/build/dalvikExecutableParserAgainstSmaliParser.test.js +223 -246
  53. package/build/dalvikExecutableUnparser/annotationUnparsers.d.ts +14 -0
  54. package/build/dalvikExecutableUnparser/annotationUnparsers.js +97 -0
  55. package/build/dalvikExecutableUnparser/poolBuilders.d.ts +49 -0
  56. package/build/dalvikExecutableUnparser/poolBuilders.js +140 -0
  57. package/build/dalvikExecutableUnparser/poolScanners.d.ts +4 -0
  58. package/build/dalvikExecutableUnparser/poolScanners.js +220 -0
  59. package/build/dalvikExecutableUnparser/sectionUnparsers.d.ts +25 -0
  60. package/build/dalvikExecutableUnparser/sectionUnparsers.js +581 -0
  61. package/build/dalvikExecutableUnparser/utils.d.ts +10 -0
  62. package/build/dalvikExecutableUnparser/utils.js +108 -0
  63. package/build/dalvikExecutableUnparser.d.ts +4 -0
  64. package/build/dalvikExecutableUnparser.js +406 -0
  65. package/build/dalvikExecutableUnparser.test.d.ts +1 -0
  66. package/build/dalvikExecutableUnparser.test.js +31 -0
  67. package/build/debugLogInputParser.js +1 -1
  68. package/build/disjunctionParser.d.ts +2 -2
  69. package/build/disjunctionParser.js +2 -2
  70. package/build/elementTerminatedArrayParser.d.ts +2 -2
  71. package/build/elementTerminatedArrayParser.js +1 -1
  72. package/build/elementTerminatedArrayParser.test.js +5 -5
  73. package/build/elementTerminatedSequenceArrayParser.d.ts +2 -2
  74. package/build/elementTerminatedSequenceArrayParser.js +1 -1
  75. package/build/elementTerminatedSequenceArrayParser.test.js +2 -2
  76. package/build/elementTerminatedSequenceParser.d.ts +2 -2
  77. package/build/elementTerminatedSequenceParser.js +1 -1
  78. package/build/elementTerminatedSequenceParser.test.js +2 -2
  79. package/build/endOfInputParser.d.ts +1 -1
  80. package/build/exactElementSwitchParser.d.ts +3 -0
  81. package/build/exactElementSwitchParser.js +22 -0
  82. package/build/fetchCid.js +2 -6
  83. package/build/fetchCid.test.d.ts +1 -0
  84. package/build/fetchCid.test.js +16 -0
  85. package/build/fixedLengthSequenceParser.test.js +2 -2
  86. package/build/hasExecutable.js +2 -2
  87. package/build/highResolutionTimer.js +1 -1
  88. package/build/inputReader.d.ts +1 -1
  89. package/build/inputReader.test.js +33 -45
  90. package/build/javaKeyStoreParser.test.js +6 -6
  91. package/build/jsonParser.js +8 -8
  92. package/build/lazyMessageError.d.ts +48 -0
  93. package/build/lazyMessageError.js +53 -0
  94. package/build/lazyMessageError.test.d.ts +1 -0
  95. package/build/lazyMessageError.test.js +15 -0
  96. package/build/leb128Parser.d.ts +1 -1
  97. package/build/leb128Parser.js +10 -10
  98. package/build/leb128Parser.test.js +7 -7
  99. package/build/negativeLookaheadParser.js +2 -2
  100. package/build/negativeLookaheadParser.test.js +4 -4
  101. package/build/noStackCaptureOverheadError.d.ts +4 -0
  102. package/build/noStackCaptureOverheadError.js +9 -0
  103. package/build/noStackCaptureOverheadError.test.d.ts +1 -0
  104. package/build/noStackCaptureOverheadError.test.js +15 -0
  105. package/build/nonEmptyArrayParser.js +2 -2
  106. package/build/nonEmptyArrayParser.test.js +2 -1
  107. package/build/optionalParser.js +2 -2
  108. package/build/parser.d.ts +2 -1
  109. package/build/parser.js +23 -8
  110. package/build/parser.test.js +78 -29
  111. package/build/parserConsumedSequenceParser.d.ts +1 -1
  112. package/build/parserConsumedSequenceParser.js +2 -2
  113. package/build/parserContext.d.ts +8 -6
  114. package/build/parserContext.js +60 -33
  115. package/build/parserContext.test.js +7 -3
  116. package/build/parserError.d.ts +603 -44
  117. package/build/parserError.js +98 -53
  118. package/build/parserImplementationInvariant.d.ts +1 -1
  119. package/build/parserImplementationInvariant.js +2 -2
  120. package/build/parserInputCompanion.js +2 -2
  121. package/build/promiseCompose.js +1 -2
  122. package/build/separatedArrayParser.js +2 -2
  123. package/build/separatedNonEmptyArrayParser.d.ts +2 -0
  124. package/build/separatedNonEmptyArrayParser.js +40 -0
  125. package/build/separatedNonEmptyArrayParser.test.d.ts +1 -0
  126. package/build/separatedNonEmptyArrayParser.test.js +66 -0
  127. package/build/sequenceBuffer.js +1 -1
  128. package/build/sequenceTerminatedSequenceParser.d.ts +2 -2
  129. package/build/sequenceTerminatedSequenceParser.js +3 -3
  130. package/build/sequenceTerminatedSequenceParser.test.js +1 -1
  131. package/build/sequenceUnparser.d.ts +1 -1
  132. package/build/skipToParser.d.ts +1 -1
  133. package/build/skipToParser.js +2 -2
  134. package/build/sliceBoundedParser.test.js +4 -9
  135. package/build/smali.d.ts +1 -1
  136. package/build/smali.js +6 -2
  137. package/build/smaliParser.d.ts +62 -6
  138. package/build/smaliParser.js +1721 -296
  139. package/build/smaliParser.test.js +338 -43
  140. package/build/stringFromAsyncIterable.d.ts +1 -0
  141. package/build/stringFromAsyncIterable.js +7 -0
  142. package/build/terminatedArrayParser.js +4 -4
  143. package/build/terminatedArrayParser.test.js +7 -7
  144. package/build/toAsyncIterator.js +4 -4
  145. package/build/unionParser.d.ts +1 -1
  146. package/build/unionParser.js +2 -2
  147. package/build/unionParser.test.js +3 -3
  148. package/build/unparser.d.ts +3 -3
  149. package/build/unparser.js +6 -4
  150. package/build/unparser.test.js +7 -19
  151. package/build/unparserContext.d.ts +2 -2
  152. package/build/unparserContext.js +2 -3
  153. package/build/unparserError.d.ts +2 -1
  154. package/build/unparserError.js +2 -1
  155. package/build/unparserImplementationInvariant.d.ts +1 -1
  156. package/build/unparserOutputCompanion.d.ts +1 -1
  157. package/build/unparserOutputCompanion.js +1 -1
  158. package/build/zipParser.js +1 -1
  159. package/build/zipUnparser.d.ts +3 -3
  160. package/build/zipUnparser.js +9 -19
  161. package/build/zipUnparser.test.js +1 -1
  162. package/package.json +19 -26
  163. package/src/allSettledStream.test.ts +2 -2
  164. package/src/allSettledStream.ts +3 -3
  165. package/src/androidPackageParser.test.ts +17 -19
  166. package/src/androidPackageParser.ts +129 -171
  167. package/src/androidPackageUnparser.test.ts +19 -21
  168. package/src/androidPackageUnparser.ts +23 -17
  169. package/src/arbitrarilySlicedAsyncInterable.ts +1 -1
  170. package/src/arbitrarilySlicedAsyncInterator.ts +4 -4
  171. package/src/arbitraryDalvikBytecode.ts +992 -0
  172. package/src/arbitraryDalvikExecutable.ts +434 -0
  173. package/src/arbitraryDosDateTime.ts +1 -0
  174. package/src/arbitraryZipStream.ts +1 -1
  175. package/src/arrayParser.ts +2 -2
  176. package/src/arrayUnparser.ts +2 -2
  177. package/src/backsmali.ts +48 -4
  178. package/src/bsonParser.test.ts +12 -14
  179. package/src/customInvariant.ts +8 -12
  180. package/src/dalvikBytecodeParser/formatParsers.ts +376 -17
  181. package/src/dalvikBytecodeParser/formatSizes.ts +35 -0
  182. package/src/dalvikBytecodeParser/operationFormats.ts +226 -0
  183. package/src/dalvikBytecodeParser.ts +1042 -243
  184. package/src/dalvikBytecodeUnparser/formatUnparsers.ts +442 -0
  185. package/src/dalvikBytecodeUnparser.test.ts +44 -0
  186. package/src/dalvikBytecodeUnparser.ts +758 -0
  187. package/src/dalvikExecutable.ts +110 -48
  188. package/src/dalvikExecutableParser/stringSyntaxParser.ts +33 -33
  189. package/src/dalvikExecutableParser/typeParsers.ts +23 -14
  190. package/src/dalvikExecutableParser/typedNumbers.ts +19 -19
  191. package/src/dalvikExecutableParser.test.ts +60 -60
  192. package/src/dalvikExecutableParser.test.ts.md +6 -6
  193. package/src/dalvikExecutableParser.test.ts.snap +0 -0
  194. package/src/dalvikExecutableParser.ts +911 -434
  195. package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +256 -239
  196. package/src/dalvikExecutableUnparser/annotationUnparsers.ts +135 -0
  197. package/src/dalvikExecutableUnparser/poolBuilders.ts +189 -0
  198. package/src/dalvikExecutableUnparser/poolScanners.ts +297 -0
  199. package/src/dalvikExecutableUnparser/sectionUnparsers.ts +683 -0
  200. package/src/dalvikExecutableUnparser/utils.ts +149 -0
  201. package/src/dalvikExecutableUnparser.test.ts +57 -0
  202. package/src/dalvikExecutableUnparser.ts +581 -0
  203. package/src/debugLogInputParser.ts +1 -1
  204. package/src/disjunctionParser.ts +5 -5
  205. package/src/elementTerminatedArrayParser.test.ts +8 -8
  206. package/src/elementTerminatedArrayParser.ts +2 -2
  207. package/src/elementTerminatedSequenceArrayParser.test.ts +4 -6
  208. package/src/elementTerminatedSequenceArrayParser.ts +2 -2
  209. package/src/elementTerminatedSequenceParser.test.ts +4 -6
  210. package/src/elementTerminatedSequenceParser.ts +2 -2
  211. package/src/endOfInputParser.ts +1 -1
  212. package/src/exactElementSwitchParser.ts +41 -0
  213. package/src/fetchCid.test.ts +20 -0
  214. package/src/fetchCid.ts +3 -7
  215. package/src/fixedLengthSequenceParser.test.ts +10 -12
  216. package/src/hasExecutable.ts +2 -2
  217. package/src/highResolutionTimer.ts +1 -1
  218. package/src/inputReader.test.ts +39 -52
  219. package/src/inputReader.ts +2 -4
  220. package/src/inputReaderState.ts +1 -1
  221. package/src/inspect.ts +1 -1
  222. package/src/javaKeyStoreParser.test.ts +12 -14
  223. package/src/javaKeyStoreParser.ts +2 -6
  224. package/src/jsonParser.test.ts +2 -4
  225. package/src/jsonParser.ts +34 -38
  226. package/src/lazyMessageError.test.ts +21 -0
  227. package/src/lazyMessageError.ts +88 -0
  228. package/src/leb128Parser.test.ts +25 -23
  229. package/src/leb128Parser.ts +19 -19
  230. package/src/negativeLookaheadParser.test.ts +7 -11
  231. package/src/negativeLookaheadParser.ts +2 -2
  232. package/src/noStackCaptureOverheadError.test.ts +17 -0
  233. package/src/noStackCaptureOverheadError.ts +12 -0
  234. package/src/nonEmptyArrayParser.test.ts +3 -2
  235. package/src/nonEmptyArrayParser.ts +2 -2
  236. package/src/optionalParser.ts +2 -2
  237. package/src/parser.test.ts +96 -43
  238. package/src/parser.test.ts.md +13 -6
  239. package/src/parser.test.ts.snap +0 -0
  240. package/src/parser.ts +35 -12
  241. package/src/parserAccessorParser.ts +1 -1
  242. package/src/parserConsumedSequenceParser.ts +3 -3
  243. package/src/parserContext.test.ts +7 -3
  244. package/src/parserContext.ts +82 -48
  245. package/src/parserError.ts +143 -63
  246. package/src/parserImplementationInvariant.ts +3 -3
  247. package/src/parserInputCompanion.ts +2 -2
  248. package/src/promiseCompose.ts +2 -2
  249. package/src/separatedArrayParser.ts +3 -3
  250. package/src/separatedNonEmptyArrayParser.test.ts +117 -0
  251. package/src/separatedNonEmptyArrayParser.ts +61 -0
  252. package/src/sequenceBuffer.test.ts +9 -9
  253. package/src/sequenceBuffer.ts +4 -4
  254. package/src/sequenceTerminatedSequenceParser.test.ts +3 -5
  255. package/src/sequenceTerminatedSequenceParser.ts +4 -4
  256. package/src/sequenceUnparser.ts +2 -2
  257. package/src/skipToParser.ts +2 -2
  258. package/src/sliceBoundedParser.test.ts +4 -12
  259. package/src/sliceBoundedParser.ts +2 -2
  260. package/src/smali.ts +8 -3
  261. package/src/smaliParser.test.ts +377 -66
  262. package/src/smaliParser.test.ts.md +1635 -48
  263. package/src/smaliParser.test.ts.snap +0 -0
  264. package/src/smaliParser.ts +2751 -569
  265. package/src/stringFromAsyncIterable.ts +9 -0
  266. package/src/terminatedArrayParser.test.ts +11 -11
  267. package/src/terminatedArrayParser.ts +5 -7
  268. package/src/toAsyncIterator.ts +8 -8
  269. package/src/uint8Array.ts +2 -3
  270. package/src/unionParser.test.ts +22 -23
  271. package/src/unionParser.ts +6 -8
  272. package/src/unparser.test.ts +18 -34
  273. package/src/unparser.ts +13 -9
  274. package/src/unparserContext.ts +9 -13
  275. package/src/unparserError.ts +2 -1
  276. package/src/unparserImplementationInvariant.ts +1 -1
  277. package/src/unparserOutputCompanion.ts +1 -1
  278. package/src/zip.ts +2 -6
  279. package/src/zipParser.ts +10 -18
  280. package/src/zipUnparser.test.ts +1 -1
  281. package/src/zipUnparser.ts +52 -64
  282. package/tsconfig.json +7 -1
  283. package/xo.config.ts +15 -0
  284. package/.yarn/releases/yarn-4.5.3.cjs +0 -934
@@ -0,0 +1,642 @@
1
+ import { isoIndexIntoStringIds, isoIndexIntoTypeIds, isoIndexIntoMethodIds, isoIndexIntoFieldIds, isoIndexIntoPrototypeIds, } from './dalvikExecutableParser/typedNumbers.js';
2
+ import { ubyteUnparser, dalvikBytecodeFormat10tUnparser, dalvikBytecodeFormat10xUnparser, dalvikBytecodeFormat11xUnparser, dalvikBytecodeFormat11nUnparser, dalvikBytecodeFormat12xUnparser, dalvikBytecodeFormat12xReversedUnparser, dalvikBytecodeFormat20tUnparser, dalvikBytecodeFormat21cUnparser, dalvikBytecodeFormat21hUnparser, dalvikBytecodeFormat21tUnparser, dalvikBytecodeFormat21sUnparser, dalvikBytecodeFormat22bUnparser, dalvikBytecodeFormat22cUnparser, dalvikBytecodeFormat22sUnparser, dalvikBytecodeFormat22tUnparser, dalvikBytecodeFormat22tCommutativeUnparser, dalvikBytecodeFormat22xUnparser, dalvikBytecodeFormat23xUnparser, dalvikBytecodeFormat30tUnparser, dalvikBytecodeFormat31iUnparser, dalvikBytecodeFormat31cUnparser, dalvikBytecodeFormat31tUnparser, dalvikBytecodeFormat32xUnparser, dalvikBytecodeFormat35cUnparser, dalvikBytecodeFormat3rcUnparser, dalvikBytecodeFormat51lUnparser, dalvikBytecodeFormat45ccUnparser, dalvikBytecodeFormat4rccUnparser, dalvikBytecodeOperationPackedSwitchPayloadUnparser, dalvikBytecodeOperationSparseSwitchPayloadUnparser, dalvikBytecodeOperationFillArrayDataPayloadUnparser, ushortUnparser, } from './dalvikBytecodeUnparser/formatUnparsers.js';
3
+ // Reverse opcode mapping: operation name → opcode
4
+ const operationToOpcodeMap = new Map([
5
+ // nop is multi-byte (0x0000 or payload instructions)
6
+ ['nop', 0x00],
7
+ // Move operations
8
+ ['move', 0x01],
9
+ ['move/from16', 0x02],
10
+ ['move-wide', 0x04],
11
+ ['move-wide/from16', 0x05],
12
+ ['move-wide/16', 0x06],
13
+ ['move-object', 0x07],
14
+ ['move-object/from16', 0x08],
15
+ ['move-result', 0x0A],
16
+ ['move-result-wide', 0x0B],
17
+ ['move-result-object', 0x0C],
18
+ ['move-exception', 0x0D],
19
+ // Return operations
20
+ ['return-void', 0x0E],
21
+ ['return', 0x0F],
22
+ ['return-wide', 0x10],
23
+ ['return-object', 0x11],
24
+ // Const operations
25
+ ['const/4', 0x12],
26
+ ['const/16', 0x13],
27
+ ['const', 0x14],
28
+ ['const/high16', 0x15],
29
+ ['const-wide/16', 0x16],
30
+ ['const-wide/32', 0x17],
31
+ ['const-wide', 0x18],
32
+ ['const-wide/high16', 0x19],
33
+ ['const-string', 0x1A],
34
+ ['const-string/jumbo', 0x1B],
35
+ ['const-class', 0x1C],
36
+ // Monitor operations
37
+ ['monitor-enter', 0x1D],
38
+ ['monitor-exit', 0x1E],
39
+ // Type operations
40
+ ['check-cast', 0x1F],
41
+ ['instance-of', 0x20],
42
+ // Array operations
43
+ ['array-length', 0x21],
44
+ ['new-instance', 0x22],
45
+ ['new-array', 0x23],
46
+ ['filled-new-array', 0x24],
47
+ ['filled-new-array/range', 0x25],
48
+ ['fill-array-data', 0x26],
49
+ // Throw
50
+ ['throw', 0x27],
51
+ // Goto operations
52
+ ['goto', 0x28],
53
+ ['goto/16', 0x29],
54
+ ['goto/32', 0x2A],
55
+ ['packed-switch', 0x2B],
56
+ ['sparse-switch', 0x2C],
57
+ // Compare operations
58
+ ['cmpl-float', 0x2D],
59
+ ['cmpg-float', 0x2E],
60
+ ['cmpl-double', 0x2F],
61
+ ['cmpg-double', 0x30],
62
+ ['cmp-long', 0x31],
63
+ // If-test operations
64
+ ['if-eq', 0x32],
65
+ ['if-ne', 0x33],
66
+ ['if-lt', 0x34],
67
+ ['if-ge', 0x35],
68
+ ['if-gt', 0x36],
69
+ ['if-le', 0x37],
70
+ // If-test-zero operations
71
+ ['if-eqz', 0x38],
72
+ ['if-nez', 0x39],
73
+ ['if-ltz', 0x3A],
74
+ ['if-gez', 0x3B],
75
+ ['if-gtz', 0x3C],
76
+ ['if-lez', 0x3D],
77
+ // Array element operations
78
+ ['aget', 0x44],
79
+ ['aget-wide', 0x45],
80
+ ['aget-object', 0x46],
81
+ ['aget-boolean', 0x47],
82
+ ['aget-byte', 0x48],
83
+ ['aget-char', 0x49],
84
+ ['aget-short', 0x4A],
85
+ ['aput', 0x4B],
86
+ ['aput-wide', 0x4C],
87
+ ['aput-object', 0x4D],
88
+ ['aput-boolean', 0x4E],
89
+ ['aput-byte', 0x4F],
90
+ ['aput-char', 0x50],
91
+ ['aput-short', 0x51],
92
+ // Instance field operations
93
+ ['iget', 0x52],
94
+ ['iget-wide', 0x53],
95
+ ['iget-object', 0x54],
96
+ ['iget-boolean', 0x55],
97
+ ['iget-byte', 0x56],
98
+ ['iget-char', 0x57],
99
+ ['iget-short', 0x58],
100
+ ['iput', 0x59],
101
+ ['iput-wide', 0x5A],
102
+ ['iput-object', 0x5B],
103
+ ['iput-boolean', 0x5C],
104
+ ['iput-byte', 0x5D],
105
+ ['iput-char', 0x5E],
106
+ ['iput-short', 0x5F],
107
+ // Static field operations
108
+ ['sget', 0x60],
109
+ ['sget-wide', 0x61],
110
+ ['sget-object', 0x62],
111
+ ['sget-boolean', 0x63],
112
+ ['sget-byte', 0x64],
113
+ ['sget-char', 0x65],
114
+ ['sget-short', 0x66],
115
+ ['sput', 0x67],
116
+ ['sput-wide', 0x68],
117
+ ['sput-object', 0x69],
118
+ ['sput-boolean', 0x6A],
119
+ ['sput-byte', 0x6B],
120
+ ['sput-char', 0x6C],
121
+ ['sput-short', 0x6D],
122
+ // Invoke operations
123
+ ['invoke-virtual', 0x6E],
124
+ ['invoke-super', 0x6F],
125
+ ['invoke-direct', 0x70],
126
+ ['invoke-static', 0x71],
127
+ ['invoke-interface', 0x72],
128
+ ['invoke-virtual/range', 0x74],
129
+ ['invoke-super/range', 0x75],
130
+ ['invoke-direct/range', 0x76],
131
+ ['invoke-static/range', 0x77],
132
+ ['invoke-interface/range', 0x78],
133
+ // Unary operations
134
+ ['neg-int', 0x7B],
135
+ ['not-int', 0x7C],
136
+ ['neg-long', 0x7D],
137
+ ['not-long', 0x7E],
138
+ ['neg-float', 0x7F],
139
+ ['neg-double', 0x80],
140
+ ['int-to-long', 0x81],
141
+ ['int-to-float', 0x82],
142
+ ['int-to-double', 0x83],
143
+ ['long-to-int', 0x84],
144
+ ['long-to-float', 0x85],
145
+ ['long-to-double', 0x86],
146
+ ['float-to-int', 0x87],
147
+ ['float-to-long', 0x88],
148
+ ['float-to-double', 0x89],
149
+ ['double-to-int', 0x8A],
150
+ ['double-to-long', 0x8B],
151
+ ['double-to-float', 0x8C],
152
+ ['int-to-byte', 0x8D],
153
+ ['int-to-char', 0x8E],
154
+ ['int-to-short', 0x8F],
155
+ // Binary operations
156
+ ['add-int', 0x90],
157
+ ['sub-int', 0x91],
158
+ ['mul-int', 0x92],
159
+ ['div-int', 0x93],
160
+ ['rem-int', 0x94],
161
+ ['and-int', 0x95],
162
+ ['or-int', 0x96],
163
+ ['xor-int', 0x97],
164
+ ['shl-int', 0x98],
165
+ ['shr-int', 0x99],
166
+ ['ushr-int', 0x9A],
167
+ ['add-long', 0x9B],
168
+ ['sub-long', 0x9C],
169
+ ['mul-long', 0x9D],
170
+ ['div-long', 0x9E],
171
+ ['rem-long', 0x9F],
172
+ ['and-long', 0xA0],
173
+ ['or-long', 0xA1],
174
+ ['xor-long', 0xA2],
175
+ ['shl-long', 0xA3],
176
+ ['shr-long', 0xA4],
177
+ ['ushr-long', 0xA5],
178
+ ['add-float', 0xA6],
179
+ ['sub-float', 0xA7],
180
+ ['mul-float', 0xA8],
181
+ ['div-float', 0xA9],
182
+ ['add-double', 0xAB],
183
+ ['sub-double', 0xAC],
184
+ ['mul-double', 0xAD],
185
+ ['div-double', 0xAE],
186
+ ['rem-double', 0xAF],
187
+ // Binary operations in place (2addr)
188
+ ['add-int/2addr', 0xB0],
189
+ ['sub-int/2addr', 0xB1],
190
+ ['mul-int/2addr', 0xB2],
191
+ ['div-int/2addr', 0xB3],
192
+ ['rem-int/2addr', 0xB4],
193
+ ['and-int/2addr', 0xB5],
194
+ ['or-int/2addr', 0xB6],
195
+ ['xor-int/2addr', 0xB7],
196
+ ['shl-int/2addr', 0xB8],
197
+ ['shr-int/2addr', 0xB9],
198
+ ['ushr-int/2addr', 0xBA],
199
+ ['add-long/2addr', 0xBB],
200
+ ['sub-long/2addr', 0xBC],
201
+ ['mul-long/2addr', 0xBD],
202
+ ['div-long/2addr', 0xBE],
203
+ ['rem-long/2addr', 0xBF],
204
+ ['and-long/2addr', 0xC0],
205
+ ['or-long/2addr', 0xC1],
206
+ ['xor-long/2addr', 0xC2],
207
+ ['shl-long/2addr', 0xC3],
208
+ ['shr-long/2addr', 0xC4],
209
+ ['ushr-long/2addr', 0xC5],
210
+ ['add-float/2addr', 0xC6],
211
+ ['sub-float/2addr', 0xC7],
212
+ ['mul-float/2addr', 0xC8],
213
+ ['div-float/2addr', 0xC9],
214
+ ['rem-float/2addr', 0xCA],
215
+ ['add-double/2addr', 0xCB],
216
+ ['sub-double/2addr', 0xCC],
217
+ ['mul-double/2addr', 0xCD],
218
+ ['div-double/2addr', 0xCE],
219
+ ['rem-double/2addr', 0xCF],
220
+ // Binary operations with literal16
221
+ ['add-int/lit16', 0xD0],
222
+ ['rsub-int', 0xD1],
223
+ ['mul-int/lit16', 0xD2],
224
+ ['div-int/lit16', 0xD3],
225
+ ['rem-int/lit16', 0xD4],
226
+ ['and-int/lit16', 0xD5],
227
+ ['or-int/lit16', 0xD6],
228
+ ['xor-int/lit16', 0xD7],
229
+ // Binary operations with literal8
230
+ ['add-int/lit8', 0xD8],
231
+ ['rsub-int/lit8', 0xD9],
232
+ ['mul-int/lit8', 0xDA],
233
+ ['div-int/lit8', 0xDB],
234
+ ['rem-int/lit8', 0xDC],
235
+ ['and-int/lit8', 0xDD],
236
+ ['or-int/lit8', 0xDE],
237
+ ['xor-int/lit8', 0xDF],
238
+ ['shl-int/lit8', 0xE0],
239
+ ['shr-int/lit8', 0xE1],
240
+ ['ushr-int/lit8', 0xE2],
241
+ // Invoke polymorphic
242
+ ['invoke-polymorphic', 0xFA],
243
+ ['invoke-polymorphic/range', 0xFB],
244
+ ['const-method-handle', 0xFE],
245
+ // Note: Payload instructions don't have single-byte opcodes
246
+ // They use multi-byte identifiers: 0x0100, 0x0200, 0x0300
247
+ ]);
248
+ export const dalvikBytecodeUnparser = async function* (input, unparserContext) {
249
+ for (const operation of input) {
250
+ yield* dalvikBytecodeOperationUnparser(operation, unparserContext);
251
+ }
252
+ };
253
+ // Type guards for payload operations
254
+ function isPackedSwitchPayload(op) {
255
+ return typeof op === 'object' && op !== null && 'operation' in op && op.operation === 'packed-switch-payload';
256
+ }
257
+ function isSparseSwitchPayload(op) {
258
+ return typeof op === 'object' && op !== null && 'operation' in op && op.operation === 'sparse-switch-payload';
259
+ }
260
+ function isFillArrayDataPayload(op) {
261
+ return typeof op === 'object' && op !== null && 'operation' in op && op.operation === 'fill-array-data-payload';
262
+ }
263
+ const dalvikBytecodeOperationUnparser = async function* (operation, unparserContext) {
264
+ if (!operation || typeof operation !== 'object' || !('operation' in operation)) {
265
+ throw new Error('Invalid operation');
266
+ }
267
+ const operationName = operation.operation;
268
+ // Handle payload instructions (multi-byte opcodes)
269
+ if (isPackedSwitchPayload(operation)) {
270
+ yield* dalvikBytecodeOperationPackedSwitchPayloadUnparser(operation, unparserContext);
271
+ return;
272
+ }
273
+ if (isSparseSwitchPayload(operation)) {
274
+ yield* dalvikBytecodeOperationSparseSwitchPayloadUnparser(operation, unparserContext);
275
+ return;
276
+ }
277
+ if (isFillArrayDataPayload(operation)) {
278
+ yield* dalvikBytecodeOperationFillArrayDataPayloadUnparser(operation, unparserContext);
279
+ return;
280
+ }
281
+ // Handle nop (can be 0x00_00 or just 0x00)
282
+ if (operationName === 'nop') {
283
+ yield* ushortUnparser(0x00_00, unparserContext);
284
+ return;
285
+ }
286
+ // Get opcode for regular operations
287
+ const opcode = operationToOpcodeMap.get(operationName);
288
+ if (opcode === undefined) {
289
+ throw new Error(`Unknown operation: ${operationName}`);
290
+ }
291
+ // Write opcode
292
+ yield* ubyteUnparser(opcode, unparserContext);
293
+ // Write operation-specific data based on operation type
294
+ yield* unparseOperationData(operation, unparserContext);
295
+ };
296
+ // Helper to check if operation has specific fields
297
+ function hasRegisters(op) {
298
+ return 'registers' in op;
299
+ }
300
+ function hasValue(op) {
301
+ return 'value' in op;
302
+ }
303
+ function hasBranchOffset(op) {
304
+ return 'branchOffset' in op;
305
+ }
306
+ async function* unparseOperationData(operation, unparserContext) {
307
+ if (!('operation' in operation)) {
308
+ throw new Error('Invalid operation structure');
309
+ }
310
+ const operationName = operation.operation;
311
+ // The operation data varies by instruction format
312
+ // We need to determine which format unparser to call based on the operation type
313
+ // Format 10x operations (no additional data besides zero byte)
314
+ if (operationName === 'nop' || operationName === 'return-void') {
315
+ yield* dalvikBytecodeFormat10xUnparser(undefined, unparserContext);
316
+ return;
317
+ }
318
+ // Format 11x operations (single register)
319
+ if (operationName === 'move-result' || operationName === 'move-result-wide' ||
320
+ operationName === 'move-result-object' || operationName === 'move-exception' ||
321
+ operationName === 'return' || operationName === 'return-wide' ||
322
+ operationName === 'return-object' || operationName === 'monitor-enter' ||
323
+ operationName === 'monitor-exit' || operationName === 'throw') {
324
+ if (!hasRegisters(operation)) {
325
+ throw new Error(`Operation ${operationName} missing registers field`);
326
+ }
327
+ yield* dalvikBytecodeFormat11xUnparser({ registers: operation.registers }, unparserContext);
328
+ return;
329
+ }
330
+ // array-length uses format 12x but reverses registers in parser
331
+ if (operationName === 'array-length') {
332
+ if (!hasRegisters(operation)) {
333
+ throw new Error(`Operation ${operationName} missing registers field`);
334
+ }
335
+ yield* dalvikBytecodeFormat12xReversedUnparser({ registers: operation.registers }, unparserContext);
336
+ return;
337
+ }
338
+ // Format 12x operations (two registers in nibbles)
339
+ if (operationName === 'move' || operationName === 'move-wide' || operationName === 'move-object' ||
340
+ operationName === 'neg-int' || operationName === 'not-int' ||
341
+ operationName === 'neg-long' || operationName === 'not-long' ||
342
+ operationName === 'neg-float' || operationName === 'neg-double' ||
343
+ operationName === 'int-to-long' || operationName === 'int-to-float' || operationName === 'int-to-double' ||
344
+ operationName === 'long-to-int' || operationName === 'long-to-float' || operationName === 'long-to-double' ||
345
+ operationName === 'float-to-int' || operationName === 'float-to-long' || operationName === 'float-to-double' ||
346
+ operationName === 'double-to-int' || operationName === 'double-to-long' || operationName === 'double-to-float' ||
347
+ operationName === 'int-to-byte' || operationName === 'int-to-char' || operationName === 'int-to-short' ||
348
+ operationName === 'add-int/2addr' || operationName === 'sub-int/2addr' || operationName === 'mul-int/2addr' ||
349
+ operationName === 'div-int/2addr' || operationName === 'rem-int/2addr' || operationName === 'and-int/2addr' ||
350
+ operationName === 'or-int/2addr' || operationName === 'xor-int/2addr' || operationName === 'shl-int/2addr' ||
351
+ operationName === 'shr-int/2addr' || operationName === 'ushr-int/2addr' ||
352
+ operationName === 'add-long/2addr' || operationName === 'sub-long/2addr' || operationName === 'mul-long/2addr' ||
353
+ operationName === 'div-long/2addr' || operationName === 'rem-long/2addr' || operationName === 'and-long/2addr' ||
354
+ operationName === 'or-long/2addr' || operationName === 'xor-long/2addr' || operationName === 'shl-long/2addr' ||
355
+ operationName === 'shr-long/2addr' || operationName === 'ushr-long/2addr' ||
356
+ operationName === 'add-float/2addr' || operationName === 'sub-float/2addr' || operationName === 'mul-float/2addr' ||
357
+ operationName === 'div-float/2addr' || operationName === 'rem-float/2addr' ||
358
+ operationName === 'add-double/2addr' || operationName === 'sub-double/2addr' || operationName === 'mul-double/2addr' ||
359
+ operationName === 'div-double/2addr' || operationName === 'rem-double/2addr') {
360
+ if (!hasRegisters(operation)) {
361
+ throw new Error(`Operation ${operationName} missing registers field`);
362
+ }
363
+ yield* dalvikBytecodeFormat12xUnparser({ registers: operation.registers }, unparserContext);
364
+ return;
365
+ }
366
+ // Format 11n (const/4)
367
+ if (operationName === 'const/4') {
368
+ if (!hasValue(operation) || !hasRegisters(operation)) {
369
+ throw new Error(`Operation ${operationName} missing value or registers field`);
370
+ }
371
+ yield* dalvikBytecodeFormat11nUnparser({ value: operation.value, registers: operation.registers }, unparserContext);
372
+ return;
373
+ }
374
+ // Format 21s (const/16, const-wide/16)
375
+ if (operationName === 'const/16') {
376
+ if (!hasValue(operation) || !hasRegisters(operation)) {
377
+ throw new Error(`Operation ${operationName} missing value or registers field`);
378
+ }
379
+ yield* dalvikBytecodeFormat21sUnparser({ registers: operation.registers, value: operation.value }, unparserContext);
380
+ return;
381
+ }
382
+ if (operationName === 'const-wide/16') {
383
+ if (!hasValue(operation) || !hasRegisters(operation)) {
384
+ throw new Error(`Operation ${operationName} missing value or registers field`);
385
+ }
386
+ // Value is bigint, but format 21s expects a signed 16-bit number
387
+ // The parser sign-extends the 16-bit value to 64 bits, so we need to extract the low 16 bits
388
+ const value = Number(operation.value & 0xffffn);
389
+ // Convert unsigned 16-bit to signed 16-bit
390
+ const signedValue = value > 32767 ? value - 65536 : value;
391
+ yield* dalvikBytecodeFormat21sUnparser({ registers: operation.registers, value: signedValue }, unparserContext);
392
+ return;
393
+ }
394
+ // Format 31i (const, const-wide/32)
395
+ if (operationName === 'const' || operationName === 'const-wide/32') {
396
+ if (!hasValue(operation) || !hasRegisters(operation)) {
397
+ throw new Error(`Operation ${operationName} missing value or registers field`);
398
+ }
399
+ // For const-wide/32, value is bigint but we need to convert to signed 32-bit number
400
+ const value = typeof operation.value === 'bigint'
401
+ ? Number(BigInt.asIntN(32, operation.value))
402
+ : operation.value;
403
+ yield* dalvikBytecodeFormat31iUnparser({ value, registers: operation.registers }, unparserContext);
404
+ return;
405
+ }
406
+ // Format 21h (const/high16, const-wide/high16)
407
+ if (operationName === 'const/high16' || operationName === 'const-wide/high16') {
408
+ if (!hasValue(operation) || !hasRegisters(operation)) {
409
+ throw new Error(`Operation ${operationName} missing value or registers field`);
410
+ }
411
+ // The value is stored shifted left (16 bits for const/high16, 48 bits for const-wide/high16)
412
+ // Shift it back for unparsing. Use unsigned shift to handle negative values correctly.
413
+ const value = operationName === 'const-wide/high16'
414
+ ? Number(operation.value >> 48n) & 0xFFFF
415
+ : (operation.value >>> 16) & 0xFFFF;
416
+ yield* dalvikBytecodeFormat21hUnparser({ value, registers: operation.registers }, unparserContext);
417
+ return;
418
+ }
419
+ // Format 51l (const-wide)
420
+ if (operationName === 'const-wide') {
421
+ if (!hasValue(operation) || !hasRegisters(operation)) {
422
+ throw new Error(`Operation ${operationName} missing value or registers field`);
423
+ }
424
+ yield* dalvikBytecodeFormat51lUnparser({ value: operation.value, registers: operation.registers }, unparserContext);
425
+ return;
426
+ }
427
+ // Format 21c (const-string, const-class, check-cast, new-instance)
428
+ if (operationName === 'const-string' || operationName === 'const-class' ||
429
+ operationName === 'check-cast' || operationName === 'new-instance') {
430
+ if (!hasRegisters(operation)) {
431
+ throw new Error(`Operation ${operationName} missing registers field`);
432
+ }
433
+ // Extract and unwrap the index from stringIndex or typeIndex fields
434
+ const index = 'stringIndex' in operation ? isoIndexIntoStringIds.unwrap(operation.stringIndex) :
435
+ 'typeIndex' in operation ? isoIndexIntoTypeIds.unwrap(operation.typeIndex) : 0;
436
+ yield* dalvikBytecodeFormat21cUnparser({ index, registers: operation.registers }, unparserContext);
437
+ return;
438
+ }
439
+ // Format 22c (instance-of, new-array, iget*, iput*)
440
+ if (operationName === 'instance-of' || operationName === 'new-array' ||
441
+ operationName.startsWith('iget') || operationName.startsWith('iput')) {
442
+ if (!hasRegisters(operation)) {
443
+ throw new Error(`Operation ${operationName} missing registers field`);
444
+ }
445
+ // Extract and unwrap the index from typeIndex or fieldIndex fields
446
+ const index = 'typeIndex' in operation ? isoIndexIntoTypeIds.unwrap(operation.typeIndex) :
447
+ 'fieldIndex' in operation ? isoIndexIntoFieldIds.unwrap(operation.fieldIndex) : 0;
448
+ yield* dalvikBytecodeFormat22cUnparser({ registers: operation.registers, index }, unparserContext);
449
+ return;
450
+ }
451
+ // Format 21c (sget*, sput* - static field operations)
452
+ if (operationName.startsWith('sget') || operationName.startsWith('sput')) {
453
+ if (!hasRegisters(operation)) {
454
+ throw new Error(`Operation ${operationName} missing registers field`);
455
+ }
456
+ // Extract and unwrap the index from fieldIndex field
457
+ const index = 'fieldIndex' in operation ? isoIndexIntoFieldIds.unwrap(operation.fieldIndex) : 0;
458
+ yield* dalvikBytecodeFormat21cUnparser({ index, registers: operation.registers }, unparserContext);
459
+ return;
460
+ }
461
+ // Format 23x (aget*, aput*, binary operations)
462
+ if (operationName.startsWith('aget') || operationName.startsWith('aput') ||
463
+ operationName === 'add-int' || operationName === 'sub-int' || operationName === 'mul-int' ||
464
+ operationName === 'div-int' || operationName === 'rem-int' || operationName === 'and-int' ||
465
+ operationName === 'or-int' || operationName === 'xor-int' || operationName === 'shl-int' ||
466
+ operationName === 'shr-int' || operationName === 'ushr-int' ||
467
+ operationName === 'add-long' || operationName === 'sub-long' || operationName === 'mul-long' ||
468
+ operationName === 'div-long' || operationName === 'rem-long' || operationName === 'and-long' ||
469
+ operationName === 'or-long' || operationName === 'xor-long' || operationName === 'shl-long' ||
470
+ operationName === 'shr-long' || operationName === 'ushr-long' ||
471
+ operationName === 'add-float' || operationName === 'sub-float' || operationName === 'mul-float' ||
472
+ operationName === 'div-float' ||
473
+ operationName === 'add-double' || operationName === 'sub-double' || operationName === 'mul-double' ||
474
+ operationName === 'div-double' || operationName === 'rem-double' ||
475
+ operationName === 'cmp-long' || operationName === 'cmpl-float' || operationName === 'cmpg-float' ||
476
+ operationName === 'cmpl-double' || operationName === 'cmpg-double') {
477
+ if (!hasRegisters(operation)) {
478
+ throw new Error(`Operation ${operationName} missing registers field`);
479
+ }
480
+ yield* dalvikBytecodeFormat23xUnparser({ registers: operation.registers }, unparserContext);
481
+ return;
482
+ }
483
+ // Format 22x (move/from16, move-wide/from16, move-object/from16)
484
+ if (operationName === 'move/from16' || operationName === 'move-wide/from16' ||
485
+ operationName === 'move-object/from16') {
486
+ if (!hasRegisters(operation)) {
487
+ throw new Error(`Operation ${operationName} missing registers field`);
488
+ }
489
+ yield* dalvikBytecodeFormat22xUnparser({ registers: operation.registers }, unparserContext);
490
+ return;
491
+ }
492
+ // Format 32x (move-wide/16)
493
+ if (operationName === 'move-wide/16') {
494
+ if (!hasRegisters(operation)) {
495
+ throw new Error(`Operation ${operationName} missing registers field`);
496
+ }
497
+ yield* dalvikBytecodeFormat32xUnparser({ registers: operation.registers }, unparserContext);
498
+ return;
499
+ }
500
+ // Format 10t (goto)
501
+ if (operationName === 'goto') {
502
+ if (!hasBranchOffset(operation)) {
503
+ throw new Error(`Operation ${operationName} missing branchOffset field`);
504
+ }
505
+ yield* dalvikBytecodeFormat10tUnparser({ branchOffset: operation.branchOffset }, unparserContext);
506
+ return;
507
+ }
508
+ // Format 20t (goto/16)
509
+ if (operationName === 'goto/16') {
510
+ if (!hasBranchOffset(operation)) {
511
+ throw new Error(`Operation ${operationName} missing branchOffset field`);
512
+ }
513
+ yield* dalvikBytecodeFormat20tUnparser({ branchOffset: operation.branchOffset }, unparserContext);
514
+ return;
515
+ }
516
+ // Format 30t (goto/32)
517
+ if (operationName === 'goto/32') {
518
+ if (!hasBranchOffset(operation)) {
519
+ throw new Error(`Operation ${operationName} missing branchOffset field`);
520
+ }
521
+ yield* dalvikBytecodeFormat30tUnparser({ branchOffset: operation.branchOffset }, unparserContext);
522
+ return;
523
+ }
524
+ // Format 22t (if-* operations)
525
+ if (operationName.startsWith('if-') && !operationName.endsWith('z')) {
526
+ if (!hasBranchOffset(operation) || !hasRegisters(operation)) {
527
+ throw new Error(`Operation ${operationName} missing branchOffset or registers field`);
528
+ }
529
+ // Commutative operations (if-eq, if-ne) have sorted registers, so don't reverse
530
+ const isCommutative = operationName === 'if-eq' || operationName === 'if-ne';
531
+ const unparser = isCommutative ? dalvikBytecodeFormat22tCommutativeUnparser : dalvikBytecodeFormat22tUnparser;
532
+ yield* unparser({ branchOffset: operation.branchOffset, registers: operation.registers }, unparserContext);
533
+ return;
534
+ }
535
+ // Format 21t (if-*z operations)
536
+ if (operationName.startsWith('if-') && operationName.endsWith('z')) {
537
+ if (!hasBranchOffset(operation) || !hasRegisters(operation)) {
538
+ throw new Error(`Operation ${operationName} missing branchOffset or registers field`);
539
+ }
540
+ yield* dalvikBytecodeFormat21tUnparser({ branchOffset: operation.branchOffset, registers: operation.registers }, unparserContext);
541
+ return;
542
+ }
543
+ // Format 31t (packed-switch, sparse-switch, fill-array-data)
544
+ if (operationName === 'packed-switch' || operationName === 'sparse-switch' ||
545
+ operationName === 'fill-array-data') {
546
+ if (!hasBranchOffset(operation) || !hasRegisters(operation)) {
547
+ throw new Error(`Operation ${operationName} missing branchOffset or registers field`);
548
+ }
549
+ yield* dalvikBytecodeFormat31tUnparser({ branchOffset: operation.branchOffset, registers: operation.registers }, unparserContext);
550
+ return;
551
+ }
552
+ // Format 22b (binary operations with literal8)
553
+ if (operationName.endsWith('/lit8')) {
554
+ if (!hasValue(operation) || !hasRegisters(operation)) {
555
+ throw new Error(`Operation ${operationName} missing value or registers field`);
556
+ }
557
+ yield* dalvikBytecodeFormat22bUnparser({ registers: operation.registers, value: operation.value }, unparserContext);
558
+ return;
559
+ }
560
+ // Format 22s (binary operations with literal16, rsub-int)
561
+ if (operationName.endsWith('/lit16') || operationName === 'rsub-int') {
562
+ if (!hasValue(operation) || !hasRegisters(operation)) {
563
+ throw new Error(`Operation ${operationName} missing value or registers field`);
564
+ }
565
+ yield* dalvikBytecodeFormat22sUnparser({ value: operation.value, registers: operation.registers }, unparserContext);
566
+ return;
567
+ }
568
+ // Format 45cc (invoke-polymorphic)
569
+ // Must be checked before generic invoke-* handler
570
+ if (operationName === 'invoke-polymorphic') {
571
+ if (!hasRegisters(operation)) {
572
+ throw new Error(`Operation ${operationName} missing registers field`);
573
+ }
574
+ const methodIndex = 'methodIndex' in operation ? isoIndexIntoMethodIds.unwrap(operation.methodIndex) : 0;
575
+ const protoIndex = 'protoIndex' in operation ? isoIndexIntoPrototypeIds.unwrap(operation.protoIndex) : 0;
576
+ yield* dalvikBytecodeFormat45ccUnparser({
577
+ methodIndex,
578
+ protoIndex,
579
+ registers: operation.registers
580
+ }, unparserContext);
581
+ return;
582
+ }
583
+ // Format 4rcc (invoke-polymorphic/range)
584
+ // Must be checked before generic /range handler
585
+ if (operationName === 'invoke-polymorphic/range') {
586
+ if (!hasRegisters(operation)) {
587
+ throw new Error(`Operation ${operationName} missing registers field`);
588
+ }
589
+ const methodIndex = 'methodIndex' in operation ? isoIndexIntoMethodIds.unwrap(operation.methodIndex) : 0;
590
+ const protoIndex = 'protoIndex' in operation ? isoIndexIntoPrototypeIds.unwrap(operation.protoIndex) : 0;
591
+ yield* dalvikBytecodeFormat4rccUnparser({
592
+ methodIndex,
593
+ protoIndex,
594
+ registers: operation.registers
595
+ }, unparserContext);
596
+ return;
597
+ }
598
+ // Format 35c (invoke-*, filled-new-array)
599
+ if ((operationName.startsWith('invoke-') && !operationName.endsWith('/range')) ||
600
+ operationName === 'filled-new-array') {
601
+ if (!hasRegisters(operation)) {
602
+ throw new Error(`Operation ${operationName} missing registers field`);
603
+ }
604
+ // Extract and unwrap the index from methodIndex or typeIndex fields
605
+ const index = 'methodIndex' in operation ? isoIndexIntoMethodIds.unwrap(operation.methodIndex) :
606
+ 'typeIndex' in operation ? isoIndexIntoTypeIds.unwrap(operation.typeIndex) : 0;
607
+ yield* dalvikBytecodeFormat35cUnparser({ index, registers: operation.registers }, unparserContext);
608
+ return;
609
+ }
610
+ // Format 3rc (invoke-*/range, filled-new-array/range)
611
+ if (operationName.endsWith('/range')) {
612
+ if (!hasRegisters(operation)) {
613
+ throw new Error(`Operation ${operationName} missing registers field`);
614
+ }
615
+ // Extract and unwrap the index from methodIndex or typeIndex fields
616
+ const index = 'methodIndex' in operation ? isoIndexIntoMethodIds.unwrap(operation.methodIndex) :
617
+ 'typeIndex' in operation ? isoIndexIntoTypeIds.unwrap(operation.typeIndex) : 0;
618
+ yield* dalvikBytecodeFormat3rcUnparser({ index, registers: operation.registers }, unparserContext);
619
+ return;
620
+ }
621
+ // Format 31c (const-string/jumbo)
622
+ if (operationName === 'const-string/jumbo') {
623
+ if (!hasRegisters(operation)) {
624
+ throw new Error(`Operation ${operationName} missing registers field`);
625
+ }
626
+ // Extract and unwrap the index from stringIndex field
627
+ const index = 'stringIndex' in operation ? isoIndexIntoStringIds.unwrap(operation.stringIndex) : 0;
628
+ yield* dalvikBytecodeFormat31cUnparser({ index, registers: operation.registers }, unparserContext);
629
+ return;
630
+ }
631
+ // Format 21c (const-method-handle)
632
+ if (operationName === 'const-method-handle') {
633
+ if (!hasRegisters(operation)) {
634
+ throw new Error(`Operation ${operationName} missing registers field`);
635
+ }
636
+ // Extract and unwrap the index from methodIndex field
637
+ const index = 'methodIndex' in operation ? isoIndexIntoMethodIds.unwrap(operation.methodIndex) : 0;
638
+ yield* dalvikBytecodeFormat21cUnparser({ index, registers: operation.registers }, unparserContext);
639
+ return;
640
+ }
641
+ throw new Error(`Unhandled operation format for: ${operationName}`);
642
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import { testProp } from '@fast-check/ava';
2
+ import { runUnparser } from './unparser.js';
3
+ import { dalvikBytecodeUnparser } from './dalvikBytecodeUnparser.js';
4
+ import { uint8ArrayUnparserOutputCompanion } from './unparserOutputCompanion.js';
5
+ import { runParser } from './parser.js';
6
+ import { createDalvikBytecodeParser } from './dalvikBytecodeParser.js';
7
+ import { uint8ArrayParserInputCompanion } from './parserInputCompanion.js';
8
+ import { arbitraryDalvikBytecode } from './arbitraryDalvikBytecode.js';
9
+ import { uint8ArrayAsyncIterableToUint8Array } from './uint8Array.js';
10
+ const seed = process.env.SEED ? Number(process.env.SEED) : undefined;
11
+ testProp('dalvik bytecode roundtrip', [arbitraryDalvikBytecode], async (t, bytecode) => {
12
+ // Unparse the bytecode to bytes
13
+ const unparsedStreamIterable = runUnparser(dalvikBytecodeUnparser, bytecode, uint8ArrayUnparserOutputCompanion);
14
+ // Collect all chunks into a single Uint8Array
15
+ const unparsedStream = await uint8ArrayAsyncIterableToUint8Array(unparsedStreamIterable);
16
+ // Create parser with the correct size
17
+ const dalvikBytecodeParser = createDalvikBytecodeParser(unparsedStream.length);
18
+ // Re-parse the unparsed bytes
19
+ const actual = await runParser(dalvikBytecodeParser, unparsedStream, uint8ArrayParserInputCompanion);
20
+ // Assert deep equality
21
+ t.deepEqual(actual, bytecode);
22
+ }, {
23
+ verbose: true,
24
+ seed,
25
+ });