Fix and refactor DexParser implementation (#509)

In the LSPosedDexParser constructor, the methodIds array is allocated correctly, but the loop condition is wrong: its length is divided by 3 twice.

We rewrite this class in Kotlin, marking the first commit of refactoring LSPosed into Vector.

Co-authored-by: Willian Wang <git@willian.wang>
This commit is contained in:
JingMatrix 2026-01-20 22:44:46 +01:00
parent 402d3984d3
commit 4cec46a074
11 changed files with 391 additions and 445 deletions

View File

@ -20,6 +20,7 @@
import com.android.build.api.dsl.ApplicationDefaultConfig
import com.android.build.api.dsl.CommonExtension
import com.android.build.gradle.api.AndroidBasePlugin
import com.ncorti.ktfmt.gradle.tasks.KtfmtFormatTask
plugins {
alias(libs.plugins.lsplugin.cmaker)
@ -27,6 +28,8 @@ plugins {
alias(libs.plugins.agp.lib) apply false
alias(libs.plugins.agp.app) apply false
alias(libs.plugins.nav.safeargs) apply false
alias(libs.plugins.kotlin) apply false
alias(libs.plugins.ktfmt)
}
cmaker {
@ -119,3 +122,11 @@ subprojects {
}
}
}
tasks.register<KtfmtFormatTask>("format") {
source = project.fileTree(rootDir)
include("*.gradle.kts", "*/build.gradle.kts")
dependsOn(":xposed:ktfmtFormat")
}
ktfmt { kotlinLangStyle() }

View File

@ -62,6 +62,7 @@ dependencies {
implementation(projects.hiddenapi.bridge)
implementation(projects.services.daemonService)
implementation(projects.services.managerService)
implementation(projects.xposed)
compileOnly(libs.androidx.annotation)
compileOnly(projects.hiddenapi.stubs)
}

View File

@ -13,7 +13,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.lsposed.lspd.core.BuildConfig;
import org.lsposed.lspd.impl.utils.LSPosedDexParser;
import org.lsposed.lspd.models.Module;
import org.lsposed.lspd.nativebridge.HookBridge;
import org.lsposed.lspd.nativebridge.NativeAPI;
@ -21,6 +20,8 @@ import org.lsposed.lspd.service.ILSPInjectedModuleService;
import org.lsposed.lspd.util.LspModuleClassLoader;
import org.lsposed.lspd.util.Utils.Log;
import org.matrix.vector.impl.utils.VectorDexParser;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -288,7 +289,7 @@ public class LSPosedContext implements XposedInterface {
@Override
public DexParser parseDex(@NonNull ByteBuffer dexData, boolean includeAnnotations) throws IOException {
return new LSPosedDexParser(dexData, includeAnnotations);
return new VectorDexParser(dexData, includeAnnotations);
}
@NonNull

View File

@ -1,424 +0,0 @@
package org.lsposed.lspd.impl.utils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.lsposed.lspd.nativebridge.DexParserBridge;
import java.io.IOException;
import java.nio.ByteBuffer;
import io.github.libxposed.api.utils.DexParser;
public class LSPosedDexParser implements DexParser {
long cookie;
@NonNull
final ByteBuffer data;
@NonNull
final StringId[] strings;
@NonNull
final TypeId[] typeIds;
@NonNull
final ProtoId[] protoIds;
@NonNull
final FieldId[] fieldIds;
@NonNull
final MethodId[] methodIds;
@NonNull
final Annotation[] annotations;
@NonNull
final Array[] arrays;
public LSPosedDexParser(@NonNull ByteBuffer buffer, boolean includeAnnotations) throws IOException {
if (!buffer.isDirect() || !buffer.asReadOnlyBuffer().hasArray()) {
data = ByteBuffer.allocateDirect(buffer.capacity());
data.put(buffer);
} else {
data = buffer;
}
try {
long[] args = new long[2];
args[1] = includeAnnotations ? 1 : 0;
var out = (Object[]) DexParserBridge.openDex(buffer, args);
cookie = args[0];
// out[0]: String[]
// out[1]: int[]
// out[2]: int[][]
// out[3]: int[]
// out[4]: int[]
// out[5]: int[]
// out[6]: Object[]
// out[7]: Object[]
var strings = (Object[]) out[0];
this.strings = new StringId[strings.length];
for (int i = 0; i < strings.length; ++i) {
this.strings[i] = new LSPosedStringId(i, strings[i]);
}
var typeIds = (int[]) out[1];
this.typeIds = new TypeId[typeIds.length];
for (int i = 0; i < typeIds.length; ++i) {
this.typeIds[i] = new LSPosedTypeId(i, typeIds[i]);
}
var protoIds = (int[][]) out[2];
this.protoIds = new ProtoId[protoIds.length];
for (int i = 0; i < protoIds.length; ++i) {
this.protoIds[i] = new LSPosedProtoId(i, protoIds[i]);
}
var fieldIds = (int[]) out[3];
this.fieldIds = new FieldId[fieldIds.length / 3];
for (int i = 0; i < this.fieldIds.length; ++i) {
this.fieldIds[i] = new LSPosedFieldId(i, fieldIds[3 * i], fieldIds[3 * i + 1], fieldIds[3 * i + 2]);
}
var methodIds = (int[]) out[4];
this.methodIds = new MethodId[methodIds.length / 3];
for (int i = 0; i < this.methodIds.length / 3; ++i) {
this.methodIds[i] = new LSPosedMethodId(i, methodIds[3 * i], methodIds[3 * i + 1], methodIds[3 * i + 2]);
}
if (out[5] != null && out[6] != null) {
var a = (int[]) out[5];
var b = (Object[]) out[6];
this.annotations = new Annotation[a.length / 2];
for (int i = 0; i < this.annotations.length; ++i) {
this.annotations[i] = new LSPosedAnnotation(a[2 * i], a[2 * i + 1], (int[]) b[2 * i], (Object[]) b[2 * i + 1]);
}
} else {
this.annotations = new Annotation[0];
}
if (out[7] != null) {
var b = (Object[]) out[7];
this.arrays = new Array[b.length / 2];
for (int i = 0; i < this.arrays.length; ++i) {
this.arrays[i] = new LSPosedArray((int[]) b[2 * i], (Object[]) b[2 * i + 1]);
}
} else {
this.arrays = new Array[0];
}
} catch (Throwable e) {
throw new IOException("Invalid dex file", e);
}
}
@Override
synchronized public void close() {
if (cookie != 0) {
DexParserBridge.closeDex(cookie);
cookie = 0;
}
}
static class LSPosedId<Self extends Id<Self>> implements Id<Self> {
final int id;
LSPosedId(int id) {
this.id = id;
}
@Override
public int getId() {
return id;
}
@Override
public int compareTo(Self o) {
return id - o.getId();
}
}
static class LSPosedStringId extends LSPosedId<StringId> implements StringId {
@NonNull
final String string;
LSPosedStringId(int id, @NonNull Object string) {
super(id);
this.string = (String) string;
}
@NonNull
@Override
public String getString() {
return string;
}
}
class LSPosedTypeId extends LSPosedId<TypeId> implements TypeId {
@NonNull
final StringId descriptor;
LSPosedTypeId(int id, int descriptor) {
super(id);
this.descriptor = strings[descriptor];
}
@NonNull
@Override
public StringId getDescriptor() {
return descriptor;
}
}
class LSPosedProtoId extends LSPosedId<ProtoId> implements ProtoId {
@NonNull
final StringId shorty;
@NonNull
final TypeId returnType;
@Nullable
final TypeId[] parameters;
LSPosedProtoId(int id, @NonNull int[] protoId) {
super(id);
this.shorty = strings[protoId[0]];
this.returnType = typeIds[protoId[1]];
if (protoId.length > 2) {
this.parameters = new TypeId[protoId.length - 2];
for (int i = 2; i < parameters.length; ++i) {
this.parameters[i] = typeIds[protoId[i]];
}
} else {
this.parameters = null;
}
}
@NonNull
@Override
public StringId getShorty() {
return shorty;
}
@NonNull
@Override
public TypeId getReturnType() {
return returnType;
}
@Nullable
@Override
public TypeId[] getParameters() {
return parameters;
}
}
class LSPosedFieldId extends LSPosedId<FieldId> implements FieldId {
@NonNull
final TypeId type;
@NonNull
final TypeId declaringClass;
@NonNull
final StringId name;
LSPosedFieldId(int id, int type, int declaringClass, int name) {
super(id);
this.type = typeIds[type];
this.declaringClass = typeIds[declaringClass];
this.name = strings[name];
}
@NonNull
@Override
public TypeId getType() {
return type;
}
@NonNull
@Override
public TypeId getDeclaringClass() {
return declaringClass;
}
@NonNull
@Override
public StringId getName() {
return name;
}
}
class LSPosedMethodId extends LSPosedId<MethodId> implements MethodId {
@NonNull
final TypeId declaringClass;
@NonNull
final ProtoId prototype;
@NonNull
final StringId name;
LSPosedMethodId(int id, int declaringClass, int prototype, int name) {
super(id);
this.declaringClass = typeIds[declaringClass];
this.prototype = protoIds[prototype];
this.name = strings[name];
}
@NonNull
@Override
public TypeId getDeclaringClass() {
return declaringClass;
}
@NonNull
@Override
public ProtoId getPrototype() {
return prototype;
}
@NonNull
@Override
public StringId getName() {
return name;
}
}
static class LSPosedArray implements Array {
@NonNull
final Value[] values;
LSPosedArray(int[] elements, @NonNull Object[] values) {
this.values = new Value[values.length];
for (int i = 0; i < values.length; ++i) {
this.values[i] = new LSPosedValue(elements[i], (ByteBuffer) values[i]);
}
}
@NonNull
@Override
public Value[] getValues() {
return values;
}
}
class LSPosedAnnotation implements Annotation {
int visibility;
@NonNull
final TypeId type;
@NonNull
final Element[] elements;
LSPosedAnnotation(int visibility, int type, @NonNull int[] elements, @NonNull Object[] elementValues) {
this.visibility = visibility;
this.type = typeIds[type];
this.elements = new Element[elementValues.length];
for (int i = 0; i < elementValues.length; ++i) {
this.elements[i] = new LSPosedElement(elements[i * 2], elements[i * 2 + 1], (ByteBuffer) elementValues[i]);
}
}
@Override
public int getVisibility() {
return visibility;
}
@NonNull
@Override
public TypeId getType() {
return type;
}
@NonNull
@Override
public Element[] getElements() {
return elements;
}
}
static class LSPosedValue implements Value {
final int valueType;
@Nullable
final byte[] value;
LSPosedValue(int valueType, @Nullable ByteBuffer value) {
this.valueType = valueType;
if (value != null) {
this.value = new byte[value.remaining()];
value.get(this.value);
} else {
this.value = null;
}
}
@Nullable
@Override
public byte[] getValue() {
return value;
}
@Override
public int getValueType() {
return valueType;
}
}
class LSPosedElement extends LSPosedValue implements Element {
@NonNull
final StringId name;
LSPosedElement(int name, int valueType, @Nullable ByteBuffer value) {
super(valueType, value);
this.name = strings[name];
}
@NonNull
@Override
public StringId getName() {
return name;
}
}
@NonNull
@Override
public StringId[] getStringId() {
return strings;
}
@NonNull
@Override
public TypeId[] getTypeId() {
return typeIds;
}
@NonNull
@Override
public FieldId[] getFieldId() {
return fieldIds;
}
@NonNull
@Override
public MethodId[] getMethodId() {
return methodIds;
}
@NonNull
@Override
public ProtoId[] getProtoId() {
return protoIds;
}
@NonNull
@Override
public Annotation[] getAnnotations() {
return annotations;
}
@NonNull
@Override
public Array[] getArrays() {
return arrays;
}
@Override
synchronized public void visitDefinedClasses(@NonNull ClassVisitor visitor) {
if (cookie == 0) {
throw new IllegalStateException("Closed");
}
var classVisitMethod = ClassVisitor.class.getDeclaredMethods()[0];
var fieldVisitMethod = FieldVisitor.class.getDeclaredMethods()[0];
var methodVisitMethod = MethodVisitor.class.getDeclaredMethods()[0];
var methodBodyVisitMethod = MethodBodyVisitor.class.getDeclaredMethods()[0];
var stopMethod = EarlyStopVisitor.class.getDeclaredMethods()[0];
DexParserBridge.visitClass(cookie, visitor, FieldVisitor.class, MethodVisitor.class, classVisitMethod, fieldVisitMethod, methodVisitMethod, methodBodyVisitMethod, stopMethod);
}
}

View File

@ -1,19 +0,0 @@
package org.lsposed.lspd.nativebridge;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import dalvik.annotation.optimization.FastNative;
import io.github.libxposed.api.utils.DexParser;
public class DexParserBridge {
@FastNative
public static native Object openDex(ByteBuffer data, long[] args) throws IOException;
@FastNative
public static native void closeDex(long cookie);
@FastNative
public static native void visitClass(long cookie, Object visitor, Class<DexParser.FieldVisitor> fieldVisitorClass, Class<DexParser.MethodVisitor> methodVisitorClass, Method classVisitMethod, Method fieldVisitMethod, Method methodVisitMethod, Method methodBodyVisitMethod, Method stopMethod);
}

View File

@ -6,6 +6,7 @@ appcenter = "5.0.5"
libxposed = "100"
glide = "5.0.5"
okhttp = "5.3.2"
ktfmt = "0.25.0"
[plugins]
agp-lib = { id = "com.android.library", version.ref = "agp" }
@ -13,6 +14,7 @@ agp-app = { id = "com.android.application", version.ref = "agp" }
kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
nav-safeargs = { id = "androidx.navigation.safeargs", version.ref = "nav" }
autoresconfig = { id = "dev.rikka.tools.autoresconfig", version = "1.2.2" }
ktfmt = { id = "com.ncorti.ktfmt.gradle", version.ref = "ktfmt" }
materialthemebuilder = { id = "dev.rikka.tools.materialthemebuilder", version = "1.5.1" }
lsplugin-resopt = { id = "org.lsposed.lsplugin.resopt", version = "1.6" }
lsplugin-apksign = { id = "org.lsposed.lsplugin.apksign", version = "1.4" }

View File

@ -37,4 +37,5 @@ include(
":magisk-loader",
":services:manager-service",
":services:daemon-service",
":xposed",
)

5
xposed/README.md Normal file
View File

@ -0,0 +1,5 @@
# Xposed API implementation of the Vector framework
LSPosed is being refactored into a new project `Vector`.
This sub-project `xposed`, written in Kotlin, will be refactored from the `core` sub-project written in `Java`.

21
xposed/build.gradle.kts Normal file
View File

@ -0,0 +1,21 @@
plugins {
alias(libs.plugins.agp.lib)
alias(libs.plugins.kotlin)
alias(libs.plugins.ktfmt)
}
ktfmt { kotlinLangStyle() }
val versionCodeProvider: Provider<String> by rootProject.extra
val versionNameProvider: Provider<String> by rootProject.extra
android {
namespace = "org.matrix.vector.xposed"
buildFeatures { androidResources { enable = false } }
}
dependencies {
api(libs.libxposed.api)
compileOnly(libs.androidx.annotation)
}

View File

@ -0,0 +1,316 @@
package org.matrix.vector.impl.utils
import io.github.libxposed.api.utils.DexParser
import io.github.libxposed.api.utils.DexParser.*
import java.io.IOException
import java.nio.ByteBuffer
import org.lsposed.lspd.nativebridge.DexParserBridge
/**
* Kotlin implementation of [DexParser] for Vector.
*
* This class acts as a high-level wrapper around the native C++ DexParser. It maps raw JNI data
* structures (integer arrays, flat buffers) into usable object graphs (StringId, TypeId, MethodId,
* etc.).
*/
@Suppress("UNCHECKED_CAST")
class VectorDexParser(buffer: ByteBuffer, includeAnnotations: Boolean) : DexParser {
private var cookie: Long = 0
private val data: ByteBuffer
// Internal storage for parsed DEX structures.
// We use private properties and explicit getter methods as requested.
private val internalStrings: Array<StringId>
private val internalTypeIds: Array<TypeId>
private val internalProtoIds: Array<ProtoId>
private val internalFieldIds: Array<FieldId>
private val internalMethodIds: Array<MethodId>
private val internalAnnotations: Array<DexParser.Annotation>
private val internalArrays: Array<DexParser.Array>
init {
// Ensure the buffer is Direct and accessible by native code
data =
if (!buffer.isDirect || !buffer.asReadOnlyBuffer().hasArray()) {
ByteBuffer.allocateDirect(buffer.capacity()).apply {
put(buffer)
// Ensure position is reset for reading if needed,
// though native uses address
flip()
}
} else {
buffer
}
try {
val args = LongArray(2)
args[1] = if (includeAnnotations) 1 else 0
// Call Native Bridge
// Returns a raw Object[] containing headers and pools
val out = DexParserBridge.openDex(data, args) as Array<Any?>
cookie = args[0]
// --- Parse Strings (Index 0) ---
val rawStrings = out[0] as Array<String>
internalStrings = Array(rawStrings.size) { i -> VectorStringId(i, rawStrings[i]) }
// --- Parse Type IDs (Index 1) ---
val rawTypeIds = out[1] as IntArray
internalTypeIds = Array(rawTypeIds.size) { i -> VectorTypeId(i, rawTypeIds[i]) }
// --- Parse Proto IDs (Index 2) ---
val rawProtoIds = out[2] as Array<IntArray>
internalProtoIds = Array(rawProtoIds.size) { i -> VectorProtoId(i, rawProtoIds[i]) }
// --- Parse Field IDs (Index 3) ---
val rawFieldIds = out[3] as IntArray
// Each field is represented by 3 integers (class_idx, type_idx, name_idx)
internalFieldIds =
Array(rawFieldIds.size / 3) { i ->
VectorFieldId(
i,
rawFieldIds[3 * i],
rawFieldIds[3 * i + 1],
rawFieldIds[3 * i + 2],
)
}
// --- Parse Method IDs (Index 4) ---
val rawMethodIds = out[4] as IntArray
// Each method is represented by 3 integers (class_idx, proto_idx, name_idx)
internalMethodIds =
Array(rawMethodIds.size / 3) { i ->
VectorMethodId(
i,
rawMethodIds[3 * i],
rawMethodIds[3 * i + 1],
rawMethodIds[3 * i + 2],
)
}
// --- Parse Annotations (Index 5 & 6) ---
val rawAnnotationMetadata = out[5] as? IntArray
val rawAnnotationValues = out[6] as? Array<Any?>
internalAnnotations =
if (rawAnnotationMetadata != null && rawAnnotationValues != null) {
Array(rawAnnotationMetadata.size / 2) { i ->
// Metadata: [visibility, type_idx]
// Values: [name_indices[], values[]]
val elementsMeta = rawAnnotationValues[2 * i] as IntArray
val elementsData = rawAnnotationValues[2 * i + 1] as Array<Any?>
VectorAnnotation(
rawAnnotationMetadata[2 * i],
rawAnnotationMetadata[2 * i + 1],
elementsMeta,
elementsData,
)
}
} else {
emptyArray()
}
// --- Parse Arrays (Index 7) ---
val rawArrays = out[7] as? Array<Any?>
internalArrays =
if (rawArrays != null) {
Array(rawArrays.size / 2) { i ->
val types = rawArrays[2 * i] as IntArray
val values = rawArrays[2 * i + 1] as Array<Any?>
VectorArray(types, values)
}
} else {
emptyArray()
}
} catch (e: Throwable) {
throw IOException("Invalid dex file", e)
}
}
@Synchronized
override fun close() {
if (cookie != 0L) {
DexParserBridge.closeDex(cookie)
cookie = 0
}
}
override fun getStringId(): Array<StringId> = internalStrings
override fun getTypeId(): Array<TypeId> = internalTypeIds
override fun getFieldId(): Array<FieldId> = internalFieldIds
override fun getMethodId(): Array<MethodId> = internalMethodIds
override fun getProtoId(): Array<ProtoId> = internalProtoIds
override fun getAnnotations(): Array<DexParser.Annotation> = internalAnnotations
override fun getArrays(): Array<DexParser.Array> = internalArrays
override fun visitDefinedClasses(visitor: ClassVisitor) {
if (cookie == 0L) {
throw IllegalStateException("Closed")
}
// Accessing [0] is fragile
val classVisitMethod = ClassVisitor::class.java.declaredMethods[0]
val fieldVisitMethod = FieldVisitor::class.java.declaredMethods[0]
val methodVisitMethod = MethodVisitor::class.java.declaredMethods[0]
val methodBodyVisitMethod = MethodBodyVisitor::class.java.declaredMethods[0]
val stopMethod = EarlyStopVisitor::class.java.declaredMethods[0]
DexParserBridge.visitClass(
cookie,
visitor,
FieldVisitor::class.java,
MethodVisitor::class.java,
classVisitMethod,
fieldVisitMethod,
methodVisitMethod,
methodBodyVisitMethod,
stopMethod,
)
}
/** Base implementation for all Dex IDs. */
private open class VectorId<Self : Id<Self>>(private val id: Int) : Id<Self> {
override fun getId(): Int = id
override fun compareTo(other: Self): Int = id - other.id
}
private inner class VectorStringId(id: Int, private val string: String) :
VectorId<StringId>(id), StringId {
override fun getString(): String = string
}
private inner class VectorTypeId(id: Int, descriptorIdx: Int) : VectorId<TypeId>(id), TypeId {
private val descriptor: StringId = internalStrings[descriptorIdx]
override fun getDescriptor(): StringId = descriptor
}
private inner class VectorProtoId(id: Int, protoData: IntArray) :
VectorId<ProtoId>(id), ProtoId {
private val shorty: StringId = internalStrings[protoData[0]]
private val returnType: TypeId = internalTypeIds[protoData[1]]
private val parameters: Array<TypeId>?
init {
if (protoData.size > 2) {
// protoData format: [shorty_idx, return_type_idx, param1_idx, param2_idx...]
parameters = Array(protoData.size - 2) { i -> internalTypeIds[protoData[i + 2]] }
} else {
parameters = null
}
}
override fun getShorty(): StringId = shorty
override fun getReturnType(): TypeId = returnType
override fun getParameters(): Array<TypeId>? = parameters
}
private inner class VectorFieldId(id: Int, classIdx: Int, typeIdx: Int, nameIdx: Int) :
VectorId<FieldId>(id), FieldId {
private val declaringClass: TypeId = internalTypeIds[classIdx]
private val type: TypeId = internalTypeIds[typeIdx]
private val name: StringId = internalStrings[nameIdx]
override fun getType(): TypeId = type
override fun getDeclaringClass(): TypeId = declaringClass
override fun getName(): StringId = name
}
private inner class VectorMethodId(id: Int, classIdx: Int, protoIdx: Int, nameIdx: Int) :
VectorId<MethodId>(id), MethodId {
private val declaringClass: TypeId = internalTypeIds[classIdx]
private val prototype: ProtoId = internalProtoIds[protoIdx]
private val name: StringId = internalStrings[nameIdx]
override fun getDeclaringClass(): TypeId = declaringClass
override fun getPrototype(): ProtoId = prototype
override fun getName(): StringId = name
}
private class VectorArray(elementsTypes: IntArray, valuesData: Array<Any?>) : DexParser.Array {
private val values: Array<Value>
init {
values =
Array(valuesData.size) { i ->
VectorValue(elementsTypes[i], valuesData[i] as? ByteBuffer)
}
}
override fun getValues(): Array<Value> = values
}
private inner class VectorAnnotation(
private val visibility: Int,
typeIdx: Int,
elementNameIndices: IntArray,
elementValues: Array<Any?>,
) : DexParser.Annotation {
private val type: TypeId = internalTypeIds[typeIdx]
private val elements: Array<Element>
init {
elements =
Array(elementValues.size) { i ->
// Flattened structure from JNI: names are at 2*i, types at 2*i+1
VectorElement(
elementNameIndices[i * 2],
elementNameIndices[i * 2 + 1], // valueType
elementValues[i] as? ByteBuffer,
)
}
}
override fun getVisibility(): Int = visibility
override fun getType(): TypeId = type
override fun getElements(): Array<Element> = elements
}
private open class VectorValue(private val valueType: Int, buffer: ByteBuffer?) : Value {
private val value: ByteArray?
init {
if (buffer != null) {
value = ByteArray(buffer.remaining())
buffer.get(value)
} else {
value = null
}
}
override fun getValue(): ByteArray? = value
override fun getValueType(): Int = valueType
}
private inner class VectorElement(nameIdx: Int, valueType: Int, value: ByteBuffer?) :
VectorValue(valueType, value), Element {
private val name: StringId = internalStrings[nameIdx]
override fun getName(): StringId = name
}
}

View File

@ -0,0 +1,31 @@
package org.lsposed.lspd.nativebridge
// TODO: refactor the JNI and thus change the package name
import dalvik.annotation.optimization.FastNative
import io.github.libxposed.api.utils.DexParser
import java.io.IOException
import java.lang.reflect.Method
import java.nio.ByteBuffer
object DexParserBridge {
@JvmStatic
@FastNative
@Throws(IOException::class)
external fun openDex(data: ByteBuffer, args: LongArray): Any
@JvmStatic @FastNative external fun closeDex(cookie: Long)
@JvmStatic
@FastNative
external fun visitClass(
cookie: Long,
visitor: Any,
fieldVisitorClass: Class<DexParser.FieldVisitor>,
methodVisitorClass: Class<DexParser.MethodVisitor>,
classVisitMethod: Method,
fieldVisitMethod: Method,
methodVisitMethod: Method,
methodBodyVisitMethod: Method,
stopMethod: Method,
)
}