@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.
- package/.github/copilot-instructions.md +149 -0
- package/.github/workflows/copilot-setup-steps.yml +18 -0
- package/.github/workflows/main.yml +29 -8
- package/.yarn/releases/yarn-4.9.4.cjs +942 -0
- package/.yarnrc.yml +1 -1
- package/build/allSettledStream.js +1 -1
- package/build/allSettledStream.test.js +2 -2
- package/build/androidPackageParser.d.ts +1 -1
- package/build/androidPackageParser.js +5 -3
- package/build/androidPackageParser.test.js +7 -7
- package/build/androidPackageUnparser.d.ts +2 -2
- package/build/androidPackageUnparser.js +18 -14
- package/build/androidPackageUnparser.test.js +7 -7
- package/build/arbitrarilySlicedAsyncInterator.js +2 -1
- package/build/arbitraryDalvikBytecode.d.ts +4 -0
- package/build/arbitraryDalvikBytecode.js +640 -0
- package/build/arbitraryDalvikExecutable.d.ts +3 -0
- package/build/arbitraryDalvikExecutable.js +282 -0
- package/build/arbitraryDosDateTime.js +1 -0
- package/build/arbitraryZipStream.js +1 -1
- package/build/arrayParser.js +2 -2
- package/build/arrayUnparser.d.ts +1 -1
- package/build/backsmali.d.ts +3 -1
- package/build/backsmali.js +31 -3
- package/build/customInvariant.d.ts +2 -1
- package/build/customInvariant.js +4 -6
- package/build/dalvikBytecodeParser/formatParsers.d.ts +76 -2
- package/build/dalvikBytecodeParser/formatParsers.js +146 -11
- package/build/dalvikBytecodeParser/formatSizes.d.ts +34 -0
- package/build/dalvikBytecodeParser/formatSizes.js +34 -0
- package/build/dalvikBytecodeParser/operationFormats.d.ts +225 -0
- package/build/dalvikBytecodeParser/operationFormats.js +225 -0
- package/build/dalvikBytecodeParser.d.ts +1105 -5
- package/build/dalvikBytecodeParser.js +658 -205
- package/build/dalvikBytecodeUnparser/formatUnparsers.d.ts +152 -0
- package/build/dalvikBytecodeUnparser/formatUnparsers.js +225 -0
- package/build/dalvikBytecodeUnparser.d.ts +3 -0
- package/build/dalvikBytecodeUnparser.js +642 -0
- package/build/dalvikBytecodeUnparser.test.d.ts +1 -0
- package/build/dalvikBytecodeUnparser.test.js +25 -0
- package/build/dalvikExecutable.d.ts +65 -8
- package/build/dalvikExecutable.js +36 -0
- package/build/dalvikExecutableParser/stringSyntaxParser.d.ts +1 -1
- package/build/dalvikExecutableParser/stringSyntaxParser.js +17 -17
- package/build/dalvikExecutableParser/typeParsers.d.ts +2 -1
- package/build/dalvikExecutableParser/typeParsers.js +16 -11
- package/build/dalvikExecutableParser/typedNumbers.d.ts +85 -69
- package/build/dalvikExecutableParser/typedNumbers.js +0 -1
- package/build/dalvikExecutableParser.d.ts +2 -2
- package/build/dalvikExecutableParser.js +655 -337
- package/build/dalvikExecutableParser.test.js +24 -22
- package/build/dalvikExecutableParserAgainstSmaliParser.test.js +223 -246
- package/build/dalvikExecutableUnparser/annotationUnparsers.d.ts +14 -0
- package/build/dalvikExecutableUnparser/annotationUnparsers.js +97 -0
- package/build/dalvikExecutableUnparser/poolBuilders.d.ts +49 -0
- package/build/dalvikExecutableUnparser/poolBuilders.js +140 -0
- package/build/dalvikExecutableUnparser/poolScanners.d.ts +4 -0
- package/build/dalvikExecutableUnparser/poolScanners.js +220 -0
- package/build/dalvikExecutableUnparser/sectionUnparsers.d.ts +25 -0
- package/build/dalvikExecutableUnparser/sectionUnparsers.js +581 -0
- package/build/dalvikExecutableUnparser/utils.d.ts +10 -0
- package/build/dalvikExecutableUnparser/utils.js +108 -0
- package/build/dalvikExecutableUnparser.d.ts +4 -0
- package/build/dalvikExecutableUnparser.js +406 -0
- package/build/dalvikExecutableUnparser.test.d.ts +1 -0
- package/build/dalvikExecutableUnparser.test.js +31 -0
- package/build/debugLogInputParser.js +1 -1
- package/build/disjunctionParser.d.ts +2 -2
- package/build/disjunctionParser.js +2 -2
- package/build/elementTerminatedArrayParser.d.ts +2 -2
- package/build/elementTerminatedArrayParser.js +1 -1
- package/build/elementTerminatedArrayParser.test.js +5 -5
- package/build/elementTerminatedSequenceArrayParser.d.ts +2 -2
- package/build/elementTerminatedSequenceArrayParser.js +1 -1
- package/build/elementTerminatedSequenceArrayParser.test.js +2 -2
- package/build/elementTerminatedSequenceParser.d.ts +2 -2
- package/build/elementTerminatedSequenceParser.js +1 -1
- package/build/elementTerminatedSequenceParser.test.js +2 -2
- package/build/endOfInputParser.d.ts +1 -1
- package/build/exactElementSwitchParser.d.ts +3 -0
- package/build/exactElementSwitchParser.js +22 -0
- package/build/fetchCid.js +2 -6
- package/build/fetchCid.test.d.ts +1 -0
- package/build/fetchCid.test.js +16 -0
- package/build/fixedLengthSequenceParser.test.js +2 -2
- package/build/hasExecutable.js +2 -2
- package/build/highResolutionTimer.js +1 -1
- package/build/inputReader.d.ts +1 -1
- package/build/inputReader.test.js +33 -45
- package/build/javaKeyStoreParser.test.js +6 -6
- package/build/jsonParser.js +8 -8
- package/build/lazyMessageError.d.ts +48 -0
- package/build/lazyMessageError.js +53 -0
- package/build/lazyMessageError.test.d.ts +1 -0
- package/build/lazyMessageError.test.js +15 -0
- package/build/leb128Parser.d.ts +1 -1
- package/build/leb128Parser.js +10 -10
- package/build/leb128Parser.test.js +7 -7
- package/build/negativeLookaheadParser.js +2 -2
- package/build/negativeLookaheadParser.test.js +4 -4
- package/build/noStackCaptureOverheadError.d.ts +4 -0
- package/build/noStackCaptureOverheadError.js +9 -0
- package/build/noStackCaptureOverheadError.test.d.ts +1 -0
- package/build/noStackCaptureOverheadError.test.js +15 -0
- package/build/nonEmptyArrayParser.js +2 -2
- package/build/nonEmptyArrayParser.test.js +2 -1
- package/build/optionalParser.js +2 -2
- package/build/parser.d.ts +2 -1
- package/build/parser.js +23 -8
- package/build/parser.test.js +78 -29
- package/build/parserConsumedSequenceParser.d.ts +1 -1
- package/build/parserConsumedSequenceParser.js +2 -2
- package/build/parserContext.d.ts +8 -6
- package/build/parserContext.js +60 -33
- package/build/parserContext.test.js +7 -3
- package/build/parserError.d.ts +603 -44
- package/build/parserError.js +98 -53
- package/build/parserImplementationInvariant.d.ts +1 -1
- package/build/parserImplementationInvariant.js +2 -2
- package/build/parserInputCompanion.js +2 -2
- package/build/promiseCompose.js +1 -2
- package/build/separatedArrayParser.js +2 -2
- package/build/separatedNonEmptyArrayParser.d.ts +2 -0
- package/build/separatedNonEmptyArrayParser.js +40 -0
- package/build/separatedNonEmptyArrayParser.test.d.ts +1 -0
- package/build/separatedNonEmptyArrayParser.test.js +66 -0
- package/build/sequenceBuffer.js +1 -1
- package/build/sequenceTerminatedSequenceParser.d.ts +2 -2
- package/build/sequenceTerminatedSequenceParser.js +3 -3
- package/build/sequenceTerminatedSequenceParser.test.js +1 -1
- package/build/sequenceUnparser.d.ts +1 -1
- package/build/skipToParser.d.ts +1 -1
- package/build/skipToParser.js +2 -2
- package/build/sliceBoundedParser.test.js +4 -9
- package/build/smali.d.ts +1 -1
- package/build/smali.js +6 -2
- package/build/smaliParser.d.ts +62 -6
- package/build/smaliParser.js +1721 -296
- package/build/smaliParser.test.js +338 -43
- package/build/stringFromAsyncIterable.d.ts +1 -0
- package/build/stringFromAsyncIterable.js +7 -0
- package/build/terminatedArrayParser.js +4 -4
- package/build/terminatedArrayParser.test.js +7 -7
- package/build/toAsyncIterator.js +4 -4
- package/build/unionParser.d.ts +1 -1
- package/build/unionParser.js +2 -2
- package/build/unionParser.test.js +3 -3
- package/build/unparser.d.ts +3 -3
- package/build/unparser.js +6 -4
- package/build/unparser.test.js +7 -19
- package/build/unparserContext.d.ts +2 -2
- package/build/unparserContext.js +2 -3
- package/build/unparserError.d.ts +2 -1
- package/build/unparserError.js +2 -1
- package/build/unparserImplementationInvariant.d.ts +1 -1
- package/build/unparserOutputCompanion.d.ts +1 -1
- package/build/unparserOutputCompanion.js +1 -1
- package/build/zipParser.js +1 -1
- package/build/zipUnparser.d.ts +3 -3
- package/build/zipUnparser.js +9 -19
- package/build/zipUnparser.test.js +1 -1
- package/package.json +19 -26
- package/src/allSettledStream.test.ts +2 -2
- package/src/allSettledStream.ts +3 -3
- package/src/androidPackageParser.test.ts +17 -19
- package/src/androidPackageParser.ts +129 -171
- package/src/androidPackageUnparser.test.ts +19 -21
- package/src/androidPackageUnparser.ts +23 -17
- package/src/arbitrarilySlicedAsyncInterable.ts +1 -1
- package/src/arbitrarilySlicedAsyncInterator.ts +4 -4
- package/src/arbitraryDalvikBytecode.ts +992 -0
- package/src/arbitraryDalvikExecutable.ts +434 -0
- package/src/arbitraryDosDateTime.ts +1 -0
- package/src/arbitraryZipStream.ts +1 -1
- package/src/arrayParser.ts +2 -2
- package/src/arrayUnparser.ts +2 -2
- package/src/backsmali.ts +48 -4
- package/src/bsonParser.test.ts +12 -14
- package/src/customInvariant.ts +8 -12
- package/src/dalvikBytecodeParser/formatParsers.ts +376 -17
- package/src/dalvikBytecodeParser/formatSizes.ts +35 -0
- package/src/dalvikBytecodeParser/operationFormats.ts +226 -0
- package/src/dalvikBytecodeParser.ts +1042 -243
- package/src/dalvikBytecodeUnparser/formatUnparsers.ts +442 -0
- package/src/dalvikBytecodeUnparser.test.ts +44 -0
- package/src/dalvikBytecodeUnparser.ts +758 -0
- package/src/dalvikExecutable.ts +110 -48
- package/src/dalvikExecutableParser/stringSyntaxParser.ts +33 -33
- package/src/dalvikExecutableParser/typeParsers.ts +23 -14
- package/src/dalvikExecutableParser/typedNumbers.ts +19 -19
- package/src/dalvikExecutableParser.test.ts +60 -60
- package/src/dalvikExecutableParser.test.ts.md +6 -6
- package/src/dalvikExecutableParser.test.ts.snap +0 -0
- package/src/dalvikExecutableParser.ts +911 -434
- package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +256 -239
- package/src/dalvikExecutableUnparser/annotationUnparsers.ts +135 -0
- package/src/dalvikExecutableUnparser/poolBuilders.ts +189 -0
- package/src/dalvikExecutableUnparser/poolScanners.ts +297 -0
- package/src/dalvikExecutableUnparser/sectionUnparsers.ts +683 -0
- package/src/dalvikExecutableUnparser/utils.ts +149 -0
- package/src/dalvikExecutableUnparser.test.ts +57 -0
- package/src/dalvikExecutableUnparser.ts +581 -0
- package/src/debugLogInputParser.ts +1 -1
- package/src/disjunctionParser.ts +5 -5
- package/src/elementTerminatedArrayParser.test.ts +8 -8
- package/src/elementTerminatedArrayParser.ts +2 -2
- package/src/elementTerminatedSequenceArrayParser.test.ts +4 -6
- package/src/elementTerminatedSequenceArrayParser.ts +2 -2
- package/src/elementTerminatedSequenceParser.test.ts +4 -6
- package/src/elementTerminatedSequenceParser.ts +2 -2
- package/src/endOfInputParser.ts +1 -1
- package/src/exactElementSwitchParser.ts +41 -0
- package/src/fetchCid.test.ts +20 -0
- package/src/fetchCid.ts +3 -7
- package/src/fixedLengthSequenceParser.test.ts +10 -12
- package/src/hasExecutable.ts +2 -2
- package/src/highResolutionTimer.ts +1 -1
- package/src/inputReader.test.ts +39 -52
- package/src/inputReader.ts +2 -4
- package/src/inputReaderState.ts +1 -1
- package/src/inspect.ts +1 -1
- package/src/javaKeyStoreParser.test.ts +12 -14
- package/src/javaKeyStoreParser.ts +2 -6
- package/src/jsonParser.test.ts +2 -4
- package/src/jsonParser.ts +34 -38
- package/src/lazyMessageError.test.ts +21 -0
- package/src/lazyMessageError.ts +88 -0
- package/src/leb128Parser.test.ts +25 -23
- package/src/leb128Parser.ts +19 -19
- package/src/negativeLookaheadParser.test.ts +7 -11
- package/src/negativeLookaheadParser.ts +2 -2
- package/src/noStackCaptureOverheadError.test.ts +17 -0
- package/src/noStackCaptureOverheadError.ts +12 -0
- package/src/nonEmptyArrayParser.test.ts +3 -2
- package/src/nonEmptyArrayParser.ts +2 -2
- package/src/optionalParser.ts +2 -2
- package/src/parser.test.ts +96 -43
- package/src/parser.test.ts.md +13 -6
- package/src/parser.test.ts.snap +0 -0
- package/src/parser.ts +35 -12
- package/src/parserAccessorParser.ts +1 -1
- package/src/parserConsumedSequenceParser.ts +3 -3
- package/src/parserContext.test.ts +7 -3
- package/src/parserContext.ts +82 -48
- package/src/parserError.ts +143 -63
- package/src/parserImplementationInvariant.ts +3 -3
- package/src/parserInputCompanion.ts +2 -2
- package/src/promiseCompose.ts +2 -2
- package/src/separatedArrayParser.ts +3 -3
- package/src/separatedNonEmptyArrayParser.test.ts +117 -0
- package/src/separatedNonEmptyArrayParser.ts +61 -0
- package/src/sequenceBuffer.test.ts +9 -9
- package/src/sequenceBuffer.ts +4 -4
- package/src/sequenceTerminatedSequenceParser.test.ts +3 -5
- package/src/sequenceTerminatedSequenceParser.ts +4 -4
- package/src/sequenceUnparser.ts +2 -2
- package/src/skipToParser.ts +2 -2
- package/src/sliceBoundedParser.test.ts +4 -12
- package/src/sliceBoundedParser.ts +2 -2
- package/src/smali.ts +8 -3
- package/src/smaliParser.test.ts +377 -66
- package/src/smaliParser.test.ts.md +1635 -48
- package/src/smaliParser.test.ts.snap +0 -0
- package/src/smaliParser.ts +2751 -569
- package/src/stringFromAsyncIterable.ts +9 -0
- package/src/terminatedArrayParser.test.ts +11 -11
- package/src/terminatedArrayParser.ts +5 -7
- package/src/toAsyncIterator.ts +8 -8
- package/src/uint8Array.ts +2 -3
- package/src/unionParser.test.ts +22 -23
- package/src/unionParser.ts +6 -8
- package/src/unparser.test.ts +18 -34
- package/src/unparser.ts +13 -9
- package/src/unparserContext.ts +9 -13
- package/src/unparserError.ts +2 -1
- package/src/unparserImplementationInvariant.ts +1 -1
- package/src/unparserOutputCompanion.ts +1 -1
- package/src/zip.ts +2 -6
- package/src/zipParser.ts +10 -18
- package/src/zipUnparser.test.ts +1 -1
- package/src/zipUnparser.ts +52 -64
- package/tsconfig.json +7 -1
- package/xo.config.ts +15 -0
- 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
|
+
});
|