frida-java-bridge
Version:
Java runtime interop from Frida
934 lines (761 loc) • 26.6 kB
JavaScript
import { Buffer } from 'buffer';
const kAccPublic = 0x0001;
const kAccNative = 0x0100;
const kAccConstructor = 0x00010000;
const kEndianTag = 0x12345678;
const kClassDefSize = 32;
const kProtoIdSize = 12;
const kFieldIdSize = 8;
const kMethodIdSize = 8;
const kTypeIdSize = 4;
const kStringIdSize = 4;
const kMapItemSize = 12;
const TYPE_HEADER_ITEM = 0;
const TYPE_STRING_ID_ITEM = 1;
const TYPE_TYPE_ID_ITEM = 2;
const TYPE_PROTO_ID_ITEM = 3;
const TYPE_FIELD_ID_ITEM = 4;
const TYPE_METHOD_ID_ITEM = 5;
const TYPE_CLASS_DEF_ITEM = 6;
const TYPE_MAP_LIST = 0x1000;
const TYPE_TYPE_LIST = 0x1001;
const TYPE_ANNOTATION_SET_ITEM = 0x1003;
const TYPE_CLASS_DATA_ITEM = 0x2000;
const TYPE_CODE_ITEM = 0x2001;
const TYPE_STRING_DATA_ITEM = 0x2002;
const TYPE_DEBUG_INFO_ITEM = 0x2003;
const TYPE_ANNOTATION_ITEM = 0x2004;
const TYPE_ANNOTATIONS_DIRECTORY_ITEM = 0x2006;
const VALUE_TYPE = 0x18;
const VALUE_ARRAY = 0x1c;
const VISIBILITY_SYSTEM = 2;
const kDefaultConstructorSize = 24;
const kDefaultConstructorDebugInfo = Buffer.from([0x03, 0x00, 0x07, 0x0e, 0x00]);
const kDalvikAnnotationTypeThrows = 'Ldalvik/annotation/Throws;';
const kNullTerminator = Buffer.from([0]);
function mkdex (spec) {
const builder = new DexBuilder();
const fullSpec = Object.assign({}, spec);
builder.addClass(fullSpec);
return builder.build();
}
class DexBuilder {
constructor () {
this.classes = [];
}
addClass (spec) {
this.classes.push(spec);
}
build () {
const model = computeModel(this.classes);
const {
classes,
interfaces,
fields,
methods,
protos,
parameters,
annotationDirectories,
annotationSets,
throwsAnnotations,
types,
strings
} = model;
let offset = 0;
const headerOffset = 0;
const checksumOffset = 8;
const signatureOffset = 12;
const signatureSize = 20;
const headerSize = 0x70;
offset += headerSize;
const stringIdsOffset = offset;
const stringIdsSize = strings.length * kStringIdSize;
offset += stringIdsSize;
const typeIdsOffset = offset;
const typeIdsSize = types.length * kTypeIdSize;
offset += typeIdsSize;
const protoIdsOffset = offset;
const protoIdsSize = protos.length * kProtoIdSize;
offset += protoIdsSize;
const fieldIdsOffset = offset;
const fieldIdsSize = fields.length * kFieldIdSize;
offset += fieldIdsSize;
const methodIdsOffset = offset;
const methodIdsSize = methods.length * kMethodIdSize;
offset += methodIdsSize;
const classDefsOffset = offset;
const classDefsSize = classes.length * kClassDefSize;
offset += classDefsSize;
const dataOffset = offset;
const annotationSetOffsets = annotationSets.map(set => {
const setOffset = offset;
set.offset = setOffset;
offset += 4 + (set.items.length * 4);
return setOffset;
});
const javaCodeItems = classes.reduce((result, klass) => {
const constructorMethods = klass.classData.constructorMethods;
constructorMethods.forEach(method => {
const [, accessFlags, superConstructor] = method;
if ((accessFlags & kAccNative) === 0 && superConstructor >= 0) {
method.push(offset);
result.push({ offset, superConstructor });
offset += kDefaultConstructorSize;
}
});
return result;
}, []);
annotationDirectories.forEach(dir => {
dir.offset = offset;
offset += 16 + (dir.methods.length * 8);
});
const interfaceOffsets = interfaces.map(iface => {
offset = align(offset, 4);
const ifaceOffset = offset;
iface.offset = ifaceOffset;
offset += 4 + (2 * iface.types.length);
return ifaceOffset;
});
const parameterOffsets = parameters.map(param => {
offset = align(offset, 4);
const paramOffset = offset;
param.offset = paramOffset;
offset += 4 + (2 * param.types.length);
return paramOffset;
});
const stringChunks = [];
const stringOffsets = strings.map(str => {
const strOffset = offset;
const header = Buffer.from(createUleb128(str.length));
const data = Buffer.from(str, 'utf8');
const chunk = Buffer.concat([header, data, kNullTerminator]);
stringChunks.push(chunk);
offset += chunk.length;
return strOffset;
});
const debugInfoOffsets = javaCodeItems.map(codeItem => {
const debugOffset = offset;
offset += kDefaultConstructorDebugInfo.length;
return debugOffset;
});
const throwsAnnotationBlobs = throwsAnnotations.map(annotation => {
const blob = makeThrowsAnnotation(annotation);
annotation.offset = offset;
offset += blob.length;
return blob;
});
const classDataBlobs = classes.map((klass, index) => {
klass.classData.offset = offset;
const blob = makeClassData(klass);
offset += blob.length;
return blob;
});
const linkSize = 0;
const linkOffset = 0;
offset = align(offset, 4);
const mapOffset = offset;
const typeListLength = interfaces.length + parameters.length;
const mapNumItems = 4 + ((fields.length > 0) ? 1 : 0) + 2 + annotationSets.length + javaCodeItems.length + annotationDirectories.length +
((typeListLength > 0) ? 1 : 0) + 1 + debugInfoOffsets.length + throwsAnnotations.length + classes.length + 1;
const mapSize = 4 + (mapNumItems * kMapItemSize);
offset += mapSize;
const dataSize = offset - dataOffset;
const fileSize = offset;
const dex = Buffer.alloc(fileSize);
dex.write('dex\n035');
dex.writeUInt32LE(fileSize, 0x20);
dex.writeUInt32LE(headerSize, 0x24);
dex.writeUInt32LE(kEndianTag, 0x28);
dex.writeUInt32LE(linkSize, 0x2c);
dex.writeUInt32LE(linkOffset, 0x30);
dex.writeUInt32LE(mapOffset, 0x34);
dex.writeUInt32LE(strings.length, 0x38);
dex.writeUInt32LE(stringIdsOffset, 0x3c);
dex.writeUInt32LE(types.length, 0x40);
dex.writeUInt32LE(typeIdsOffset, 0x44);
dex.writeUInt32LE(protos.length, 0x48);
dex.writeUInt32LE(protoIdsOffset, 0x4c);
dex.writeUInt32LE(fields.length, 0x50);
dex.writeUInt32LE(fields.length > 0 ? fieldIdsOffset : 0, 0x54);
dex.writeUInt32LE(methods.length, 0x58);
dex.writeUInt32LE(methodIdsOffset, 0x5c);
dex.writeUInt32LE(classes.length, 0x60);
dex.writeUInt32LE(classDefsOffset, 0x64);
dex.writeUInt32LE(dataSize, 0x68);
dex.writeUInt32LE(dataOffset, 0x6c);
stringOffsets.forEach((offset, index) => {
dex.writeUInt32LE(offset, stringIdsOffset + (index * kStringIdSize));
});
types.forEach((id, index) => {
dex.writeUInt32LE(id, typeIdsOffset + (index * kTypeIdSize));
});
protos.forEach((proto, index) => {
const [shortyIndex, returnTypeIndex, params] = proto;
const protoOffset = protoIdsOffset + (index * kProtoIdSize);
dex.writeUInt32LE(shortyIndex, protoOffset);
dex.writeUInt32LE(returnTypeIndex, protoOffset + 4);
dex.writeUInt32LE((params !== null) ? params.offset : 0, protoOffset + 8);
});
fields.forEach((field, index) => {
const [classIndex, typeIndex, nameIndex] = field;
const fieldOffset = fieldIdsOffset + (index * kFieldIdSize);
dex.writeUInt16LE(classIndex, fieldOffset);
dex.writeUInt16LE(typeIndex, fieldOffset + 2);
dex.writeUInt32LE(nameIndex, fieldOffset + 4);
});
methods.forEach((method, index) => {
const [classIndex, protoIndex, nameIndex] = method;
const methodOffset = methodIdsOffset + (index * kMethodIdSize);
dex.writeUInt16LE(classIndex, methodOffset);
dex.writeUInt16LE(protoIndex, methodOffset + 2);
dex.writeUInt32LE(nameIndex, methodOffset + 4);
});
classes.forEach((klass, index) => {
const { interfaces, annotationsDirectory } = klass;
const interfacesOffset = (interfaces !== null) ? interfaces.offset : 0;
const annotationsOffset = (annotationsDirectory !== null) ? annotationsDirectory.offset : 0;
const staticValuesOffset = 0;
const classOffset = classDefsOffset + (index * kClassDefSize);
dex.writeUInt32LE(klass.index, classOffset);
dex.writeUInt32LE(klass.accessFlags, classOffset + 4);
dex.writeUInt32LE(klass.superClassIndex, classOffset + 8);
dex.writeUInt32LE(interfacesOffset, classOffset + 12);
dex.writeUInt32LE(klass.sourceFileIndex, classOffset + 16);
dex.writeUInt32LE(annotationsOffset, classOffset + 20);
dex.writeUInt32LE(klass.classData.offset, classOffset + 24);
dex.writeUInt32LE(staticValuesOffset, classOffset + 28);
});
annotationSets.forEach((set, index) => {
const { items } = set;
const setOffset = annotationSetOffsets[index];
dex.writeUInt32LE(items.length, setOffset);
items.forEach((item, index) => {
dex.writeUInt32LE(item.offset, setOffset + 4 + (index * 4));
});
});
javaCodeItems.forEach((codeItem, index) => {
const { offset, superConstructor } = codeItem;
const registersSize = 1;
const insSize = 1;
const outsSize = 1;
const triesSize = 0;
const insnsSize = 4;
dex.writeUInt16LE(registersSize, offset);
dex.writeUInt16LE(insSize, offset + 2);
dex.writeUInt16LE(outsSize, offset + 4);
dex.writeUInt16LE(triesSize, offset + 6);
dex.writeUInt32LE(debugInfoOffsets[index], offset + 8);
dex.writeUInt32LE(insnsSize, offset + 12);
dex.writeUInt16LE(0x1070, offset + 16);
dex.writeUInt16LE(superConstructor, offset + 18);
dex.writeUInt16LE(0x0000, offset + 20);
dex.writeUInt16LE(0x000e, offset + 22);
});
annotationDirectories.forEach(dir => {
const dirOffset = dir.offset;
const classAnnotationsOffset = 0;
const fieldsSize = 0;
const annotatedMethodsSize = dir.methods.length;
const annotatedParametersSize = 0;
dex.writeUInt32LE(classAnnotationsOffset, dirOffset);
dex.writeUInt32LE(fieldsSize, dirOffset + 4);
dex.writeUInt32LE(annotatedMethodsSize, dirOffset + 8);
dex.writeUInt32LE(annotatedParametersSize, dirOffset + 12);
dir.methods.forEach((method, index) => {
const entryOffset = dirOffset + 16 + (index * 8);
const [methodIndex, annotationSet] = method;
dex.writeUInt32LE(methodIndex, entryOffset);
dex.writeUInt32LE(annotationSet.offset, entryOffset + 4);
});
});
interfaces.forEach((iface, index) => {
const ifaceOffset = interfaceOffsets[index];
dex.writeUInt32LE(iface.types.length, ifaceOffset);
iface.types.forEach((type, typeIndex) => {
dex.writeUInt16LE(type, ifaceOffset + 4 + (typeIndex * 2));
});
});
parameters.forEach((param, index) => {
const paramOffset = parameterOffsets[index];
dex.writeUInt32LE(param.types.length, paramOffset);
param.types.forEach((type, typeIndex) => {
dex.writeUInt16LE(type, paramOffset + 4 + (typeIndex * 2));
});
});
stringChunks.forEach((chunk, index) => {
chunk.copy(dex, stringOffsets[index]);
});
debugInfoOffsets.forEach(debugInfoOffset => {
kDefaultConstructorDebugInfo.copy(dex, debugInfoOffset);
});
throwsAnnotationBlobs.forEach((annotationBlob, index) => {
annotationBlob.copy(dex, throwsAnnotations[index].offset);
});
classDataBlobs.forEach((classDataBlob, index) => {
classDataBlob.copy(dex, classes[index].classData.offset);
});
dex.writeUInt32LE(mapNumItems, mapOffset);
const mapItems = [
[TYPE_HEADER_ITEM, 1, headerOffset],
[TYPE_STRING_ID_ITEM, strings.length, stringIdsOffset],
[TYPE_TYPE_ID_ITEM, types.length, typeIdsOffset],
[TYPE_PROTO_ID_ITEM, protos.length, protoIdsOffset]
];
if (fields.length > 0) {
mapItems.push([TYPE_FIELD_ID_ITEM, fields.length, fieldIdsOffset]);
}
mapItems.push([TYPE_METHOD_ID_ITEM, methods.length, methodIdsOffset]);
mapItems.push([TYPE_CLASS_DEF_ITEM, classes.length, classDefsOffset]);
annotationSets.forEach((set, index) => {
mapItems.push([TYPE_ANNOTATION_SET_ITEM, set.items.length, annotationSetOffsets[index]]);
});
javaCodeItems.forEach(codeItem => {
mapItems.push([TYPE_CODE_ITEM, 1, codeItem.offset]);
});
annotationDirectories.forEach(dir => {
mapItems.push([TYPE_ANNOTATIONS_DIRECTORY_ITEM, 1, dir.offset]);
});
if (typeListLength > 0) {
mapItems.push([TYPE_TYPE_LIST, typeListLength, interfaceOffsets.concat(parameterOffsets)[0]]);
}
mapItems.push([TYPE_STRING_DATA_ITEM, strings.length, stringOffsets[0]]);
debugInfoOffsets.forEach(debugInfoOffset => {
mapItems.push([TYPE_DEBUG_INFO_ITEM, 1, debugInfoOffset]);
});
throwsAnnotations.forEach(annotation => {
mapItems.push([TYPE_ANNOTATION_ITEM, 1, annotation.offset]);
});
classes.forEach(klass => {
mapItems.push([TYPE_CLASS_DATA_ITEM, 1, klass.classData.offset]);
});
mapItems.push([TYPE_MAP_LIST, 1, mapOffset]);
mapItems.forEach((item, index) => {
const [type, size, offset] = item;
const itemOffset = mapOffset + 4 + (index * kMapItemSize);
dex.writeUInt16LE(type, itemOffset);
dex.writeUInt32LE(size, itemOffset + 4);
dex.writeUInt32LE(offset, itemOffset + 8);
});
const hash = new Checksum('sha1');
hash.update(dex.slice(signatureOffset + signatureSize));
Buffer.from(hash.getDigest()).copy(dex, signatureOffset);
dex.writeUInt32LE(adler32(dex, signatureOffset), checksumOffset);
return dex;
}
}
function makeClassData (klass) {
const { instanceFields, constructorMethods, virtualMethods } = klass.classData;
const staticFieldsSize = 0;
return Buffer.from([
staticFieldsSize
]
.concat(createUleb128(instanceFields.length))
.concat(createUleb128(constructorMethods.length))
.concat(createUleb128(virtualMethods.length))
.concat(instanceFields.reduce((result, [indexDiff, accessFlags]) => {
return result
.concat(createUleb128(indexDiff))
.concat(createUleb128(accessFlags));
}, []))
.concat(constructorMethods.reduce((result, [indexDiff, accessFlags, , codeOffset]) => {
return result
.concat(createUleb128(indexDiff))
.concat(createUleb128(accessFlags))
.concat(createUleb128(codeOffset || 0));
}, []))
.concat(virtualMethods.reduce((result, [indexDiff, accessFlags]) => {
const codeOffset = 0;
return result
.concat(createUleb128(indexDiff))
.concat(createUleb128(accessFlags))
.concat([codeOffset]);
}, [])));
}
function makeThrowsAnnotation (annotation) {
const { thrownTypes } = annotation;
return Buffer.from([
VISIBILITY_SYSTEM
]
.concat(createUleb128(annotation.type))
.concat([1])
.concat(createUleb128(annotation.value))
.concat([VALUE_ARRAY, thrownTypes.length])
.concat(thrownTypes.reduce((result, type) => {
result.push(VALUE_TYPE, type);
return result;
}, []))
);
}
function computeModel (classes) {
const strings = new Set();
const types = new Set();
const protos = {};
const fields = [];
const methods = [];
const throwsAnnotations = {};
const javaConstructors = new Set();
const superConstructors = new Set();
classes.forEach(klass => {
const { name, superClass, sourceFileName } = klass;
strings.add('this');
strings.add(name);
types.add(name);
strings.add(superClass);
types.add(superClass);
strings.add(sourceFileName);
klass.interfaces.forEach(iface => {
strings.add(iface);
types.add(iface);
});
klass.fields.forEach(field => {
const [fieldName, fieldType] = field;
strings.add(fieldName);
strings.add(fieldType);
types.add(fieldType);
fields.push([klass.name, fieldType, fieldName]);
});
if (!klass.methods.some(([methodName]) => methodName === '<init>')) {
klass.methods.unshift(['<init>', 'V', []]);
javaConstructors.add(name);
}
klass.methods.forEach(method => {
const [methodName, retType, argTypes, thrownTypes = [], accessFlags] = method;
strings.add(methodName);
const protoId = addProto(retType, argTypes);
let throwsAnnotationId = null;
if (thrownTypes.length > 0) {
const typesNormalized = thrownTypes.slice();
typesNormalized.sort();
throwsAnnotationId = typesNormalized.join('|');
let throwsAnnotation = throwsAnnotations[throwsAnnotationId];
if (throwsAnnotation === undefined) {
throwsAnnotation = {
id: throwsAnnotationId,
types: typesNormalized
};
throwsAnnotations[throwsAnnotationId] = throwsAnnotation;
}
strings.add(kDalvikAnnotationTypeThrows);
types.add(kDalvikAnnotationTypeThrows);
thrownTypes.forEach(type => {
strings.add(type);
types.add(type);
});
strings.add('value');
}
methods.push([klass.name, protoId, methodName, throwsAnnotationId, accessFlags]);
if (methodName === '<init>') {
superConstructors.add(name + '|' + protoId);
const superConstructorId = superClass + '|' + protoId;
if (javaConstructors.has(name) && !superConstructors.has(superConstructorId)) {
methods.push([superClass, protoId, methodName, null, 0]);
superConstructors.add(superConstructorId);
}
}
});
});
function addProto (retType, argTypes) {
const signature = [retType].concat(argTypes);
const id = signature.join('|');
if (protos[id] !== undefined) {
return id;
}
strings.add(retType);
types.add(retType);
argTypes.forEach(argType => {
strings.add(argType);
types.add(argType);
});
const shorty = signature.map(typeToShorty).join('');
strings.add(shorty);
protos[id] = [id, shorty, retType, argTypes];
return id;
}
const stringItems = Array.from(strings);
stringItems.sort();
const stringToIndex = stringItems.reduce((result, string, index) => {
result[string] = index;
return result;
}, {});
const typeItems = Array.from(types).map(name => stringToIndex[name]);
typeItems.sort(compareNumbers);
const typeToIndex = typeItems.reduce((result, stringIndex, typeIndex) => {
result[stringItems[stringIndex]] = typeIndex;
return result;
}, {});
const literalProtoItems = Object.keys(protos).map(id => protos[id]);
literalProtoItems.sort(compareProtoItems);
const parameters = {};
const protoItems = literalProtoItems.map(item => {
const [, shorty, retType, argTypes] = item;
let params;
if (argTypes.length > 0) {
const argTypesSig = argTypes.join('|');
params = parameters[argTypesSig];
if (params === undefined) {
params = {
types: argTypes.map(type => typeToIndex[type]),
offset: -1
};
parameters[argTypesSig] = params;
}
} else {
params = null;
}
return [
stringToIndex[shorty],
typeToIndex[retType],
params
];
});
const protoToIndex = literalProtoItems.reduce((result, item, index) => {
const [id] = item;
result[id] = index;
return result;
}, {});
const parameterItems = Object.keys(parameters).map(id => parameters[id]);
const fieldItems = fields.map(field => {
const [klass, fieldType, fieldName] = field;
return [
typeToIndex[klass],
typeToIndex[fieldType],
stringToIndex[fieldName]
];
});
fieldItems.sort(compareFieldItems);
const methodItems = methods.map(method => {
const [klass, protoId, name, annotationsId, accessFlags] = method;
return [
typeToIndex[klass],
protoToIndex[protoId],
stringToIndex[name],
annotationsId,
accessFlags
];
});
methodItems.sort(compareMethodItems);
const throwsAnnotationItems = Object.keys(throwsAnnotations)
.map(id => throwsAnnotations[id])
.map(item => {
return {
id: item.id,
type: typeToIndex[kDalvikAnnotationTypeThrows],
value: stringToIndex.value,
thrownTypes: item.types.map(type => typeToIndex[type]),
offset: -1
};
});
const annotationSetItems = throwsAnnotationItems.map(item => {
return {
id: item.id,
items: [item],
offset: -1
};
});
const annotationSetIdToIndex = annotationSetItems.reduce((result, item, index) => {
result[item.id] = index;
return result;
}, {});
const interfaceLists = {};
const annotationDirectories = [];
const classItems = classes.map(klass => {
const classIndex = typeToIndex[klass.name];
const accessFlags = kAccPublic;
const superClassIndex = typeToIndex[klass.superClass];
let ifaceList;
const ifaces = klass.interfaces.map(type => typeToIndex[type]);
if (ifaces.length > 0) {
ifaces.sort(compareNumbers);
const ifacesId = ifaces.join('|');
ifaceList = interfaceLists[ifacesId];
if (ifaceList === undefined) {
ifaceList = {
types: ifaces,
offset: -1
};
interfaceLists[ifacesId] = ifaceList;
}
} else {
ifaceList = null;
}
const sourceFileIndex = stringToIndex[klass.sourceFileName];
const classMethods = methodItems.reduce((result, method, index) => {
const [holder, protoIndex, name, annotationsId, accessFlags] = method;
if (holder === classIndex) {
result.push([index, name, annotationsId, protoIndex, accessFlags]);
}
return result;
}, []);
let annotationsDirectory = null;
const methodAnnotations = classMethods
.filter(([, , annotationsId]) => {
return annotationsId !== null;
})
.map(([index, , annotationsId]) => {
return [index, annotationSetItems[annotationSetIdToIndex[annotationsId]]];
});
if (methodAnnotations.length > 0) {
annotationsDirectory = {
methods: methodAnnotations,
offset: -1
};
annotationDirectories.push(annotationsDirectory);
}
const instanceFields = fieldItems.reduce((result, field, index) => {
const [holder] = field;
if (holder === classIndex) {
result.push([index > 0 ? 1 : 0, kAccPublic]);
}
return result;
}, []);
const constructorNameIndex = stringToIndex['<init>'];
const constructorMethods = classMethods
.filter(([, name]) => name === constructorNameIndex)
.map(([index, , , protoIndex]) => {
if (javaConstructors.has(klass.name)) {
let superConstructor = -1;
const numMethodItems = methodItems.length;
for (let i = 0; i !== numMethodItems; i++) {
const [methodClass, methodProto, methodName] = methodItems[i];
if (methodClass === superClassIndex && methodName === constructorNameIndex && methodProto === protoIndex) {
superConstructor = i;
break;
}
}
return [index, kAccPublic | kAccConstructor, superConstructor];
} else {
return [index, kAccPublic | kAccConstructor | kAccNative, -1];
}
});
const virtualMethods = compressClassMethodIndexes(classMethods
.filter(([, name]) => name !== constructorNameIndex)
.map(([index, , , , accessFlags]) => {
return [index, accessFlags | kAccPublic | kAccNative];
}));
const classData = {
instanceFields,
constructorMethods,
virtualMethods,
offset: -1
};
return {
index: classIndex,
accessFlags,
superClassIndex,
interfaces: ifaceList,
sourceFileIndex,
annotationsDirectory,
classData
};
});
const interfaceItems = Object.keys(interfaceLists).map(id => interfaceLists[id]);
return {
classes: classItems,
interfaces: interfaceItems,
fields: fieldItems,
methods: methodItems,
protos: protoItems,
parameters: parameterItems,
annotationDirectories,
annotationSets: annotationSetItems,
throwsAnnotations: throwsAnnotationItems,
types: typeItems,
strings: stringItems
};
}
function compressClassMethodIndexes (items) {
let previousIndex = 0;
return items.map(([index, accessFlags], elementIndex) => {
let result;
if (elementIndex === 0) {
result = [index, accessFlags];
} else {
result = [index - previousIndex, accessFlags];
}
previousIndex = index;
return result;
});
}
function compareNumbers (a, b) {
return a - b;
}
function compareProtoItems (a, b) {
const [, , aRetType, aArgTypes] = a;
const [, , bRetType, bArgTypes] = b;
if (aRetType < bRetType) {
return -1;
}
if (aRetType > bRetType) {
return 1;
}
const aArgTypesSig = aArgTypes.join('|');
const bArgTypesSig = bArgTypes.join('|');
if (aArgTypesSig < bArgTypesSig) {
return -1;
}
if (aArgTypesSig > bArgTypesSig) {
return 1;
}
return 0;
}
function compareFieldItems (a, b) {
const [aClass, aType, aName] = a;
const [bClass, bType, bName] = b;
if (aClass !== bClass) {
return aClass - bClass;
}
if (aName !== bName) {
return aName - bName;
}
return aType - bType;
}
function compareMethodItems (a, b) {
const [aClass, aProto, aName] = a;
const [bClass, bProto, bName] = b;
if (aClass !== bClass) {
return aClass - bClass;
}
if (aName !== bName) {
return aName - bName;
}
return aProto - bProto;
}
function typeToShorty (type) {
const firstCharacter = type[0];
return (firstCharacter === 'L' || firstCharacter === '[') ? 'L' : type;
}
function createUleb128 (value) {
if (value <= 0x7f) {
return [value];
}
const result = [];
let moreSlicesNeeded = false;
do {
let slice = value & 0x7f;
value >>= 7;
moreSlicesNeeded = value !== 0;
if (moreSlicesNeeded) {
slice |= 0x80;
}
result.push(slice);
} while (moreSlicesNeeded);
return result;
}
function align (value, alignment) {
const alignmentDelta = value % alignment;
if (alignmentDelta === 0) {
return value;
}
return value + alignment - alignmentDelta;
}
function adler32 (buffer, offset) {
let a = 1;
let b = 0;
const length = buffer.length;
for (let i = offset; i < length; i++) {
a = (a + buffer[i]) % 65521;
b = (b + a) % 65521;
}
return ((b << 16) | a) >>> 0;
}
export default mkdex;