@futpib/parser 1.0.3 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +24 -0
- package/.github/workflows/main.yml +1 -0
- package/build/androidPackageParser.js +30 -32
- package/build/arbitraryDalvikBytecode.d.ts +3 -3
- package/build/arbitraryDalvikBytecode.js +33 -27
- package/build/arbitraryDalvikExecutable.js +55 -17
- package/build/arbitraryJava.d.ts +31 -0
- package/build/arbitraryJava.js +532 -0
- package/build/arbitraryJavaScript.d.ts +3 -0
- package/build/arbitraryJavaScript.js +263 -0
- package/build/arbitraryJavascript.d.ts +3 -0
- package/build/arbitraryJavascript.js +263 -0
- package/build/arbitraryZig.d.ts +3 -0
- package/build/arbitraryZig.js +240 -0
- package/build/arbitraryZipStream.d.ts +1 -1
- package/build/arrayParser.js +72 -13
- package/build/backsmali.d.ts +4 -3
- package/build/backsmali.js +26 -6
- package/build/bash.d.ts +89 -0
- package/build/bash.js +1 -0
- package/build/bashParser.d.ts +6 -0
- package/build/bashParser.js +335 -0
- package/build/bashParser.test.d.ts +1 -0
- package/build/bashParser.test.js +343 -0
- package/build/bashParserEdgeCases.test.d.ts +1 -0
- package/build/bashParserEdgeCases.test.js +117 -0
- package/build/dalvikBytecodeParser/addressConversion.d.ts +110 -0
- package/build/dalvikBytecodeParser/addressConversion.js +334 -0
- package/build/dalvikBytecodeParser/formatParsers.d.ts +7 -6
- package/build/dalvikBytecodeParser/formatParsers.js +13 -14
- package/build/dalvikBytecodeParser.d.ts +60 -31
- package/build/dalvikBytecodeParser.js +92 -35
- package/build/dalvikBytecodeParser.test-d.d.ts +1 -0
- package/build/dalvikBytecodeParser.test-d.js +268 -0
- package/build/dalvikBytecodeUnparser/formatUnparsers.d.ts +9 -8
- package/build/dalvikBytecodeUnparser/formatUnparsers.js +13 -12
- package/build/dalvikBytecodeUnparser.d.ts +2 -2
- package/build/dalvikBytecodeUnparser.js +23 -23
- package/build/dalvikBytecodeUnparser.test.js +7 -7
- package/build/dalvikExecutable.d.ts +3 -3
- package/build/dalvikExecutable.test-d.d.ts +1 -0
- package/build/dalvikExecutable.test-d.js +59 -0
- package/build/dalvikExecutableParser/typedNumbers.d.ts +18 -0
- package/build/dalvikExecutableParser/typedNumbers.js +3 -0
- package/build/dalvikExecutableParser.d.ts +2 -1
- package/build/dalvikExecutableParser.js +96 -77
- package/build/dalvikExecutableParser.test.js +24 -3
- package/build/dalvikExecutableParserAgainstSmaliParser.test.js +3 -0
- package/build/dalvikExecutableUnparser/poolScanners.d.ts +2 -2
- package/build/dalvikExecutableUnparser/sectionUnparsers.d.ts +3 -3
- package/build/dalvikExecutableUnparser/sectionUnparsers.js +26 -11
- package/build/dalvikExecutableUnparser.d.ts +2 -2
- package/build/dalvikExecutableUnparser.test.js +2 -1
- package/build/disjunctionParser.d.ts +5 -3
- package/build/disjunctionParser.js +79 -17
- package/build/disjunctionParser.test-d.d.ts +1 -0
- package/build/disjunctionParser.test-d.js +72 -0
- package/build/elementSwitchParser.d.ts +4 -0
- package/build/{exactElementSwitchParser.js → elementSwitchParser.js} +3 -4
- package/build/elementSwitchParser.test-d.d.ts +1 -0
- package/build/elementSwitchParser.test-d.js +44 -0
- package/build/exactSequenceParser.d.ts +4 -2
- package/build/exactSequenceParser.test-d.d.ts +1 -0
- package/build/exactSequenceParser.test-d.js +36 -0
- package/build/fetchCid.js +2 -66
- package/build/index.d.ts +25 -2
- package/build/index.js +23 -1
- package/build/index.test.js +16 -1
- package/build/inputReader.d.ts +10 -0
- package/build/inputReader.js +36 -0
- package/build/java.d.ts +502 -0
- package/build/java.js +2 -0
- package/build/javaKeyStoreParser.js +14 -17
- package/build/javaParser.d.ts +51 -0
- package/build/javaParser.js +1538 -0
- package/build/javaParser.test.d.ts +1 -0
- package/build/javaParser.test.js +1287 -0
- package/build/javaScript.d.ts +35 -0
- package/build/javaScript.js +1 -0
- package/build/javaScriptParser.d.ts +9 -0
- package/build/javaScriptParser.js +34 -0
- package/build/javaScriptUnparser.d.ts +3 -0
- package/build/javaScriptUnparser.js +4 -0
- package/build/javaScriptUnparser.test.d.ts +1 -0
- package/build/javaScriptUnparser.test.js +24 -0
- package/build/javaUnparser.d.ts +2 -0
- package/build/javaUnparser.js +519 -0
- package/build/javaUnparser.test.d.ts +1 -0
- package/build/javaUnparser.test.js +24 -0
- package/build/javascript.d.ts +35 -0
- package/build/javascript.js +1 -0
- package/build/javascriptParser.d.ts +9 -0
- package/build/javascriptParser.js +34 -0
- package/build/javascriptUnparser.d.ts +3 -0
- package/build/javascriptUnparser.js +4 -0
- package/build/javascriptUnparser.test.d.ts +1 -0
- package/build/javascriptUnparser.test.js +24 -0
- package/build/jsonParser.js +2 -12
- package/build/lazyMessageError.d.ts +3 -0
- package/build/lookaheadParser.js +60 -3
- package/build/negativeLookaheadParser.js +70 -11
- package/build/nonEmptyArrayParser.js +72 -13
- package/build/objectParser.d.ts +12 -0
- package/build/objectParser.js +31 -0
- package/build/objectParser.test-d.d.ts +1 -0
- package/build/objectParser.test-d.js +112 -0
- package/build/objectParser.test.d.ts +1 -0
- package/build/objectParser.test.js +55 -0
- package/build/optionalParser.js +69 -10
- package/build/parser.d.ts +4 -0
- package/build/parser.js +3 -1
- package/build/parser.test.js +114 -1
- package/build/parserConsumedSequenceParser.js +66 -7
- package/build/parserContext.d.ts +6 -0
- package/build/parserContext.js +20 -11
- package/build/parserError.d.ts +119 -27
- package/build/parserError.js +16 -8
- package/build/regexpParser.d.ts +2 -0
- package/build/regexpParser.js +101 -0
- package/build/regexpParser.test.d.ts +1 -0
- package/build/regexpParser.test.js +114 -0
- package/build/regularExpression.d.ts +63 -0
- package/build/regularExpression.js +1 -0
- package/build/regularExpressionParser.d.ts +3 -0
- package/build/regularExpressionParser.js +600 -0
- package/build/regularExpressionParser.test.d.ts +1 -0
- package/build/regularExpressionParser.test.js +89 -0
- package/build/separatedArrayParser.js +73 -14
- package/build/separatedNonEmptyArrayParser.js +73 -14
- package/build/sliceBoundedParser.js +62 -5
- package/build/smaliParser.d.ts +7 -7
- package/build/smaliParser.js +185 -268
- package/build/smaliParser.test.js +58 -0
- package/build/stringEscapes.d.ts +5 -0
- package/build/stringEscapes.js +244 -0
- package/build/symbolicExpression.d.ts +29 -0
- package/build/symbolicExpression.js +1 -0
- package/build/symbolicExpressionParser.d.ts +4 -0
- package/build/symbolicExpressionParser.js +123 -0
- package/build/symbolicExpressionParser.test.d.ts +1 -0
- package/build/symbolicExpressionParser.test.js +289 -0
- package/build/terminatedArrayParser.js +113 -38
- package/build/terminatedArrayParser.test.js +4 -2
- package/build/tupleParser.d.ts +7 -15
- package/build/tupleParser.js +1 -0
- package/build/unionParser.d.ts +5 -3
- package/build/unionParser.js +7 -2
- package/build/unionParser.test-d.d.ts +1 -0
- package/build/unionParser.test-d.js +72 -0
- package/build/unionParser.test.js +10 -11
- package/build/zig.d.ts +280 -0
- package/build/zig.js +2 -0
- package/build/zigParser.d.ts +3 -0
- package/build/zigParser.js +1119 -0
- package/build/zigParser.test.d.ts +1 -0
- package/build/zigParser.test.js +1590 -0
- package/build/zigUnparser.d.ts +2 -0
- package/build/zigUnparser.js +460 -0
- package/build/zigUnparser.test.d.ts +1 -0
- package/build/zigUnparser.test.js +24 -0
- package/build/zipParser.js +19 -32
- package/build/zipUnparser.js +19 -7
- package/build/zipUnparser.test.js +1 -1
- package/node_modules-@types/s-expression/index.d.ts +5 -0
- package/package.json +25 -6
- package/src/androidPackageParser.ts +33 -60
- package/src/arbitraryDalvikBytecode.ts +39 -31
- package/src/arbitraryDalvikExecutable.ts +65 -20
- package/src/arbitraryJava.ts +804 -0
- package/src/arbitraryJavaScript.ts +410 -0
- package/src/arbitraryZig.ts +380 -0
- package/src/arrayParser.ts +1 -3
- package/src/backsmali.ts +35 -4
- package/src/bash.ts +127 -0
- package/src/bashParser.test.ts +590 -0
- package/src/bashParser.ts +498 -0
- package/src/dalvikBytecodeParser/addressConversion.ts +496 -0
- package/src/dalvikBytecodeParser/formatParsers.ts +19 -29
- package/src/dalvikBytecodeParser.test-d.ts +310 -0
- package/src/dalvikBytecodeParser.ts +194 -69
- package/src/dalvikBytecodeUnparser/formatUnparsers.ts +27 -26
- package/src/dalvikBytecodeUnparser.test.ts +7 -7
- package/src/dalvikBytecodeUnparser.ts +31 -30
- package/src/dalvikExecutable.test-d.ts +132 -0
- package/src/dalvikExecutable.ts +3 -3
- package/src/dalvikExecutableParser/typedNumbers.ts +11 -0
- package/src/dalvikExecutableParser.test.ts +37 -3
- package/src/dalvikExecutableParser.test.ts.md +163 -2
- package/src/dalvikExecutableParser.test.ts.snap +0 -0
- package/src/dalvikExecutableParser.ts +121 -139
- package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +4 -0
- package/src/dalvikExecutableUnparser/poolScanners.ts +6 -6
- package/src/dalvikExecutableUnparser/sectionUnparsers.ts +38 -14
- package/src/dalvikExecutableUnparser.test.ts +3 -2
- package/src/dalvikExecutableUnparser.ts +4 -4
- package/src/disjunctionParser.test-d.ts +105 -0
- package/src/disjunctionParser.ts +18 -15
- package/src/elementSwitchParser.test-d.ts +74 -0
- package/src/elementSwitchParser.ts +51 -0
- package/src/exactSequenceParser.test-d.ts +43 -0
- package/src/exactSequenceParser.ts +13 -8
- package/src/fetchCid.ts +2 -76
- package/src/index.test.ts +22 -1
- package/src/index.ts +119 -2
- package/src/inputReader.ts +53 -0
- package/src/java.ts +708 -0
- package/src/javaKeyStoreParser.ts +18 -32
- package/src/javaParser.test.ts +1592 -0
- package/src/javaParser.ts +2640 -0
- package/src/javaScript.ts +36 -0
- package/src/javaScriptParser.ts +57 -0
- package/src/javaScriptUnparser.test.ts +37 -0
- package/src/javaScriptUnparser.ts +7 -0
- package/src/javaUnparser.test.ts +37 -0
- package/src/javaUnparser.ts +640 -0
- package/src/jsonParser.ts +6 -27
- package/src/lookaheadParser.ts +2 -6
- package/src/negativeLookaheadParser.ts +1 -3
- package/src/nonEmptyArrayParser.ts +1 -3
- package/src/objectParser.test-d.ts +152 -0
- package/src/objectParser.test.ts +71 -0
- package/src/objectParser.ts +69 -0
- package/src/optionalParser.ts +1 -3
- package/src/parser.test.ts +151 -4
- package/src/parser.ts +11 -1
- package/src/parserConsumedSequenceParser.ts +2 -4
- package/src/parserContext.ts +26 -11
- package/src/parserError.ts +17 -3
- package/src/regexpParser.test.ts +264 -0
- package/src/regexpParser.ts +126 -0
- package/src/regularExpression.ts +24 -0
- package/src/regularExpressionParser.test.ts +102 -0
- package/src/regularExpressionParser.ts +920 -0
- package/src/separatedArrayParser.ts +1 -3
- package/src/separatedNonEmptyArrayParser.ts +1 -3
- package/src/sliceBoundedParser.test.ts +2 -2
- package/src/sliceBoundedParser.ts +15 -19
- package/src/smaliParser.test.ts +64 -0
- package/src/smaliParser.test.ts.md +12 -12
- package/src/smaliParser.test.ts.snap +0 -0
- package/src/smaliParser.ts +246 -534
- package/src/stringEscapes.ts +253 -0
- package/src/symbolicExpression.ts +17 -0
- package/src/symbolicExpressionParser.test.ts +466 -0
- package/src/symbolicExpressionParser.ts +190 -0
- package/src/terminatedArrayParser.test.ts +9 -6
- package/src/terminatedArrayParser.ts +25 -29
- package/src/tupleParser.ts +21 -18
- package/src/unionParser.test-d.ts +105 -0
- package/src/unionParser.test.ts +18 -17
- package/src/unionParser.ts +28 -16
- package/src/zig.ts +411 -0
- package/src/zigParser.test.ts +1693 -0
- package/src/zigParser.ts +1745 -0
- package/src/zigUnparser.test.ts +37 -0
- package/src/zigUnparser.ts +615 -0
- package/src/zipParser.ts +20 -56
- package/src/zipUnparser.test.ts +1 -1
- package/src/zipUnparser.ts +22 -7
- package/tsconfig.json +2 -2
- package/build/exactElementSwitchParser.d.ts +0 -3
- package/src/exactElementSwitchParser.ts +0 -41
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futpib/parser",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"main": "build/index.js",
|
|
5
|
+
"types": "build/index.d.ts",
|
|
5
6
|
"license": "GPL-3.0-only",
|
|
6
7
|
"type": "module",
|
|
7
8
|
"scripts": {
|
|
8
9
|
"dev": "tsc --watch",
|
|
9
10
|
"build": "tsc",
|
|
11
|
+
"tsd": "tsd --files 'src/**/*.test-d.ts'",
|
|
10
12
|
"test": "c8 ava",
|
|
11
13
|
"prepack": "yarn build"
|
|
12
14
|
},
|
|
@@ -21,13 +23,26 @@
|
|
|
21
23
|
"verbose": true,
|
|
22
24
|
"timeout": "60s"
|
|
23
25
|
},
|
|
26
|
+
"tsd": {
|
|
27
|
+
"directory": "src",
|
|
28
|
+
"compilerOptions": {
|
|
29
|
+
"lib": [
|
|
30
|
+
"ES2022",
|
|
31
|
+
"DOM",
|
|
32
|
+
"DOM.AsyncIterable"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
},
|
|
24
36
|
"devDependencies": {
|
|
25
37
|
"@ava/typescript": "^6.0.0",
|
|
26
38
|
"@fast-check/ava": "^2.0.2",
|
|
39
|
+
"@futpib/fetch-cid": "^1.0.2",
|
|
40
|
+
"@gruhn/regex-utils": "^2.7.3",
|
|
41
|
+
"@types/estree": "^1.0.8",
|
|
27
42
|
"@types/invariant": "^2.2.37",
|
|
28
|
-
"@types/node": "^24.
|
|
43
|
+
"@types/node": "^24.10.1",
|
|
29
44
|
"ava": "^6.4.1",
|
|
30
|
-
"bson": "^
|
|
45
|
+
"bson": "^7.0.0",
|
|
31
46
|
"c8": "^10.1.3",
|
|
32
47
|
"coveralls": "^3.1.1",
|
|
33
48
|
"env-paths": "^3.0.0",
|
|
@@ -39,13 +54,17 @@
|
|
|
39
54
|
"leb128": "^0.0.5",
|
|
40
55
|
"mutf-8": "^1.2.2",
|
|
41
56
|
"p-memoize": "^8.0.0",
|
|
57
|
+
"s-expression": "^3.1.1",
|
|
42
58
|
"tempy": "^3.1.0",
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
59
|
+
"tsd": "^0.33.0",
|
|
60
|
+
"type-fest": "^5.2.0",
|
|
61
|
+
"typescript": "^5.9.3",
|
|
62
|
+
"xo": "^1.2.3"
|
|
46
63
|
},
|
|
47
64
|
"packageManager": "yarn@4.9.4",
|
|
48
65
|
"dependencies": {
|
|
66
|
+
"acorn": "^8.15.0",
|
|
67
|
+
"astring": "^1.9.0",
|
|
49
68
|
"fp-ts": "^2.16.11",
|
|
50
69
|
"mem": "^10.0.0",
|
|
51
70
|
"monocle-ts": "^2.3.13",
|
|
@@ -26,6 +26,7 @@ import { createExactSequenceParser } from './exactSequenceParser.js';
|
|
|
26
26
|
import { createSliceBoundedParser } from './sliceBoundedParser.js';
|
|
27
27
|
import { createDisjunctionParser } from './disjunctionParser.js';
|
|
28
28
|
import { createExactElementParser } from './exactElementParser.js';
|
|
29
|
+
import { createObjectParser } from './objectParser.js';
|
|
29
30
|
import { createParserConsumedSequenceParser } from './parserConsumedSequenceParser.js';
|
|
30
31
|
import { createDebugLogInputParser } from './debugLogInputParser.js';
|
|
31
32
|
import { createDebugLogParser } from './debugLogParser.js';
|
|
@@ -86,13 +87,11 @@ type AndroidPackageSigningBlockSignatureV2Pair = {
|
|
|
86
87
|
};
|
|
87
88
|
|
|
88
89
|
const createAndroidPackageSigningBlockSignatureV2PairInnerParser = (length: number): Parser<AndroidPackageSigningBlockSignatureV2Pair, Uint8Array> => {
|
|
89
|
-
const androidPackageSigningBlockSignatureV2PairInnerParser =
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
([ _magic, signers = [] ]) => ({ type: 'signatureV2' as const, signers }),
|
|
95
|
-
);
|
|
90
|
+
const androidPackageSigningBlockSignatureV2PairInnerParser = createObjectParser({
|
|
91
|
+
type: 'signatureV2' as const,
|
|
92
|
+
_magic: createExactSequenceParser<Uint8Array>(Buffer.from('1a870971', 'hex')),
|
|
93
|
+
signers: androidPackageSignatureV2SignersParser,
|
|
94
|
+
});
|
|
96
95
|
|
|
97
96
|
return setParserName(androidPackageSigningBlockSignatureV2PairInnerParser, 'androidPackageSigningBlockSignatureV2PairInnerParser');
|
|
98
97
|
};
|
|
@@ -102,13 +101,13 @@ type AndroidPackageSigningBlockGenericPair = {
|
|
|
102
101
|
pair: AndroidPackageSigningBlockPair;
|
|
103
102
|
};
|
|
104
103
|
|
|
105
|
-
const createAndroidPackageSigningBlockGenericPairInnerParser = (length: number): Parser<AndroidPackageSigningBlockGenericPair, Uint8Array> =>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
);
|
|
104
|
+
const createAndroidPackageSigningBlockGenericPairInnerParser = (length: number): Parser<AndroidPackageSigningBlockGenericPair, Uint8Array> => createObjectParser({
|
|
105
|
+
type: 'generic' as const,
|
|
106
|
+
pair: createObjectParser({
|
|
107
|
+
id: uint32LEParser,
|
|
108
|
+
value: createFixedLengthSequenceParser<Uint8Array>(length - 4),
|
|
109
|
+
}),
|
|
110
|
+
});
|
|
112
111
|
|
|
113
112
|
type AndroidPackageSigningBlockPairType =
|
|
114
113
|
| AndroidPackageSigningBlockZeroPaddingPair
|
|
@@ -126,7 +125,7 @@ const createAndroidPackageSigningBlockPairInnerParser = (length: number): Parser
|
|
|
126
125
|
length,
|
|
127
126
|
);
|
|
128
127
|
},
|
|
129
|
-
createDisjunctionParser
|
|
128
|
+
createDisjunctionParser([
|
|
130
129
|
createAndroidPackageSigningBlockZeroPaddingPairInnerParser(length),
|
|
131
130
|
createAndroidPackageSigningBlockSignatureV2PairInnerParser(length),
|
|
132
131
|
createAndroidPackageSigningBlockGenericPairInnerParser(length),
|
|
@@ -189,13 +188,10 @@ export const androidPackageSigningBlockParser: Parser<AndroidPackageSigningBlock
|
|
|
189
188
|
},
|
|
190
189
|
));
|
|
191
190
|
|
|
192
|
-
const androidPackageSignatureV2DigestParser = createUint32LengthPrefixedParser<AndroidPackageSignatureV2Digest>(pairLength =>
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
]),
|
|
197
|
-
([ signatureAlgorithmId, digest ]) => ({ signatureAlgorithmId, digest }),
|
|
198
|
-
));
|
|
191
|
+
const androidPackageSignatureV2DigestParser = createUint32LengthPrefixedParser<AndroidPackageSignatureV2Digest>(pairLength => createObjectParser({
|
|
192
|
+
signatureAlgorithmId: uint32LEParser,
|
|
193
|
+
digest: createUint32LengthPrefixedParser(digestLength => createFixedLengthSequenceParser(digestLength)),
|
|
194
|
+
}));
|
|
199
195
|
|
|
200
196
|
const androidPackageSignatureV2DigestsParser = createUint32LengthPrefixedSliceBoundedArrayParser(androidPackageSignatureV2DigestParser);
|
|
201
197
|
|
|
@@ -209,13 +205,10 @@ const androidPackageSignatureV2CertificatesParser = createUint32LengthPrefixedSl
|
|
|
209
205
|
|
|
210
206
|
setParserName(androidPackageSignatureV2CertificatesParser, 'androidPackageSignatureV2CertificatesParser');
|
|
211
207
|
|
|
212
|
-
const androidPackageSignatureV2AdditionalAttributeParser = createUint32LengthPrefixedParser<AndroidPackageSignatureV2AdditionalAttribute>(pairLength =>
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
]),
|
|
217
|
-
([ id, value ]) => ({ id, value }),
|
|
218
|
-
));
|
|
208
|
+
const androidPackageSignatureV2AdditionalAttributeParser = createUint32LengthPrefixedParser<AndroidPackageSignatureV2AdditionalAttribute>(pairLength => createObjectParser({
|
|
209
|
+
id: uint32LEParser,
|
|
210
|
+
value: createFixedLengthSequenceParser<Uint8Array>(pairLength - 4),
|
|
211
|
+
}));
|
|
219
212
|
|
|
220
213
|
setParserName(androidPackageSignatureV2AdditionalAttributeParser, 'androidPackageSignatureV2AdditionalAttributeParser');
|
|
221
214
|
|
|
@@ -228,7 +221,7 @@ const androidPackageSignatureV2SignedDataParser = createUint32LengthPrefixedSlic
|
|
|
228
221
|
androidPackageSignatureV2DigestsParser,
|
|
229
222
|
androidPackageSignatureV2CertificatesParser,
|
|
230
223
|
androidPackageSignatureV2AdditionalAttributesParser,
|
|
231
|
-
createArrayParser(createExactElementParser(0)),
|
|
224
|
+
createArrayParser(createExactElementParser<Uint8Array>(0)),
|
|
232
225
|
]),
|
|
233
226
|
([
|
|
234
227
|
digests,
|
|
@@ -245,19 +238,10 @@ const androidPackageSignatureV2SignedDataParser = createUint32LengthPrefixedSlic
|
|
|
245
238
|
|
|
246
239
|
setParserName(androidPackageSignatureV2SignedDataParser, 'androidPackageSignatureV2SignedDataParser');
|
|
247
240
|
|
|
248
|
-
const androidPackageSignatureV2SignatureParser = createUint32LengthPrefixedParser(signatureLength =>
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
]),
|
|
253
|
-
([
|
|
254
|
-
signatureAlgorithmId,
|
|
255
|
-
signature,
|
|
256
|
-
]): AndroidPackageSignatureV2Signature => ({
|
|
257
|
-
signatureAlgorithmId,
|
|
258
|
-
signature,
|
|
259
|
-
}),
|
|
260
|
-
));
|
|
241
|
+
const androidPackageSignatureV2SignatureParser = createUint32LengthPrefixedParser(signatureLength => createObjectParser({
|
|
242
|
+
signatureAlgorithmId: uint32LEParser,
|
|
243
|
+
signature: createUint32LengthPrefixedParser(signatureLength => createFixedLengthSequenceParser(signatureLength)),
|
|
244
|
+
}));
|
|
261
245
|
|
|
262
246
|
const androidPackageSignatureV2SignaturesParser = createUint32LengthPrefixedSliceBoundedArrayParser(androidPackageSignatureV2SignatureParser);
|
|
263
247
|
|
|
@@ -267,22 +251,11 @@ const androidPackageSignatureV2PublicKeyParser = createUint32LengthPrefixedParse
|
|
|
267
251
|
|
|
268
252
|
setParserName(androidPackageSignatureV2PublicKeyParser, 'androidPackageSignatureV2PublicKeyParser');
|
|
269
253
|
|
|
270
|
-
const androidPackageSignatureV2SignerParser = createUint32LengthPrefixedSliceBoundedParser(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
]),
|
|
276
|
-
([
|
|
277
|
-
signedData,
|
|
278
|
-
signatures = [],
|
|
279
|
-
publicKey,
|
|
280
|
-
]): AndroidPackageSignatureV2Signer => ({
|
|
281
|
-
signedData,
|
|
282
|
-
signatures,
|
|
283
|
-
publicKey,
|
|
284
|
-
}),
|
|
285
|
-
));
|
|
254
|
+
const androidPackageSignatureV2SignerParser: Parser<AndroidPackageSignatureV2Signer, Uint8Array> = createUint32LengthPrefixedSliceBoundedParser(createObjectParser({
|
|
255
|
+
signedData: androidPackageSignatureV2SignedDataParser,
|
|
256
|
+
signatures: androidPackageSignatureV2SignaturesParser,
|
|
257
|
+
publicKey: androidPackageSignatureV2PublicKeyParser,
|
|
258
|
+
}));
|
|
286
259
|
|
|
287
260
|
setParserName(androidPackageSignatureV2SignerParser, 'androidPackageSignatureV2SignerParser');
|
|
288
261
|
|
|
@@ -350,7 +323,7 @@ export const androidPackageSignableSectionsParser: Parser<AndroidPackageSignable
|
|
|
350
323
|
createOptionalParser(zipArchiveDecryptionHeaderParser),
|
|
351
324
|
createOptionalParser(zipArchiveExtraDataRecordParser),
|
|
352
325
|
])),
|
|
353
|
-
createArrayParser(createExactElementParser(0)),
|
|
326
|
+
createArrayParser(createExactElementParser<Uint8Array>(0)),
|
|
354
327
|
createOptionalParser(createParserConsumedSequenceParser(androidPackageSigningBlockParser)),
|
|
355
328
|
createParserConsumedSequenceParser(createTupleParser([
|
|
356
329
|
createArrayParser(zipCentralDirectoryHeaderParser),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fc from 'fast-check';
|
|
2
2
|
import {
|
|
3
|
-
type
|
|
4
|
-
type
|
|
3
|
+
type RawDalvikBytecode,
|
|
4
|
+
type RawDalvikBytecodeOperation,
|
|
5
5
|
} from './dalvikBytecodeParser.js';
|
|
6
6
|
import {
|
|
7
7
|
type IndexIntoStringIds,
|
|
@@ -9,11 +9,13 @@ import {
|
|
|
9
9
|
type IndexIntoMethodIds,
|
|
10
10
|
type IndexIntoFieldIds,
|
|
11
11
|
type IndexIntoPrototypeIds,
|
|
12
|
+
type CodeUnit,
|
|
12
13
|
isoIndexIntoStringIds,
|
|
13
14
|
isoIndexIntoTypeIds,
|
|
14
15
|
isoIndexIntoMethodIds,
|
|
15
16
|
isoIndexIntoFieldIds,
|
|
16
17
|
isoIndexIntoPrototypeIds,
|
|
18
|
+
isoCodeUnit,
|
|
17
19
|
} from './dalvikExecutableParser/typedNumbers.js';
|
|
18
20
|
|
|
19
21
|
// Arbitrary generators for typed indexes
|
|
@@ -49,13 +51,19 @@ const arbitraryShortValue = fc.integer({ min: -32768, max: 32767 }); // 16-bit s
|
|
|
49
51
|
const arbitraryIntValue = fc.integer({ min: -2147483648, max: 2147483647 }); // 32-bit signed
|
|
50
52
|
const arbitraryLongValue = fc.bigInt({ min: -9223372036854775808n, max: 9223372036854775807n }); // 64-bit signed
|
|
51
53
|
|
|
52
|
-
// Arbitrary branch offsets (relative)
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
// Arbitrary branch offsets (relative) - wrapped as CodeUnit
|
|
55
|
+
const arbitraryBranchOffsetCodeUnit8: fc.Arbitrary<CodeUnit> = fc
|
|
56
|
+
.integer({ min: -128, max: 127 })
|
|
57
|
+
.map(n => isoCodeUnit.wrap(n));
|
|
58
|
+
const arbitraryBranchOffsetCodeUnit16: fc.Arbitrary<CodeUnit> = fc
|
|
59
|
+
.integer({ min: -32768, max: 32767 })
|
|
60
|
+
.map(n => isoCodeUnit.wrap(n));
|
|
61
|
+
const arbitraryBranchOffsetCodeUnit32: fc.Arbitrary<CodeUnit> = fc
|
|
62
|
+
.integer({ min: -2147483648, max: 2147483647 })
|
|
63
|
+
.map(n => isoCodeUnit.wrap(n));
|
|
56
64
|
|
|
57
65
|
// No-operation
|
|
58
|
-
const arbitraryNop = fc.constant<
|
|
66
|
+
const arbitraryNop = fc.constant<RawDalvikBytecodeOperation>({
|
|
59
67
|
operation: 'nop',
|
|
60
68
|
});
|
|
61
69
|
|
|
@@ -119,7 +127,7 @@ const arbitraryMoveException = fc.record({
|
|
|
119
127
|
});
|
|
120
128
|
|
|
121
129
|
// Return operations
|
|
122
|
-
const arbitraryReturnVoid = fc.constant<
|
|
130
|
+
const arbitraryReturnVoid = fc.constant<RawDalvikBytecodeOperation>({
|
|
123
131
|
operation: 'return-void',
|
|
124
132
|
});
|
|
125
133
|
|
|
@@ -265,36 +273,36 @@ const arbitraryThrow = fc.record({
|
|
|
265
273
|
// Goto operations
|
|
266
274
|
const arbitraryGoto = fc.record({
|
|
267
275
|
operation: fc.constant('goto' as const),
|
|
268
|
-
|
|
276
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit8,
|
|
269
277
|
});
|
|
270
278
|
|
|
271
279
|
const arbitraryGoto16 = fc.record({
|
|
272
280
|
operation: fc.constant('goto/16' as const),
|
|
273
|
-
|
|
281
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
274
282
|
});
|
|
275
283
|
|
|
276
284
|
const arbitraryGoto32 = fc.record({
|
|
277
285
|
operation: fc.constant('goto/32' as const),
|
|
278
|
-
|
|
286
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit32,
|
|
279
287
|
});
|
|
280
288
|
|
|
281
289
|
// Switch operations
|
|
282
290
|
const arbitraryPackedSwitch = fc.record({
|
|
283
291
|
operation: fc.constant('packed-switch' as const),
|
|
284
292
|
registers: fc.tuple(arbitraryRegister8),
|
|
285
|
-
|
|
293
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit32,
|
|
286
294
|
});
|
|
287
295
|
|
|
288
296
|
const arbitrarySparseSwitch = fc.record({
|
|
289
297
|
operation: fc.constant('sparse-switch' as const),
|
|
290
298
|
registers: fc.tuple(arbitraryRegister8),
|
|
291
|
-
|
|
299
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit32,
|
|
292
300
|
});
|
|
293
301
|
|
|
294
302
|
const arbitraryFillArrayData = fc.record({
|
|
295
303
|
operation: fc.constant('fill-array-data' as const),
|
|
296
304
|
registers: fc.tuple(arbitraryRegister8),
|
|
297
|
-
|
|
305
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit32,
|
|
298
306
|
});
|
|
299
307
|
|
|
300
308
|
// Payload operations
|
|
@@ -304,7 +312,7 @@ const arbitraryPackedSwitchPayload = fc
|
|
|
304
312
|
fc.record({
|
|
305
313
|
operation: fc.constant('packed-switch-payload' as const),
|
|
306
314
|
value: arbitraryIntValue,
|
|
307
|
-
|
|
315
|
+
branchOffsetsCodeUnit: fc.array(arbitraryBranchOffsetCodeUnit32, { minLength: size, maxLength: size }),
|
|
308
316
|
})
|
|
309
317
|
);
|
|
310
318
|
|
|
@@ -314,7 +322,7 @@ const arbitrarySparseSwitchPayload = fc
|
|
|
314
322
|
fc.record({
|
|
315
323
|
operation: fc.constant('sparse-switch-payload' as const),
|
|
316
324
|
keys: fc.array(arbitraryIntValue, { minLength: size, maxLength: size }),
|
|
317
|
-
|
|
325
|
+
branchOffsetsCodeUnit: fc.array(arbitraryBranchOffsetCodeUnit32, { minLength: size, maxLength: size }),
|
|
318
326
|
})
|
|
319
327
|
);
|
|
320
328
|
|
|
@@ -337,74 +345,74 @@ const arbitraryFillArrayDataPayload = fc
|
|
|
337
345
|
const arbitraryIfEqual = fc.record({
|
|
338
346
|
operation: fc.constant('if-eq' as const),
|
|
339
347
|
registers: fc.tuple(arbitraryRegister4, arbitraryRegister4).map(([a, b]) => [a, b].sort((x, y) => x - y)),
|
|
340
|
-
|
|
348
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
341
349
|
});
|
|
342
350
|
|
|
343
351
|
const arbitraryIfNotEqual = fc.record({
|
|
344
352
|
operation: fc.constant('if-ne' as const),
|
|
345
353
|
registers: fc.tuple(arbitraryRegister4, arbitraryRegister4).map(([a, b]) => [a, b].sort((x, y) => x - y)),
|
|
346
|
-
|
|
354
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
347
355
|
});
|
|
348
356
|
|
|
349
357
|
const arbitraryIfLessThan = fc.record({
|
|
350
358
|
operation: fc.constant('if-lt' as const),
|
|
351
359
|
registers: fc.tuple(arbitraryRegister4, arbitraryRegister4),
|
|
352
|
-
|
|
360
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
353
361
|
});
|
|
354
362
|
|
|
355
363
|
const arbitraryIfGreaterThanOrEqualTo = fc.record({
|
|
356
364
|
operation: fc.constant('if-ge' as const),
|
|
357
365
|
registers: fc.tuple(arbitraryRegister4, arbitraryRegister4),
|
|
358
|
-
|
|
366
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
359
367
|
});
|
|
360
368
|
|
|
361
369
|
const arbitraryIfGreaterThan = fc.record({
|
|
362
370
|
operation: fc.constant('if-gt' as const),
|
|
363
371
|
registers: fc.tuple(arbitraryRegister4, arbitraryRegister4),
|
|
364
|
-
|
|
372
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
365
373
|
});
|
|
366
374
|
|
|
367
375
|
const arbitraryIfLessThanOrEqualTo = fc.record({
|
|
368
376
|
operation: fc.constant('if-le' as const),
|
|
369
377
|
registers: fc.tuple(arbitraryRegister4, arbitraryRegister4),
|
|
370
|
-
|
|
378
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
371
379
|
});
|
|
372
380
|
|
|
373
381
|
// If-test-zero operations (Format 21t)
|
|
374
382
|
const arbitraryIfEqualZero = fc.record({
|
|
375
383
|
operation: fc.constant('if-eqz' as const),
|
|
376
384
|
registers: fc.tuple(arbitraryRegister8),
|
|
377
|
-
|
|
385
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
378
386
|
});
|
|
379
387
|
|
|
380
388
|
const arbitraryIfNotEqualZero = fc.record({
|
|
381
389
|
operation: fc.constant('if-nez' as const),
|
|
382
390
|
registers: fc.tuple(arbitraryRegister8),
|
|
383
|
-
|
|
391
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
384
392
|
});
|
|
385
393
|
|
|
386
394
|
const arbitraryIfLessThanZero = fc.record({
|
|
387
395
|
operation: fc.constant('if-ltz' as const),
|
|
388
396
|
registers: fc.tuple(arbitraryRegister8),
|
|
389
|
-
|
|
397
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
390
398
|
});
|
|
391
399
|
|
|
392
400
|
const arbitraryIfGreaterThanOrEqualToZero = fc.record({
|
|
393
401
|
operation: fc.constant('if-gez' as const),
|
|
394
402
|
registers: fc.tuple(arbitraryRegister8),
|
|
395
|
-
|
|
403
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
396
404
|
});
|
|
397
405
|
|
|
398
406
|
const arbitraryIfGreaterThanZero = fc.record({
|
|
399
407
|
operation: fc.constant('if-gtz' as const),
|
|
400
408
|
registers: fc.tuple(arbitraryRegister8),
|
|
401
|
-
|
|
409
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
402
410
|
});
|
|
403
411
|
|
|
404
412
|
const arbitraryIfLessThanOrEqualToZero = fc.record({
|
|
405
413
|
operation: fc.constant('if-lez' as const),
|
|
406
414
|
registers: fc.tuple(arbitraryRegister8),
|
|
407
|
-
|
|
415
|
+
branchOffsetCodeUnit: arbitraryBranchOffsetCodeUnit16,
|
|
408
416
|
});
|
|
409
417
|
|
|
410
418
|
// Array element operations (Format 23x)
|
|
@@ -740,7 +748,7 @@ const arbitraryIntToChar = createArbitraryUnaryOperation('int-to-char');
|
|
|
740
748
|
const arbitraryIntToShort = createArbitraryUnaryOperation('int-to-short');
|
|
741
749
|
|
|
742
750
|
// Combine all operations
|
|
743
|
-
export const
|
|
751
|
+
export const arbitraryRawDalvikBytecodeOperation: fc.Arbitrary<RawDalvikBytecodeOperation> = fc.oneof(
|
|
744
752
|
arbitraryNop,
|
|
745
753
|
// Move operations
|
|
746
754
|
arbitraryMove,
|
|
@@ -986,7 +994,7 @@ export const arbitraryDalvikBytecodeOperation: fc.Arbitrary<DalvikBytecodeOperat
|
|
|
986
994
|
);
|
|
987
995
|
|
|
988
996
|
// Arbitrary for complete Dalvik bytecode (array of operations)
|
|
989
|
-
export const
|
|
990
|
-
|
|
997
|
+
export const arbitraryRawDalvikBytecode: fc.Arbitrary<RawDalvikBytecode> = fc.array(
|
|
998
|
+
arbitraryRawDalvikBytecodeOperation,
|
|
991
999
|
{ minLength: 0, maxLength: 100 }
|
|
992
1000
|
);
|
|
@@ -288,52 +288,97 @@ const arbitraryDalvikExecutableDebugInfo: fc.Arbitrary<DalvikExecutableDebugInfo
|
|
|
288
288
|
|
|
289
289
|
// Try-catch handler generators
|
|
290
290
|
interface DalvikExecutableTry {
|
|
291
|
-
|
|
291
|
+
startInstructionIndex: number;
|
|
292
292
|
instructionCount: number;
|
|
293
293
|
handler: DalvikExecutableEncodedCatchHandler;
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
interface DalvikExecutableEncodedCatchHandler {
|
|
297
297
|
handlers: DalvikExecutableEncodedTypeAddressPair[];
|
|
298
|
-
|
|
298
|
+
catchAllInstructionIndex: undefined | number;
|
|
299
299
|
}
|
|
300
300
|
|
|
301
301
|
interface DalvikExecutableEncodedTypeAddressPair {
|
|
302
302
|
type: string;
|
|
303
|
-
|
|
303
|
+
handlerInstructionIndex: number;
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
306
|
+
// Factory function to create arbitrary try blocks that are valid for given instruction count
|
|
307
|
+
// Since addresses are now instruction indices, they must be within [0, instructionCount]
|
|
308
|
+
function createArbitraryDalvikExecutableTry(instructionCount: number): fc.Arbitrary<DalvikExecutableTry> {
|
|
309
|
+
// If no instructions, we can't have meaningful try blocks
|
|
310
|
+
if (instructionCount === 0) {
|
|
311
|
+
// Return a try block that covers the "after last instruction" position (index 0)
|
|
312
|
+
const arbitraryEmptyHandler: fc.Arbitrary<DalvikExecutableEncodedCatchHandler> = fc.record({
|
|
313
|
+
handlers: fc.constant([]),
|
|
314
|
+
catchAllInstructionIndex: fc.constant(0),
|
|
315
|
+
});
|
|
316
|
+
return fc.record({
|
|
317
|
+
startInstructionIndex: fc.constant(0),
|
|
318
|
+
instructionCount: fc.constant(0),
|
|
319
|
+
handler: arbitraryEmptyHandler,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Addresses must be in range [0, instructionCount] (inclusive, since handler can point to end)
|
|
324
|
+
const maxAddress = instructionCount;
|
|
325
|
+
|
|
326
|
+
const arbitraryDalvikExecutableEncodedTypeAddressPair: fc.Arbitrary<DalvikExecutableEncodedTypeAddressPair> = fc.record({
|
|
327
|
+
type: arbitraryDalvikClassName,
|
|
328
|
+
handlerInstructionIndex: fc.nat({ max: maxAddress }),
|
|
329
|
+
});
|
|
310
330
|
|
|
311
|
-
const arbitraryDalvikExecutableEncodedCatchHandler: fc.Arbitrary<DalvikExecutableEncodedCatchHandler> = fc.record({
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}).filter(handler => {
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
});
|
|
331
|
+
const arbitraryDalvikExecutableEncodedCatchHandler: fc.Arbitrary<DalvikExecutableEncodedCatchHandler> = fc.record({
|
|
332
|
+
handlers: fc.array(arbitraryDalvikExecutableEncodedTypeAddressPair, { maxLength: 3 }),
|
|
333
|
+
catchAllInstructionIndex: fc.option(fc.nat({ max: maxAddress }), { nil: undefined }),
|
|
334
|
+
}).filter(handler => {
|
|
335
|
+
// A handler must have at least one typed handler OR a catch-all address
|
|
336
|
+
return handler.handlers.length > 0 || handler.catchAllInstructionIndex !== undefined;
|
|
337
|
+
});
|
|
318
338
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
339
|
+
// startInstructionIndex + instructionCount must not exceed instructionCount (the total)
|
|
340
|
+
return fc.tuple(
|
|
341
|
+
fc.nat({ max: instructionCount }),
|
|
342
|
+
fc.nat({ max: instructionCount }),
|
|
343
|
+
).chain(([start, count]) => {
|
|
344
|
+
// Adjust count so start + count <= instructionCount
|
|
345
|
+
const adjustedCount = Math.min(count, instructionCount - start);
|
|
346
|
+
return fc.record({
|
|
347
|
+
startInstructionIndex: fc.constant(start),
|
|
348
|
+
instructionCount: fc.constant(adjustedCount),
|
|
349
|
+
handler: arbitraryDalvikExecutableEncodedCatchHandler,
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Helper function to get instruction count from instructions
|
|
355
|
+
// This handles both array and non-array instruction types
|
|
356
|
+
function getInstructionCount<Instructions>(instructions: Instructions): number {
|
|
357
|
+
if (Array.isArray(instructions)) {
|
|
358
|
+
return instructions.length;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return 0; // Non-array instructions default to 0
|
|
362
|
+
}
|
|
324
363
|
|
|
325
364
|
// Generic factory function for DalvikExecutable
|
|
326
365
|
export const createArbitraryDalvikExecutable = <Instructions>(
|
|
327
366
|
arbitraryInstructions: fc.Arbitrary<Instructions>,
|
|
328
367
|
): fc.Arbitrary<DalvikExecutable<Instructions>> => {
|
|
329
368
|
// Code generator using provided instructions arbitrary
|
|
369
|
+
// First generate instructions, then generate valid tries based on instruction count
|
|
330
370
|
const arbitraryDalvikExecutableCode: fc.Arbitrary<DalvikExecutableCode<Instructions>> = fc.record({
|
|
331
371
|
registersSize: fc.nat({ max: 65535 }),
|
|
332
372
|
insSize: fc.nat({ max: 255 }),
|
|
333
373
|
outsSize: fc.nat({ max: 255 }),
|
|
334
374
|
debugInfo: fc.option(arbitraryDalvikExecutableDebugInfo, { nil: undefined }),
|
|
335
375
|
instructions: arbitraryInstructions,
|
|
336
|
-
|
|
376
|
+
}).chain(partialCode => {
|
|
377
|
+
const instructionCount = getInstructionCount(partialCode.instructions);
|
|
378
|
+
return fc.array(createArbitraryDalvikExecutableTry(instructionCount), { maxLength: 2 }).map(tries => ({
|
|
379
|
+
...partialCode,
|
|
380
|
+
tries,
|
|
381
|
+
}));
|
|
337
382
|
});
|
|
338
383
|
|
|
339
384
|
// Method with access and code
|