Debugger: Disable code interpreting by default in evaluator
Provide the compiled bytecode to debugger instead of interpreting the expression by default.
This significantly increases evaluation speed in numerous cases, e.g. `(1..10000).map { it.toString() }.count()`.
Leave interpreter mode as a fallback.
Android does not provide decent in-memory class loading support.
Moreover, we need to run dex on the compiled classes, and it required significant time.
So for now compiling evaluator for Android is available only in Android O, and only if there're any local classes or non-inline lambdas (evaluator didn't work for them before so we may consider this change as an improvement).
This commit is contained in:
committed by
Yan Zhulanow
parent
4851a83a83
commit
069579fd79
@@ -24,5 +24,6 @@
|
||||
<orderEntry type="library" name="uast-java" level="project" />
|
||||
<orderEntry type="library" name="kotlin-reflect" level="project" />
|
||||
<orderEntry type="module" module-name="light-classes" />
|
||||
<orderEntry type="library" scope="PROVIDED" name="dx-android" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.debugger
|
||||
|
||||
import com.android.dx.cf.direct.DirectClassFile
|
||||
import com.android.dx.cf.direct.StdAttributeFactory
|
||||
import com.android.dx.command.dexer.Main
|
||||
import com.android.dx.dex.cf.CfTranslator
|
||||
import com.android.dx.dex.file.DexFile
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad
|
||||
|
||||
class AndroidDexWrapper {
|
||||
@Suppress("unused") // Used in AndroidOClassLoadingAdapter#dex
|
||||
fun dex(classes: Collection<ClassToLoad>): ByteArray? {
|
||||
val dexArguments = Main.Arguments().apply { parse(arrayOf("testArgs")) }
|
||||
|
||||
val dexFile = DexFile(dexArguments.dexOptions)
|
||||
|
||||
for ((_, relativeFileName, bytes) in classes) {
|
||||
val cf = DirectClassFile(bytes, relativeFileName, true)
|
||||
cf.setAttributeFactory(StdAttributeFactory.THE_ONE)
|
||||
val classDef = CfTranslator.translate(
|
||||
cf,
|
||||
bytes,
|
||||
dexArguments.cfOptions,
|
||||
dexArguments.dexOptions,
|
||||
dexFile
|
||||
)
|
||||
|
||||
dexFile.add(classDef)
|
||||
}
|
||||
|
||||
return dexFile.toDex(null, false)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.android.debugger
|
||||
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ProjectRootModificationTracker
|
||||
import com.intellij.psi.util.CachedValueProvider
|
||||
import com.intellij.psi.util.CachedValuesManager
|
||||
import org.jetbrains.android.facet.AndroidFacet
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.AndroidDexer
|
||||
import org.jetbrains.kotlin.idea.debugger.evaluate.classLoading.ClassToLoad
|
||||
import java.io.File
|
||||
import java.net.URLClassLoader
|
||||
import java.security.ProtectionDomain
|
||||
|
||||
class AndroidDexerImpl(val project: Project) : AndroidDexer {
|
||||
private val cachedDexWrapper = CachedValuesManager.getManager(project).createCachedValue({
|
||||
val dexWrapper = doGetAndroidDexFile()?.let { dexJarFile ->
|
||||
val androidDexWrapperName = AndroidDexWrapper::class.java.canonicalName
|
||||
val classBytes = this.javaClass.classLoader.getResource(
|
||||
androidDexWrapperName.replace('.', '/') + ".class").readBytes()
|
||||
|
||||
val dexClassLoader = object : URLClassLoader(arrayOf(dexJarFile.toURI().toURL()), this::class.java.classLoader) {
|
||||
init {
|
||||
defineClass(androidDexWrapperName, classBytes, 0, classBytes.size, null as ProtectionDomain?)
|
||||
}
|
||||
}
|
||||
|
||||
Class.forName(androidDexWrapperName, true, dexClassLoader).newInstance()
|
||||
}
|
||||
|
||||
CachedValueProvider.Result.createSingleDependency(dexWrapper, ProjectRootModificationTracker.getInstance(project))
|
||||
}, /* trackValue = */ false)
|
||||
|
||||
override fun dex(classes: Collection<ClassToLoad>): ByteArray? {
|
||||
val dexWrapper = cachedDexWrapper.value
|
||||
val dexMethod = dexWrapper::class.java.methods.firstOrNull { it.name == "dex" } ?: return null
|
||||
return dexMethod.invoke(dexWrapper, classes) as? ByteArray ?: return null
|
||||
}
|
||||
|
||||
private fun doGetAndroidDexFile(): File? {
|
||||
for (module in ModuleManager.getInstance(project).modules) {
|
||||
val androidFacet = AndroidFacet.getInstance(module) ?: continue
|
||||
val sdkData = androidFacet.sdkData ?: continue
|
||||
val latestBuildTool = sdkData.getLatestBuildTool(/* allowPreview = */ false)
|
||||
?: sdkData.getLatestBuildTool(/* allowPreview = */ true)
|
||||
?: continue
|
||||
|
||||
val dxJar = File(latestBuildTool.location, "lib/dx.jar")
|
||||
if (dxJar.exists()) {
|
||||
return dxJar
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user