Use compiling evaluator only if there is non-inline lambda or a loop in the bytecode

This commit is contained in:
Yan Zhulanow
2017-06-06 18:28:43 +03:00
parent a6925e0d0f
commit 00225b3de3
3 changed files with 44 additions and 15 deletions
@@ -21,15 +21,11 @@ import com.intellij.debugger.engine.evaluation.EvaluateException
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl
import com.intellij.debugger.impl.DebuggerUtilsEx
import com.sun.jdi.*
import org.jetbrains.kotlin.idea.debugger.isDexDebug
class AndroidOClassLoadingAdapter : AbstractAndroidClassLoadingAdapter() {
override fun isApplicable(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): Boolean {
if (classes.size <= 1) {
// Dex takes significant amount of time so we load classes only if we have some non-inline lambdas
return false
}
return context.classLoader?.isDalvikClassLoader ?: false
override fun isApplicable(context: EvaluationContextImpl, hasAdditionalClasses: Boolean, hasLoops: Boolean): Boolean {
return (hasAdditionalClasses || hasLoops) && context.debugProcess.isDexDebug()
}
private fun resolveClassLoaderClass(context: EvaluationContextImpl): ClassType? {
@@ -22,6 +22,11 @@ import com.intellij.debugger.impl.DebuggerUtilsEx
import com.sun.jdi.ArrayReference
import com.sun.jdi.ArrayType
import com.sun.jdi.Value
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.tree.ClassNode
import org.jetbrains.org.objectweb.asm.tree.JumpInsnNode
import org.jetbrains.org.objectweb.asm.tree.LabelNode
interface ClassLoadingAdapter {
companion object {
@@ -29,19 +34,47 @@ interface ClassLoadingAdapter {
AndroidOClassLoadingAdapter(),
OrdinaryClassLoadingAdapter())
fun loadClasses(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): ClassLoaderHandler {
fun loadClasses(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): ClassLoaderHandler? {
val hasAdditionalClasses = classes.size > 1
val hasLoops = classes.isNotEmpty() && doesContainLoops(classes.first().bytes)
for (adapter in ADAPTERS) {
if (adapter.isApplicable(context, classes)) {
if (adapter.isApplicable(
context,
hasAdditionalClasses = hasAdditionalClasses,
hasLoops = hasLoops
)) {
return adapter.loadClasses(context, classes)
}
}
// Should never happen because OrdinaryClassLoadingAdapter is always applicable
throw IllegalStateException("Class loading adapter not found for $context")
return null
}
private fun doesContainLoops(clazz: ByteArray): Boolean {
val classNode = ClassNode().apply { ClassReader(clazz).accept(this, ClassReader.EXPAND_FRAMES) }
val methodToRun = classNode.methods.single()
val labelsVisited = hashSetOf<Label>()
var currentInsn = methodToRun.instructions.first
while (currentInsn != null) {
if (currentInsn is LabelNode) {
labelsVisited += currentInsn.label
}
else if (currentInsn is JumpInsnNode) {
if (currentInsn.label.label in labelsVisited) {
return true
}
}
currentInsn = currentInsn.next
}
return false
}
}
fun isApplicable(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): Boolean
fun isApplicable(context: EvaluationContextImpl, hasAdditionalClasses: Boolean, hasLoops: Boolean): Boolean
fun loadClasses(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): ClassLoaderHandler
@@ -26,6 +26,7 @@ import com.intellij.openapi.util.SystemInfo
import com.sun.jdi.ClassLoaderReference
import com.sun.jdi.ClassType
import org.jetbrains.kotlin.idea.debugger.evaluate.CompilingEvaluatorUtils
import org.jetbrains.kotlin.idea.debugger.isDexDebug
class OrdinaryClassLoadingAdapter : ClassLoadingAdapter {
private companion object {
@@ -36,9 +37,8 @@ class OrdinaryClassLoadingAdapter : ClassLoadingAdapter {
private val LAMBDA_SUPERCLASSES = listOf(ClassBytes("kotlin.jvm.internal.Lambda"))
}
override fun isApplicable(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): Boolean {
val classLoader = context.classLoader
return classLoader != null && !classLoader.isDalvikClassLoader
override fun isApplicable(context: EvaluationContextImpl, hasAdditionalClasses: Boolean, hasLoops: Boolean): Boolean {
return (hasAdditionalClasses || hasLoops) && context.classLoader != null && !context.debugProcess.isDexDebug()
}
override fun loadClasses(context: EvaluationContextImpl, classes: Collection<ClassToLoad>): ClassLoaderHandler {