@futpib/parser 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +6 -1
- package/build/bashParser.js +131 -90
- package/build/bashParser.test.js +162 -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 +3 -2
- package/build/index.js +2 -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.js +33 -3
- package/build/regexpParser.test.js +31 -0
- package/build/regularExpressionParser.js +35 -15
- 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 +24 -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 +8 -1
- package/src/bashParser.test.ts +258 -0
- package/src/bashParser.ts +180 -143
- 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 +7 -1
- 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 +78 -0
- package/src/regexpParser.ts +35 -3
- package/src/regularExpressionParser.ts +36 -37
- 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
|
@@ -25,8 +25,16 @@ import {
|
|
|
25
25
|
isDalvikExecutableField,
|
|
26
26
|
isDalvikExecutableMethod,
|
|
27
27
|
} from '../dalvikExecutable.js';
|
|
28
|
-
import { type
|
|
29
|
-
import {
|
|
28
|
+
import { type RawDalvikBytecodeOperation } from '../dalvikBytecodeParser.js';
|
|
29
|
+
import { rawDalvikBytecodeUnparser } from '../dalvikBytecodeUnparser.js';
|
|
30
|
+
import {
|
|
31
|
+
type DalvikBytecodeOperation,
|
|
32
|
+
buildIndexToCodeUnitMapFromResolved,
|
|
33
|
+
instructionIndexToCodeUnit,
|
|
34
|
+
wrapBranchOffsets,
|
|
35
|
+
convertInstructionOffsetsToBranchOffsets,
|
|
36
|
+
} from '../dalvikBytecodeParser/addressConversion.js';
|
|
37
|
+
import { isoCodeUnit, isoInstructionIndex } from '../dalvikExecutableParser/typedNumbers.js';
|
|
30
38
|
import { ubyteUnparser, ushortUnparser, uintUnparser } from '../dalvikBytecodeUnparser/formatUnparsers.js';
|
|
31
39
|
import { type PoolBuilders } from './poolBuilders.js';
|
|
32
40
|
import { WriteLater } from '../unparserContext.js';
|
|
@@ -521,7 +529,7 @@ export function createSectionUnparsers(poolBuilders: PoolBuilders) {
|
|
|
521
529
|
yield * ubyteUnparser(0x00, unparserContext);
|
|
522
530
|
};
|
|
523
531
|
|
|
524
|
-
const codeItemUnparser = (callback?: (result: { debugInfoOffsetWriteLater?: WriteLater<Uint8Array, number> }) => void): Unparser<DalvikExecutableCode<
|
|
532
|
+
const codeItemUnparser = (callback?: (result: { debugInfoOffsetWriteLater?: WriteLater<Uint8Array, number> }) => void): Unparser<DalvikExecutableCode<DalvikBytecodeOperation[]>, Uint8Array> => {
|
|
525
533
|
return async function * (input, unparserContext) {
|
|
526
534
|
yield * ushortUnparser(input.registersSize, unparserContext);
|
|
527
535
|
yield * ushortUnparser(input.insSize, unparserContext);
|
|
@@ -540,21 +548,33 @@ export function createSectionUnparsers(poolBuilders: PoolBuilders) {
|
|
|
540
548
|
callback({ debugInfoOffsetWriteLater });
|
|
541
549
|
}
|
|
542
550
|
|
|
543
|
-
|
|
551
|
+
// Convert branch offsets from instruction offsets back to code units
|
|
552
|
+
// Tier 3 (plain numbers) -> Tier 2 (InstructionIndex) -> Tier 1 (CodeUnit)
|
|
553
|
+
const rawInstructions = convertInstructionOffsetsToBranchOffsets(wrapBranchOffsets(input.instructions));
|
|
554
|
+
|
|
555
|
+
const instructionsSizeInShorts = await calculateRawInstructionsSize(rawInstructions);
|
|
544
556
|
yield * uintUnparser(instructionsSizeInShorts, unparserContext);
|
|
545
557
|
|
|
546
|
-
yield *
|
|
558
|
+
yield * rawDalvikBytecodeUnparser(rawInstructions, unparserContext);
|
|
547
559
|
|
|
548
560
|
if (input.tries.length > 0 && instructionsSizeInShorts % 2 !== 0) {
|
|
549
561
|
yield * ushortUnparser(0, unparserContext);
|
|
550
562
|
}
|
|
551
563
|
|
|
552
564
|
if (input.tries.length > 0) {
|
|
565
|
+
// Build index to code unit mapping for address conversion
|
|
566
|
+
const indexToCodeUnitMap = buildIndexToCodeUnitMapFromResolved(input.instructions);
|
|
567
|
+
|
|
553
568
|
const handlerOffsetWriteLaters: Array<WriteLater<Uint8Array, number>> = [];
|
|
554
569
|
|
|
555
570
|
for (const tryBlock of input.tries) {
|
|
556
|
-
|
|
557
|
-
|
|
571
|
+
// Convert instruction indices back to code unit offsets
|
|
572
|
+
const startAddressCodeUnit = isoCodeUnit.unwrap(instructionIndexToCodeUnit(isoInstructionIndex.wrap(tryBlock.startInstructionIndex), indexToCodeUnitMap));
|
|
573
|
+
const endAddressCodeUnit = isoCodeUnit.unwrap(instructionIndexToCodeUnit(isoInstructionIndex.wrap(tryBlock.startInstructionIndex + tryBlock.instructionCount), indexToCodeUnitMap));
|
|
574
|
+
const instructionCountCodeUnits = endAddressCodeUnit - startAddressCodeUnit;
|
|
575
|
+
|
|
576
|
+
yield * uintUnparser(startAddressCodeUnit, unparserContext);
|
|
577
|
+
yield * ushortUnparser(instructionCountCodeUnits, unparserContext);
|
|
558
578
|
const handlerOffsetWriteLater = yield * yieldAndCapture(unparserContext.writeLater(2));
|
|
559
579
|
handlerOffsetWriteLaters.push(handlerOffsetWriteLater);
|
|
560
580
|
}
|
|
@@ -569,7 +589,7 @@ export function createSectionUnparsers(poolBuilders: PoolBuilders) {
|
|
|
569
589
|
const handlerOffset = unparserContext.position - handlersStartOffset;
|
|
570
590
|
yield * unparserContext.writeEarlier(handlerOffsetWriteLaters[i], ushortUnparser, handlerOffset);
|
|
571
591
|
|
|
572
|
-
if (handler.
|
|
592
|
+
if (handler.catchAllInstructionIndex !== undefined) {
|
|
573
593
|
yield * sleb128Unparser(-handler.handlers.length, unparserContext);
|
|
574
594
|
} else {
|
|
575
595
|
yield * sleb128Unparser(handler.handlers.length, unparserContext);
|
|
@@ -578,18 +598,22 @@ export function createSectionUnparsers(poolBuilders: PoolBuilders) {
|
|
|
578
598
|
for (const handlerItem of handler.handlers) {
|
|
579
599
|
const typeIndex = getTypeIndex(handlerItem.type);
|
|
580
600
|
yield * uleb128Unparser(typeIndex, unparserContext);
|
|
581
|
-
|
|
601
|
+
// Convert handler address from instruction index to code unit offset
|
|
602
|
+
const handlerAddressCodeUnit = isoCodeUnit.unwrap(instructionIndexToCodeUnit(isoInstructionIndex.wrap(handlerItem.handlerInstructionIndex), indexToCodeUnitMap));
|
|
603
|
+
yield * uleb128Unparser(handlerAddressCodeUnit, unparserContext);
|
|
582
604
|
}
|
|
583
605
|
|
|
584
|
-
if (handler.
|
|
585
|
-
|
|
606
|
+
if (handler.catchAllInstructionIndex !== undefined) {
|
|
607
|
+
// Convert catchAllInstructionIndex from instruction index to code unit offset
|
|
608
|
+
const catchAllAddressCodeUnit = isoCodeUnit.unwrap(instructionIndexToCodeUnit(isoInstructionIndex.wrap(handler.catchAllInstructionIndex), indexToCodeUnitMap));
|
|
609
|
+
yield * uleb128Unparser(catchAllAddressCodeUnit, unparserContext);
|
|
586
610
|
}
|
|
587
611
|
}
|
|
588
612
|
}
|
|
589
613
|
};
|
|
590
614
|
};
|
|
591
615
|
|
|
592
|
-
async function
|
|
616
|
+
async function calculateRawInstructionsSize(instructions: RawDalvikBytecodeOperation[]): Promise<number> {
|
|
593
617
|
let totalSize = 0;
|
|
594
618
|
|
|
595
619
|
const mockContext: UnparserContext<Uint8Array, number> = {
|
|
@@ -598,7 +622,7 @@ export function createSectionUnparsers(poolBuilders: PoolBuilders) {
|
|
|
598
622
|
writeEarlier: () => { throw new Error('Not supported'); },
|
|
599
623
|
};
|
|
600
624
|
|
|
601
|
-
for await (const chunk of
|
|
625
|
+
for await (const chunk of rawDalvikBytecodeUnparser(instructions, mockContext)) {
|
|
602
626
|
if (chunk instanceof Uint8Array) {
|
|
603
627
|
totalSize += chunk.length;
|
|
604
628
|
}
|
|
@@ -607,7 +631,7 @@ export function createSectionUnparsers(poolBuilders: PoolBuilders) {
|
|
|
607
631
|
return Math.floor(totalSize / 2);
|
|
608
632
|
}
|
|
609
633
|
|
|
610
|
-
const classDataUnparser = (codeOffsetMap: Map<DalvikExecutableCode<
|
|
634
|
+
const classDataUnparser = (codeOffsetMap: Map<DalvikExecutableCode<DalvikBytecodeOperation[]>, number>): Unparser<DalvikExecutableClassData<DalvikBytecodeOperation[]>, Uint8Array> => {
|
|
611
635
|
return async function * (input, unparserContext) {
|
|
612
636
|
yield * uleb128Unparser(input.staticFields.length, unparserContext);
|
|
613
637
|
yield * uleb128Unparser(input.instanceFields.length, unparserContext);
|
|
@@ -8,11 +8,12 @@ import { runUnparser } from './unparser.js';
|
|
|
8
8
|
import { uint8ArrayParserInputCompanion } from './parserInputCompanion.js';
|
|
9
9
|
import { uint8ArrayUnparserOutputCompanion } from './unparserOutputCompanion.js';
|
|
10
10
|
import { uint8ArrayAsyncIterableToUint8Array } from './uint8Array.js';
|
|
11
|
-
import { type DalvikBytecodeOperation } from './dalvikBytecodeParser.js';
|
|
11
|
+
import { type DalvikBytecodeOperation } from './dalvikBytecodeParser/addressConversion.js';
|
|
12
12
|
|
|
13
13
|
const seed = process.env.SEED ? Number(process.env.SEED) : undefined;
|
|
14
14
|
|
|
15
15
|
// Use minimal bytecode for testing - simple nop and return-void instructions
|
|
16
|
+
// These operations have no branch offsets, so they work the same in all tiers
|
|
16
17
|
const arbitraryMinimalBytecode = fc.array(
|
|
17
18
|
fc.oneof(
|
|
18
19
|
fc.constant<DalvikBytecodeOperation>({
|
|
@@ -37,7 +38,7 @@ testProp(
|
|
|
37
38
|
);
|
|
38
39
|
const bytes = await uint8ArrayAsyncIterableToUint8Array(unparsedIterable);
|
|
39
40
|
|
|
40
|
-
// Re-parse
|
|
41
|
+
// Re-parse (parser now outputs Tier 3 directly)
|
|
41
42
|
const reparsed = await runParser(
|
|
42
43
|
dalvikExecutableParser,
|
|
43
44
|
bytes,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Unparser } from './unparser.js';
|
|
2
2
|
import { type DalvikExecutable, type DalvikExecutableClassDefinition, type DalvikExecutableCode, type DalvikExecutableDebugInfo, type DalvikExecutableAnnotation, type DalvikExecutableEncodedValue } from './dalvikExecutable.js';
|
|
3
|
-
import { type
|
|
3
|
+
import { type DalvikBytecodeOperation } from './dalvikBytecodeParser/addressConversion.js';
|
|
4
4
|
import { uintUnparser, ushortUnparser } from './dalvikBytecodeUnparser/formatUnparsers.js';
|
|
5
5
|
import { createPoolBuilders } from './dalvikExecutableUnparser/poolBuilders.js';
|
|
6
6
|
import { scanForPoolReferences } from './dalvikExecutableUnparser/poolScanners.js';
|
|
@@ -76,7 +76,7 @@ class SectionTracker {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
export const dalvikExecutableUnparser: Unparser<DalvikExecutable<
|
|
79
|
+
export const dalvikExecutableUnparser: Unparser<DalvikExecutable<DalvikBytecodeOperation[]>, Uint8Array> = async function * (input, unparserContext) {
|
|
80
80
|
const poolBuilders = createPoolBuilders();
|
|
81
81
|
|
|
82
82
|
scanForPoolReferences(input, poolBuilders);
|
|
@@ -263,7 +263,7 @@ export const dalvikExecutableUnparser: Unparser<DalvikExecutable<DalvikBytecode>
|
|
|
263
263
|
// Track classDataItem, codeItem, and debugInfoItem for later writing
|
|
264
264
|
// Collect data to write items grouped by type (required by DEX format)
|
|
265
265
|
const classDataToWrite: Array<{
|
|
266
|
-
classDef: DalvikExecutableClassDefinition<
|
|
266
|
+
classDef: DalvikExecutableClassDefinition<DalvikBytecodeOperation[]>;
|
|
267
267
|
classDefItem: {
|
|
268
268
|
interfacesOffsetWriteLater?: WriteLater<Uint8Array, number>;
|
|
269
269
|
annotationsOffsetWriteLater?: WriteLater<Uint8Array, number>;
|
|
@@ -272,7 +272,7 @@ export const dalvikExecutableUnparser: Unparser<DalvikExecutable<DalvikBytecode>
|
|
|
272
272
|
};
|
|
273
273
|
classIdx: number;
|
|
274
274
|
}> = [];
|
|
275
|
-
const codeToWrite: Array<{ code: DalvikExecutableCode<
|
|
275
|
+
const codeToWrite: Array<{ code: DalvikExecutableCode<DalvikBytecodeOperation[]> }> = [];
|
|
276
276
|
const debugInfoToWrite: Array<{ debugInfo: DalvikExecutableDebugInfo; offsetWriteLater: WriteLater<Uint8Array, number> }> = [];
|
|
277
277
|
|
|
278
278
|
// First pass: collect type lists, encoded arrays, and classData/code/debugInfo to write
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { expectAssignable } from 'tsd';
|
|
2
|
+
import { createDisjunctionParser } from './disjunctionParser.js';
|
|
3
|
+
import { type Parser, type ParserOutput } from './parser.js';
|
|
4
|
+
import { createExactSequenceParser } from './exactSequenceParser.js';
|
|
5
|
+
import { createExactElementParser } from './exactElementParser.js';
|
|
6
|
+
import { createFixedLengthSequenceParser } from './fixedLengthSequenceParser.js';
|
|
7
|
+
|
|
8
|
+
// Test: basic disjunction of string parsers - output inferred as string
|
|
9
|
+
{
|
|
10
|
+
const parser = createDisjunctionParser([
|
|
11
|
+
createExactElementParser('a'),
|
|
12
|
+
createExactElementParser('b'),
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
type Output = ParserOutput<typeof parser>;
|
|
16
|
+
|
|
17
|
+
expectAssignable<string>(null! as Output);
|
|
18
|
+
expectAssignable<Output>(null! as string);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Test: disjunction preserves literal types when parsers have explicit literal output types
|
|
22
|
+
{
|
|
23
|
+
const parserA: Parser<'a', string> = async () => 'a' as const;
|
|
24
|
+
const parserB: Parser<'b', string> = async () => 'b' as const;
|
|
25
|
+
|
|
26
|
+
const parser = createDisjunctionParser([parserA, parserB]);
|
|
27
|
+
|
|
28
|
+
type Output = ParserOutput<typeof parser>;
|
|
29
|
+
|
|
30
|
+
// Output should be 'a' | 'b', not string
|
|
31
|
+
expectAssignable<'a' | 'b'>(null! as Output);
|
|
32
|
+
expectAssignable<Output>(null! as 'a' | 'b');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Test: disjunction of parsers with different output types
|
|
36
|
+
{
|
|
37
|
+
const stringParser: Parser<string, string> = createFixedLengthSequenceParser(3);
|
|
38
|
+
const numberParser: Parser<number, string> = () => 42;
|
|
39
|
+
|
|
40
|
+
const parser = createDisjunctionParser([
|
|
41
|
+
stringParser,
|
|
42
|
+
numberParser,
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
type Output = ParserOutput<typeof parser>;
|
|
46
|
+
|
|
47
|
+
// Output should be string | number
|
|
48
|
+
expectAssignable<string | number>(null! as Output);
|
|
49
|
+
expectAssignable<Output>(null! as string | number);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Test: nested disjunctions
|
|
53
|
+
{
|
|
54
|
+
const inner = createDisjunctionParser([
|
|
55
|
+
createExactElementParser('a'),
|
|
56
|
+
createExactElementParser('b'),
|
|
57
|
+
]);
|
|
58
|
+
|
|
59
|
+
const parser = createDisjunctionParser([
|
|
60
|
+
inner,
|
|
61
|
+
createExactElementParser('c'),
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
type Output = ParserOutput<typeof parser>;
|
|
65
|
+
|
|
66
|
+
expectAssignable<string>(null! as Output);
|
|
67
|
+
expectAssignable<Output>(null! as string);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Test: sequence type inferred from child parsers
|
|
71
|
+
{
|
|
72
|
+
const parser = createDisjunctionParser([
|
|
73
|
+
createExactSequenceParser('hello'),
|
|
74
|
+
createExactSequenceParser('world'),
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
type Output = ParserOutput<typeof parser>;
|
|
78
|
+
|
|
79
|
+
// Parser should be for string sequences, output is string (widened from literals)
|
|
80
|
+
expectAssignable<string>(null! as Output);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Test: single parser in disjunction
|
|
84
|
+
{
|
|
85
|
+
const parser = createDisjunctionParser([
|
|
86
|
+
createExactSequenceParser('only'),
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
type Output = ParserOutput<typeof parser>;
|
|
90
|
+
|
|
91
|
+
// Output is string (widened from literal 'only')
|
|
92
|
+
expectAssignable<string>(null! as Output);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Test: disjunction of object-producing parsers
|
|
96
|
+
{
|
|
97
|
+
const parser1: Parser<{ type: 'a'; value: number }, string> = async () => ({ type: 'a', value: 1 });
|
|
98
|
+
const parser2: Parser<{ type: 'b'; name: string }, string> = async () => ({ type: 'b', name: 'test' });
|
|
99
|
+
|
|
100
|
+
const parser = createDisjunctionParser([parser1, parser2]);
|
|
101
|
+
|
|
102
|
+
type Output = ParserOutput<typeof parser>;
|
|
103
|
+
|
|
104
|
+
expectAssignable<{ type: 'a'; value: number } | { type: 'b'; name: string }>(null! as Output);
|
|
105
|
+
}
|
package/src/disjunctionParser.ts
CHANGED
|
@@ -1,39 +1,42 @@
|
|
|
1
|
-
import { getParserName, setParserName, type Parser } from './parser.js';
|
|
1
|
+
import { getParserName, setParserName, type Parser, type ParserOutput, type ParserSequence } from './parser.js';
|
|
2
2
|
import { isParserParsingFailedError, ParserParsingFailedError } from './parserError.js';
|
|
3
3
|
import { parserImplementationInvariant } from './parserImplementationInvariant.js';
|
|
4
4
|
import { promiseSettled } from './promiseSettled.js';
|
|
5
|
-
import { type DeriveSequenceElement } from './sequence.js';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
// Union all output types from an array of parsers
|
|
7
|
+
type DisjunctionParserOutput<Parsers extends readonly unknown[]> = ParserOutput<Parsers[number]>;
|
|
8
|
+
|
|
9
|
+
// Infer Sequence from parser array
|
|
10
|
+
type InferSequenceFromParserArray<T extends readonly unknown[]> = ParserSequence<T[number]>;
|
|
11
|
+
|
|
12
|
+
export function createDisjunctionParser<
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
const Parsers extends readonly Parser<any, any, any>[],
|
|
11
15
|
>(
|
|
12
|
-
childParsers:
|
|
13
|
-
): Parser<
|
|
16
|
+
childParsers: Parsers,
|
|
17
|
+
): Parser<DisjunctionParserOutput<Parsers>, InferSequenceFromParserArray<Parsers>> {
|
|
14
18
|
parserImplementationInvariant(childParsers.length > 0, 'Disjunction parser must have at least one child parser.');
|
|
15
19
|
|
|
16
|
-
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
const disjunctionParser: Parser<any, any, any> = async parserContext => {
|
|
17
22
|
const parserParsingFailedErrors: ParserParsingFailedError[] = [];
|
|
18
23
|
|
|
19
24
|
for (const childParser of childParsers) {
|
|
20
|
-
|
|
25
|
+
using childParserContext = parserContext.lookahead({
|
|
21
26
|
debugName: getParserName(childParser, 'anonymousDisjunctionChild'),
|
|
22
27
|
});
|
|
23
28
|
|
|
24
|
-
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
const childParserResult = await promiseSettled<any>(childParser(childParserContext) as Promise<any>);
|
|
25
31
|
|
|
26
32
|
if (childParserResult.status === 'fulfilled') {
|
|
27
33
|
const successfulParserOutput = childParserResult.value;
|
|
28
34
|
|
|
29
35
|
childParserContext.unlookahead();
|
|
30
|
-
childParserContext.dispose();
|
|
31
36
|
|
|
32
37
|
return successfulParserOutput;
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
childParserContext.dispose();
|
|
36
|
-
|
|
37
40
|
const error = childParserResult.reason;
|
|
38
41
|
|
|
39
42
|
if (isParserParsingFailedError(error)) {
|
|
@@ -57,4 +60,4 @@ export const createDisjunctionParser = <
|
|
|
57
60
|
].join('');
|
|
58
61
|
|
|
59
62
|
return setParserName(disjunctionParser, name);
|
|
60
|
-
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { expectAssignable, expectType } from 'tsd';
|
|
2
|
+
import { createElementSwitchParser } from './elementSwitchParser.js';
|
|
3
|
+
import { type Parser, type ParserOutput } from './parser.js';
|
|
4
|
+
|
|
5
|
+
// Test: basic element switch with inferred output type
|
|
6
|
+
{
|
|
7
|
+
const parserA: Parser<'a', string> = async () => 'a' as const;
|
|
8
|
+
const parserB: Parser<'b', string> = async () => 'b' as const;
|
|
9
|
+
|
|
10
|
+
const parser = createElementSwitchParser(
|
|
11
|
+
new Map<string, typeof parserA | typeof parserB>([
|
|
12
|
+
['x', parserA],
|
|
13
|
+
['y', parserB],
|
|
14
|
+
]),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
type Output = ParserOutput<typeof parser>;
|
|
18
|
+
|
|
19
|
+
// Output should be 'a' | 'b'
|
|
20
|
+
expectAssignable<'a' | 'b'>(null! as Output);
|
|
21
|
+
expectAssignable<Output>(null! as 'a' | 'b');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Test: element switch with default parser
|
|
25
|
+
{
|
|
26
|
+
const parserA: Parser<'a', string> = async () => 'a' as const;
|
|
27
|
+
const defaultParser: Parser<'default', string> = async () => 'default' as const;
|
|
28
|
+
|
|
29
|
+
const parser = createElementSwitchParser(
|
|
30
|
+
new Map([
|
|
31
|
+
['x', parserA],
|
|
32
|
+
]),
|
|
33
|
+
defaultParser,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
type Output = ParserOutput<typeof parser>;
|
|
37
|
+
|
|
38
|
+
// Output should be 'a' | 'default'
|
|
39
|
+
expectAssignable<'a' | 'default'>(null! as Output);
|
|
40
|
+
expectAssignable<Output>(null! as 'a' | 'default');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Test: element switch with number keys (Uint8Array sequence)
|
|
44
|
+
{
|
|
45
|
+
const parser1: Parser<{ type: 'one' }, Uint8Array> = async () => ({ type: 'one' });
|
|
46
|
+
const parser2: Parser<{ type: 'two' }, Uint8Array> = async () => ({ type: 'two' });
|
|
47
|
+
|
|
48
|
+
const parser = createElementSwitchParser(
|
|
49
|
+
new Map<number, typeof parser1 | typeof parser2>([
|
|
50
|
+
[1, parser1],
|
|
51
|
+
[2, parser2],
|
|
52
|
+
]),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
type Output = ParserOutput<typeof parser>;
|
|
56
|
+
|
|
57
|
+
expectAssignable<{ type: 'one' } | { type: 'two' }>(null! as Output);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Test: element switch without default parser has no 'never' in output
|
|
61
|
+
{
|
|
62
|
+
const parserA: Parser<string, string> = async () => 'result';
|
|
63
|
+
|
|
64
|
+
const parser = createElementSwitchParser(
|
|
65
|
+
new Map([
|
|
66
|
+
['key', parserA],
|
|
67
|
+
]),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
type Output = ParserOutput<typeof parser>;
|
|
71
|
+
|
|
72
|
+
// Output should just be string, not string | never
|
|
73
|
+
expectType<string>(null! as Output);
|
|
74
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import invariant from "invariant";
|
|
2
|
+
import { getParserName, Parser, ParserOutput, ParserSequence, setParserName } from "./parser.js";
|
|
3
|
+
import { parserImplementationInvariant } from "./parserImplementationInvariant.js";
|
|
4
|
+
|
|
5
|
+
// Output type: union of child parser outputs and default parser output (if present)
|
|
6
|
+
type ElementSwitchOutput<
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
ChildParser extends Parser<any, any, any>,
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
DefaultParser extends Parser<any, any, any> | undefined,
|
|
11
|
+
> = ParserOutput<ChildParser> | (DefaultParser extends undefined ? never : ParserOutput<NonNullable<DefaultParser>>);
|
|
12
|
+
|
|
13
|
+
export function createElementSwitchParser<
|
|
14
|
+
Element,
|
|
15
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
16
|
+
ChildParser extends Parser<any, any, Element>,
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
DefaultParser extends Parser<any, any, Element> | undefined = undefined,
|
|
19
|
+
>(
|
|
20
|
+
childParsers: Map<Element, ChildParser>,
|
|
21
|
+
defaultParser?: DefaultParser,
|
|
22
|
+
): Parser<ElementSwitchOutput<ChildParser, DefaultParser>, ParserSequence<ChildParser>, Element> {
|
|
23
|
+
parserImplementationInvariant(childParsers.size > 0, 'Element switch parser must have at least one child parser.');
|
|
24
|
+
|
|
25
|
+
type Output = ElementSwitchOutput<ChildParser, DefaultParser>;
|
|
26
|
+
type Sequence = ParserSequence<ChildParser>;
|
|
27
|
+
|
|
28
|
+
const elementSwitchParser: Parser<Output, Sequence, Element> = async parserContext => {
|
|
29
|
+
const currentElement = await parserContext.peek(0);
|
|
30
|
+
|
|
31
|
+
parserContext.invariant(currentElement !== undefined, 'Unexpected end of input.');
|
|
32
|
+
invariant(currentElement !== undefined, 'Unexpected end of input.');
|
|
33
|
+
|
|
34
|
+
const childParser = childParsers.get(currentElement) ?? defaultParser;
|
|
35
|
+
|
|
36
|
+
parserContext.invariant(childParser, `No child parser found for element: ${String(currentElement)}`);
|
|
37
|
+
|
|
38
|
+
return childParser!(parserContext) as Promise<Output>;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const name = [
|
|
42
|
+
'elementSwitch(',
|
|
43
|
+
...Array.from(childParsers.entries()).map(
|
|
44
|
+
([ element, childParser ]) => `${String(element)}=>${getParserName(childParser, 'anonymousElementSwitchChild')}`,
|
|
45
|
+
),
|
|
46
|
+
defaultParser ? `|default=>${getParserName(defaultParser, 'anonymousElementSwitchDefaultChild')}` : '',
|
|
47
|
+
')',
|
|
48
|
+
].join('');
|
|
49
|
+
|
|
50
|
+
return setParserName(elementSwitchParser, name);
|
|
51
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { expectType, expectAssignable } from 'tsd';
|
|
2
|
+
import { createExactSequenceParser, createExactSequenceNaiveParser } from './exactSequenceParser.js';
|
|
3
|
+
import { type Parser } from './parser.js';
|
|
4
|
+
|
|
5
|
+
// Test: string literal output type is preserved, sequence type is string
|
|
6
|
+
{
|
|
7
|
+
const parser = createExactSequenceParser('foo');
|
|
8
|
+
expectType<Parser<'foo', string, unknown>>(parser);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Test: string literal with 'as const' also works
|
|
12
|
+
{
|
|
13
|
+
const parser = createExactSequenceParser('bar' as const);
|
|
14
|
+
expectType<Parser<'bar', string, unknown>>(parser);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Test: plain string variable widens to string
|
|
18
|
+
{
|
|
19
|
+
const value: string = 'baz';
|
|
20
|
+
const parser = createExactSequenceParser(value);
|
|
21
|
+
expectType<Parser<string, string, unknown>>(parser);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Test: Uint8Array sequence type is preserved
|
|
25
|
+
{
|
|
26
|
+
const bytes = new Uint8Array([1, 2, 3]);
|
|
27
|
+
const parser = createExactSequenceParser(bytes);
|
|
28
|
+
// Output is the specific Uint8Array instance type, Sequence is Uint8Array
|
|
29
|
+
expectAssignable<Parser<Uint8Array, Uint8Array, unknown>>(parser);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Test: naive parser has same type behavior
|
|
33
|
+
{
|
|
34
|
+
const parser = createExactSequenceNaiveParser('hello');
|
|
35
|
+
expectType<Parser<'hello', string, unknown>>(parser);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Test: naive parser with Uint8Array
|
|
39
|
+
{
|
|
40
|
+
const bytes = new Uint8Array([0xff]);
|
|
41
|
+
const parser = createExactSequenceNaiveParser(bytes);
|
|
42
|
+
expectAssignable<Parser<Uint8Array, Uint8Array, unknown>>(parser);
|
|
43
|
+
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { setParserName, type Parser } from './parser.js';
|
|
2
2
|
import { inspect } from './inspect.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
// Derive the base sequence type from a literal (e.g., 'foo' -> string, Uint8Array -> Uint8Array)
|
|
5
|
+
type DeriveBaseSequence<T> = T extends string ? string : T extends Uint8Array ? Uint8Array : T;
|
|
6
|
+
|
|
7
|
+
export const createExactSequenceNaiveParser = <const Output>(sequence: Output) => {
|
|
8
|
+
type Sequence = DeriveBaseSequence<Output>;
|
|
9
|
+
const exactSequenceParser: Parser<Output, Sequence, unknown> = async parserContext => {
|
|
10
|
+
const length = parserContext.length(sequence as Sequence);
|
|
7
11
|
|
|
8
12
|
for (let index = 0; index < length; index++) {
|
|
9
13
|
const element = await parserContext.read(0);
|
|
10
|
-
const expectedElement = parserContext.at(sequence, index);
|
|
14
|
+
const expectedElement = parserContext.at(sequence as Sequence, index);
|
|
11
15
|
|
|
12
16
|
parserContext.invariant(
|
|
13
17
|
element === expectedElement,
|
|
@@ -26,14 +30,15 @@ export const createExactSequenceNaiveParser = <Sequence>(sequence: Sequence) =>
|
|
|
26
30
|
return exactSequenceParser;
|
|
27
31
|
};
|
|
28
32
|
|
|
29
|
-
export const createExactSequenceParser = <
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
export const createExactSequenceParser = <const Output>(expectedSequence: Output) => {
|
|
34
|
+
type Sequence = DeriveBaseSequence<Output>;
|
|
35
|
+
const exactSequenceParser: Parser<Output, Sequence, unknown> = async parserContext => {
|
|
36
|
+
const length = parserContext.length(expectedSequence as Sequence);
|
|
32
37
|
|
|
33
38
|
const actualSequence = await parserContext.readSequence(0, length);
|
|
34
39
|
|
|
35
40
|
parserContext.invariant(
|
|
36
|
-
parserContext.equals(actualSequence, expectedSequence),
|
|
41
|
+
parserContext.equals(actualSequence, expectedSequence as Sequence),
|
|
37
42
|
'Expected "%s", got "%s"',
|
|
38
43
|
() => inspect(expectedSequence),
|
|
39
44
|
() => inspect(actualSequence),
|
package/src/fetchCid.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import fsPromises from 'node:fs/promises';
|
|
2
|
-
import
|
|
3
|
-
import pMemoize from 'p-memoize';
|
|
4
|
-
import envPaths from 'env-paths';
|
|
5
|
-
|
|
6
|
-
const paths = envPaths('parser.futpib.github.io');
|
|
2
|
+
import { fetchCid as fetchCidFromIpfs } from '@futpib/fetch-cid';
|
|
7
3
|
|
|
8
4
|
function readableWebStreamOnFinish<T>(readableWebStream: ReadableStream<T>, onFinish: () => void): ReadableStream<T> {
|
|
9
5
|
const reader = readableWebStream.getReader();
|
|
@@ -34,74 +30,6 @@ function readableWebStreamOnFinish<T>(readableWebStream: ReadableStream<T>, onFi
|
|
|
34
30
|
return stream;
|
|
35
31
|
}
|
|
36
32
|
|
|
37
|
-
class FsCache {
|
|
38
|
-
private get _basePath() {
|
|
39
|
-
return path.join(paths.cache, 'fetchCid');
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
private _getKeyPath(key: string) {
|
|
43
|
-
return path.join(this._basePath, key.replaceAll('/', '_'));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
async get(key: string): Promise<[ ReadableStream<Uint8Array>, ReadableStream<Uint8Array> ] | undefined> {
|
|
47
|
-
try {
|
|
48
|
-
const file = await fsPromises.open(this._getKeyPath(key), 'r');
|
|
49
|
-
|
|
50
|
-
const stream = file.readableWebStream() as ReadableStream<Uint8Array>;
|
|
51
|
-
|
|
52
|
-
const streamWithClose = readableWebStreamOnFinish(stream, () => {
|
|
53
|
-
file.close();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
return [ streamWithClose, undefined as unknown as ReadableStream<Uint8Array> ];
|
|
57
|
-
} catch (error) {
|
|
58
|
-
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
59
|
-
return undefined;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
throw error;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async has(key: string) {
|
|
67
|
-
const streams = await this.get(key);
|
|
68
|
-
try {
|
|
69
|
-
return streams !== undefined;
|
|
70
|
-
} finally {
|
|
71
|
-
for (const stream of streams ?? []) {
|
|
72
|
-
await stream?.cancel();
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async set(key: string, [ _, value ]: [ ReadableStream<Uint8Array>, ReadableStream<Uint8Array> ]) {
|
|
78
|
-
await fsPromises.mkdir(this._basePath, {
|
|
79
|
-
recursive: true,
|
|
80
|
-
});
|
|
81
|
-
const file = await fsPromises.open(this._getKeyPath(key), 'w');
|
|
82
|
-
try {
|
|
83
|
-
for await (const chunk of value) {
|
|
84
|
-
await file.write(chunk);
|
|
85
|
-
}
|
|
86
|
-
} finally {
|
|
87
|
-
await file.close();
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async delete(key: string) {
|
|
92
|
-
await fsPromises.unlink(this._getKeyPath(key));
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function reallyFetchCid(cid: string): Promise<[ ReadableStream<Uint8Array>, ReadableStream<Uint8Array> ]> {
|
|
97
|
-
const response = await fetch('https://ipfs.io/ipfs/' + cid);
|
|
98
|
-
return response.body!.tee();
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const cachedReallyFetchCid = pMemoize(reallyFetchCid, {
|
|
102
|
-
cache: new FsCache(),
|
|
103
|
-
});
|
|
104
|
-
|
|
105
33
|
export async function fetchCid(cidOrPath: string): Promise<AsyncIterable<Uint8Array>> {
|
|
106
34
|
if (cidOrPath.includes('/')) {
|
|
107
35
|
const file = await fsPromises.open(cidOrPath, 'r');
|
|
@@ -115,7 +43,5 @@ export async function fetchCid(cidOrPath: string): Promise<AsyncIterable<Uint8Ar
|
|
|
115
43
|
return streamWithClose;
|
|
116
44
|
}
|
|
117
45
|
|
|
118
|
-
|
|
119
|
-
await unused?.cancel();
|
|
120
|
-
return readable;
|
|
46
|
+
return fetchCidFromIpfs(cidOrPath);
|
|
121
47
|
}
|