Pass byte array more efficiently

Filling byte[] values is especially slow on Android (>3000ms to fill a byte[3000] on emulator).
This commit is contained in:
Yan Zhulanow
2017-06-02 19:22:10 +03:00
parent 069579fd79
commit 904b693571
3 changed files with 50 additions and 20 deletions
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.debugger.evaluate.classLoading
import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.evaluation.EvaluationContext
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.impl.DebuggerUtilsEx
import com.sun.jdi.*
abstract class AbstractAndroidClassLoadingAdapter : ClassLoadingAdapter {
@@ -27,18 +26,6 @@ abstract class AbstractAndroidClassLoadingAdapter : ClassLoadingAdapter {
return AndroidDexer.getInstances(context.project).single().dex(classes)
}
protected fun mirrorOfByteArray(bytes: ByteArray, context: EvaluationContextImpl, process: DebugProcessImpl): ArrayReference {
val arrayClass = process.findClass(context, "byte[]", context.classLoader) as ArrayType
val reference = process.newInstance(arrayClass, bytes.size)
DebuggerUtilsEx.keep(reference, context)
for (i in 0..bytes.lastIndex) {
reference.setValue(i, process.virtualMachineProxy.mirrorOf(bytes[i]))
}
return reference
}
protected fun wrapToByteBuffer(bytes: ArrayReference, context: EvaluationContext, process: DebugProcessImpl): ObjectReference {
val byteBufferClass = process.findClass(context, "java.nio.ByteBuffer", context.classLoader) as ClassType
val wrapMethod = byteBufferClass.concreteMethodByName("wrap", "([B)Ljava/nio/ByteBuffer;")
@@ -16,9 +16,12 @@
package org.jetbrains.kotlin.idea.debugger.evaluate.classLoading
import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.psi.PsiFile
import com.sun.jdi.ClassLoaderReference
import com.intellij.debugger.impl.DebuggerUtilsEx
import com.sun.jdi.ArrayReference
import com.sun.jdi.ArrayType
import com.sun.jdi.Value
interface ClassLoadingAdapter {
companion object {
@@ -41,4 +44,18 @@ interface ClassLoadingAdapter {
fun isApplicable(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): Boolean
fun loadClasses(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): ClassLoaderHandler
fun mirrorOfByteArray(bytes: ByteArray, context: EvaluationContextImpl, process: DebugProcessImpl): ArrayReference {
val arrayClass = process.findClass(context, "byte[]", context.classLoader) as ArrayType
val reference = process.newInstance(arrayClass, bytes.size)
DebuggerUtilsEx.keep(reference, context)
val mirrors = ArrayList<Value>(bytes.size)
for (byte in bytes) {
mirrors += process.virtualMachineProxy.mirrorOf(byte)
}
reference.values = mirrors
return reference
}
}
@@ -16,14 +16,15 @@
package org.jetbrains.kotlin.idea.debugger.evaluate.classLoading
import com.intellij.debugger.engine.DebugProcess
import com.intellij.debugger.engine.DebugProcessImpl
import com.intellij.debugger.engine.evaluation.EvaluateException
import com.intellij.debugger.engine.evaluation.EvaluationContext
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.impl.ClassLoadingUtils
import com.intellij.debugger.impl.DebuggerUtilsEx
import com.intellij.openapi.projectRoots.JdkVersionUtil
import com.intellij.openapi.util.SystemInfo
import com.sun.jdi.ClassLoaderReference
import com.sun.jdi.ClassType
import org.jetbrains.kotlin.idea.debugger.evaluate.CompilingEvaluatorUtils
class OrdinaryClassLoadingAdapter : ClassLoadingAdapter {
@@ -72,8 +73,8 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter {
private fun defineClasses(
classes: Collection<ClassToLoad>,
context: EvaluationContext,
process: DebugProcess,
context: EvaluationContextImpl,
process: DebugProcessImpl,
classLoader: ClassLoaderReference
) {
val classesToLoad = if (classes.size == 1) {
@@ -89,10 +90,35 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter {
for ((className, _, bytes) in classesToLoad) {
val patchedBytes = CompilingEvaluatorUtils.changeSuperToMagicAccessor(bytes)
ClassLoadingUtils.defineClass(className, patchedBytes, context, process, classLoader)
defineClass(className, patchedBytes, context, process, classLoader)
}
}
fun defineClass(
name: String,
bytes: ByteArray,
context: EvaluationContextImpl,
process: DebugProcessImpl,
classLoader: ClassLoaderReference
) {
try {
val vm = process.virtualMachineProxy
val classLoaderType = classLoader.referenceType() as ClassType
val defineMethod = classLoaderType.concreteMethodByName("defineClass", "(Ljava/lang/String;[BII)Ljava/lang/Class;")
val nameObj = vm.mirrorOf(name)
DebuggerUtilsEx.keep(nameObj, context)
process.invokeMethod(
context, classLoader, defineMethod,
listOf(nameObj, mirrorOfByteArray(bytes, context, process), vm.mirrorOf(0), vm.mirrorOf(bytes.size)))
}
catch (e: Exception) {
throw EvaluateException("Error during class $name definition: $e", e)
}
}
private class ClassBytes(val name: String) {
val bytes: ByteArray by lazy {
val inputStream = this::class.java.classLoader.getResourceAsStream(name.replace('.', '/') + ".class")