Support cache cleanup in android-compiler-plugin
This commit is contained in:
@@ -1447,8 +1447,9 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
List<ValueParameterDescriptor> superValueParameters = superConstructor.getValueParameters();
|
List<ValueParameterDescriptor> superValueParameters = superConstructor.getValueParameters();
|
||||||
int params = superValueParameters.size();
|
int params = superValueParameters.size();
|
||||||
List<Type> superMappedTypes = typeMapper.mapToCallableMethod(superConstructor).getValueParameterTypes();
|
List<Type> superMappedTypes = typeMapper.mapToCallableMethod(superConstructor).getValueParameterTypes();
|
||||||
assert superMappedTypes.size() >= params : String.format("Incorrect number of mapped parameters vs arguments: %d < %d for %s",
|
assert superMappedTypes.size() >= params : String
|
||||||
superMappedTypes.size(), params, classDescriptor);
|
.format("Incorrect number of mapped parameters vs arguments: %d < %d for %s",
|
||||||
|
superMappedTypes.size(), params, classDescriptor);
|
||||||
|
|
||||||
List<ResolvedValueArgument> valueArguments = new ArrayList<ResolvedValueArgument>(params);
|
List<ResolvedValueArgument> valueArguments = new ArrayList<ResolvedValueArgument>(params);
|
||||||
List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(params);
|
List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(params);
|
||||||
@@ -1461,7 +1462,8 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
mappedTypes.add(superMappedTypes.get(parameter.getIndex()));
|
mappedTypes.add(superMappedTypes.get(parameter.getIndex()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ArgumentGenerator argumentGenerator = new CallBasedArgumentGenerator(ExpressionCodegen.this, defaultCallGenerator, valueParameters, mappedTypes);
|
ArgumentGenerator argumentGenerator =
|
||||||
|
new CallBasedArgumentGenerator(ExpressionCodegen.this, defaultCallGenerator, valueParameters, mappedTypes);
|
||||||
|
|
||||||
argumentGenerator.generate(valueArguments);
|
argumentGenerator.generate(valueArguments);
|
||||||
}
|
}
|
||||||
@@ -1904,10 +1906,15 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
if (descriptor instanceof PropertyDescriptor) {
|
if (descriptor instanceof PropertyDescriptor) {
|
||||||
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
|
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
|
||||||
|
|
||||||
for (ExpressionCodegenExtension extension : ExpressionCodegenExtension.OBJECT$.getInstances(state.getProject())) {
|
Collection<ExpressionCodegenExtension> codegenExtensions = ExpressionCodegenExtension.Default.getInstances(state.getProject());
|
||||||
StackValue result = extension.apply(receiver, resolvedCall, new ExpressionCodegenExtension.Context(typeMapper, v));
|
if (!codegenExtensions.isEmpty() && resolvedCall != null) {
|
||||||
|
ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v);
|
||||||
if (result != null) return result;
|
JetType returnType = propertyDescriptor.getReturnType();
|
||||||
|
for (ExpressionCodegenExtension extension : codegenExtensions) {
|
||||||
|
if (returnType != null && extension.apply(receiver, resolvedCall, context)) {
|
||||||
|
return StackValue.onStack(typeMapper.mapType(returnType));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean directToField =
|
boolean directToField =
|
||||||
@@ -2338,6 +2345,15 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
|
|||||||
) {
|
) {
|
||||||
if (!(resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor)) { // otherwise already
|
if (!(resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor)) { // otherwise already
|
||||||
receiver = StackValue.receiver(resolvedCall, receiver, this, callableMethod);
|
receiver = StackValue.receiver(resolvedCall, receiver, this, callableMethod);
|
||||||
|
|
||||||
|
Collection<ExpressionCodegenExtension> codegenExtensions = ExpressionCodegenExtension.Default.getInstances(state.getProject());
|
||||||
|
if (!codegenExtensions.isEmpty()) {
|
||||||
|
ExpressionCodegenExtension.Context context = new ExpressionCodegenExtension.Context(typeMapper, v);
|
||||||
|
for (ExpressionCodegenExtension extension : codegenExtensions) {
|
||||||
|
if (extension.apply(receiver, resolvedCall, context)) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
receiver.put(receiver.type, v);
|
receiver.put(receiver.type, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -35,8 +35,9 @@ public trait ExpressionCodegenExtension {
|
|||||||
public val v: InstructionAdapter
|
public val v: InstructionAdapter
|
||||||
)
|
)
|
||||||
|
|
||||||
// return null if not applicable
|
// Function is responsible to put the value on stack by itself.
|
||||||
public fun apply(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): StackValue?
|
// Returns false if not applicable, and if stack was not modified.
|
||||||
|
public fun apply(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: Context): Boolean
|
||||||
|
|
||||||
public fun generateClassSyntheticParts(
|
public fun generateClassSyntheticParts(
|
||||||
classBuilder: ClassBuilder,
|
classBuilder: ClassBuilder,
|
||||||
|
|||||||
@@ -23,20 +23,20 @@ import org.jetbrains.kotlin.codegen.StackValue
|
|||||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.codegen.state.JetTypeMapper
|
import org.jetbrains.kotlin.codegen.state.JetTypeMapper
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
|
||||||
import org.jetbrains.kotlin.lang.resolve.android.AndroidConst
|
import org.jetbrains.kotlin.lang.resolve.android.AndroidConst
|
||||||
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor
|
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor
|
||||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.JetFile
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
|
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassReceiver
|
||||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||||
|
import org.jetbrains.kotlin.types.Flexibility
|
||||||
import org.jetbrains.org.objectweb.asm.Label
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PRIVATE
|
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PRIVATE
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PUBLIC
|
import org.jetbrains.org.objectweb.asm.Opcodes.ACC_PUBLIC
|
||||||
@@ -53,60 +53,103 @@ private enum class AndroidClassType(val internalClassName: String, val supportsC
|
|||||||
public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
||||||
default object {
|
default object {
|
||||||
private val PROPERTY_NAME = "_\$_findViewCache"
|
private val PROPERTY_NAME = "_\$_findViewCache"
|
||||||
private val METHOD_NAME = "_\$_findCachedViewById"
|
private val CACHED_FIND_VIEW_BY_ID_METHOD_NAME = "_\$_findCachedViewById"
|
||||||
|
private val CLEAR_CACHE_METHOD_NAME = "_\$_clearFindViewByIdCache"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun apply(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): StackValue? {
|
private class SyntheticPartsGenerateContext(
|
||||||
if (resolvedCall.getResultingDescriptor() !is PropertyDescriptor) return null
|
val classBuilder: ClassBuilder,
|
||||||
|
val state: GenerationState,
|
||||||
|
val descriptor: ClassDescriptor,
|
||||||
|
val classOrObject: JetClassOrObject,
|
||||||
|
val androidClassType: AndroidClassType) {
|
||||||
|
|
||||||
val propertyDescriptor = resolvedCall.getResultingDescriptor() as PropertyDescriptor
|
}
|
||||||
|
|
||||||
val file = DescriptorToSourceUtils.getContainingFile(propertyDescriptor)
|
override fun apply(receiver: StackValue, resolvedCall: ResolvedCall<*>, c: ExpressionCodegenExtension.Context): Boolean {
|
||||||
if (file == null) return null
|
val resultingDescriptor = resolvedCall.getResultingDescriptor()
|
||||||
|
return when (resultingDescriptor) {
|
||||||
|
is PropertyDescriptor -> generateSyntheticPropertyCall(receiver, resolvedCall, c, resultingDescriptor)
|
||||||
|
is FunctionDescriptor -> generateSyntheticFunctionCall(receiver, resolvedCall, c, resultingDescriptor)
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val androidPackage = file.getUserData<String>(AndroidConst.ANDROID_USER_PACKAGE)
|
private fun generateSyntheticFunctionCall(
|
||||||
if (androidPackage == null) return null
|
receiver: StackValue,
|
||||||
|
resolvedCall: ResolvedCall<*>,
|
||||||
|
c: ExpressionCodegenExtension.Context,
|
||||||
|
descriptor: FunctionDescriptor
|
||||||
|
): Boolean {
|
||||||
|
if (descriptor.getAndroidPackage() == null) return false
|
||||||
|
if (descriptor.getName().asString() != AndroidConst.CLEAR_FUNCTION_NAME) return false
|
||||||
|
|
||||||
val retType = c.typeMapper.mapType(propertyDescriptor.getReturnType()!!)
|
val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return false
|
||||||
|
if (!isCacheSupported(declarationDescriptor)) return true
|
||||||
val extensionReceiver = resolvedCall.getExtensionReceiver()
|
|
||||||
val declarationDescriptor = extensionReceiver.getType().getConstructor().getDeclarationDescriptor()
|
|
||||||
|
|
||||||
if (declarationDescriptor == null) return null
|
|
||||||
|
|
||||||
val supportsCache = declarationDescriptor.getSource() is KotlinSourceElement
|
|
||||||
|
|
||||||
val androidClassType = getClassType(declarationDescriptor)
|
val androidClassType = getClassType(declarationDescriptor)
|
||||||
if (supportsCache && androidClassType.supportsCache) {
|
if (androidClassType == AndroidClassType.UNKNOWN) return false
|
||||||
|
|
||||||
|
val bytecodeClassName = DescriptorUtils.getFqName(declarationDescriptor).asString().replace('.', '/')
|
||||||
|
|
||||||
|
receiver.put(c.typeMapper.mapType(declarationDescriptor), c.v)
|
||||||
|
c.v.invokevirtual(bytecodeClassName, CLEAR_CACHE_METHOD_NAME, "()V", false)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateSyntheticPropertyCall(
|
||||||
|
receiver: StackValue,
|
||||||
|
resolvedCall: ResolvedCall<*>,
|
||||||
|
c: ExpressionCodegenExtension.Context,
|
||||||
|
descriptor: PropertyDescriptor
|
||||||
|
): Boolean {
|
||||||
|
val androidPackage = descriptor.getAndroidPackage() ?: return false
|
||||||
|
val declarationDescriptor = resolvedCall.getReceiverDeclarationDescriptor() ?: return false
|
||||||
|
|
||||||
|
val androidClassType = getClassType(declarationDescriptor)
|
||||||
|
if (androidClassType.supportsCache && isCacheSupported(declarationDescriptor)) {
|
||||||
val className = DescriptorUtils.getFqName(declarationDescriptor).toString()
|
val className = DescriptorUtils.getFqName(declarationDescriptor).toString()
|
||||||
val bytecodeClassName = className.replace('.', '/')
|
val bytecodeClassName = className.replace('.', '/')
|
||||||
|
|
||||||
receiver.put(Type.getType("L$bytecodeClassName;"), c.v)
|
receiver.put(c.typeMapper.mapType(declarationDescriptor), c.v)
|
||||||
c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.getName().asString(), "I")
|
c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I")
|
||||||
c.v.invokevirtual(bytecodeClassName, METHOD_NAME, "(I)Landroid/view/View;", false)
|
c.v.invokevirtual(bytecodeClassName, CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;", false)
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
when (androidClassType) {
|
when (androidClassType) {
|
||||||
AndroidClassType.ACTIVITY, AndroidClassType.VIEW -> {
|
AndroidClassType.ACTIVITY, AndroidClassType.VIEW -> {
|
||||||
receiver.put(Type.getType("L${androidClassType.internalClassName};"), c.v)
|
receiver.put(Type.getType("L${androidClassType.internalClassName};"), c.v)
|
||||||
c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.getName().asString(), "I")
|
c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I")
|
||||||
c.v.invokevirtual(androidClassType.internalClassName, "findViewById", "(I)Landroid/view/View;", false)
|
c.v.invokevirtual(androidClassType.internalClassName, "findViewById", "(I)Landroid/view/View;", false)
|
||||||
}
|
}
|
||||||
AndroidClassType.FRAGMENT -> {
|
AndroidClassType.FRAGMENT -> {
|
||||||
receiver.put(Type.getType("L${androidClassType.internalClassName};"), c.v)
|
receiver.put(Type.getType("L${androidClassType.internalClassName};"), c.v)
|
||||||
c.v.invokevirtual(androidClassType.internalClassName, "getView", "()Landroid/view/View;", false)
|
c.v.invokevirtual(androidClassType.internalClassName, "getView", "()Landroid/view/View;", false)
|
||||||
c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", propertyDescriptor.getName().asString(), "I")
|
c.v.getstatic(androidPackage.replace(".", "/") + "/R\$id", descriptor.getName().asString(), "I")
|
||||||
c.v.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false)
|
c.v.invokevirtual("android/view/View", "findViewById", "(I)Landroid/view/View;", false)
|
||||||
}
|
}
|
||||||
else -> return null
|
else -> return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val retType = c.typeMapper.mapType(descriptor.getReturnType()!!)
|
||||||
c.v.checkcast(retType)
|
c.v.checkcast(retType)
|
||||||
return StackValue.onStack(retType)
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun CallableDescriptor.getAndroidPackage(): String? {
|
||||||
|
return DescriptorToSourceUtils.getContainingFile(this)?.getUserData<String>(AndroidConst.ANDROID_USER_PACKAGE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ResolvedCall<*>.getReceiverDeclarationDescriptor(): ClassifierDescriptor? {
|
||||||
|
return getExtensionReceiver().getType().getConstructor().getDeclarationDescriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isCacheSupported(descriptor: ClassifierDescriptor) = descriptor.getSource() is KotlinSourceElement
|
||||||
|
|
||||||
private fun getClassType(descriptor: ClassifierDescriptor): AndroidClassType {
|
private fun getClassType(descriptor: ClassifierDescriptor): AndroidClassType {
|
||||||
fun getClassTypeInternal(name: String): AndroidClassType? = when (name) {
|
fun getClassTypeInternal(name: String): AndroidClassType? = when (name) {
|
||||||
"android.app.Activity" -> AndroidClassType.ACTIVITY
|
"android.app.Activity" -> AndroidClassType.ACTIVITY
|
||||||
@@ -118,7 +161,8 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
if (descriptor is LazyJavaClassDescriptor) {
|
if (descriptor is LazyJavaClassDescriptor) {
|
||||||
val androidClassType = getClassTypeInternal(descriptor.fqName.asString())
|
val androidClassType = getClassTypeInternal(descriptor.fqName.asString())
|
||||||
if (androidClassType != null) return androidClassType
|
if (androidClassType != null) return androidClassType
|
||||||
} else if (descriptor is LazyClassDescriptor) { // For tests (FakeActivity)
|
}
|
||||||
|
else if (descriptor is LazyClassDescriptor) { // For tests (FakeActivity)
|
||||||
val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).toString())
|
val androidClassType = getClassTypeInternal(DescriptorUtils.getFqName(descriptor).toString())
|
||||||
if (androidClassType != null) return androidClassType
|
if (androidClassType != null) return androidClassType
|
||||||
}
|
}
|
||||||
@@ -146,15 +190,47 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
val androidClassType = getClassType(descriptor)
|
val androidClassType = getClassType(descriptor)
|
||||||
if (androidClassType == AndroidClassType.UNKNOWN) return
|
if (androidClassType == AndroidClassType.UNKNOWN) return
|
||||||
|
|
||||||
|
val context = SyntheticPartsGenerateContext(classBuilder, state, descriptor, classOrObject, androidClassType)
|
||||||
|
context.generateCachedFindViewByIdFunction()
|
||||||
|
context.generateClearCacheFunction()
|
||||||
|
|
||||||
|
classBuilder.newField(JvmDeclarationOrigin.Default.NO_ORIGIN, ACC_PRIVATE, PROPERTY_NAME, "Ljava/util/HashMap;", null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SyntheticPartsGenerateContext.generateClearCacheFunction() {
|
||||||
|
val methodVisitor = classBuilder.newMethod(
|
||||||
|
JvmDeclarationOrigin.Default.NO_ORIGIN, ACC_PUBLIC, CLEAR_CACHE_METHOD_NAME, "()V", null, null)
|
||||||
|
methodVisitor.visitCode()
|
||||||
|
val iv = InstructionAdapter(methodVisitor)
|
||||||
|
|
||||||
|
val classType = state.getTypeMapper().mapClass(descriptor)
|
||||||
|
val className = classType.getInternalName()
|
||||||
|
|
||||||
|
fun loadCache() {
|
||||||
|
iv.load(0, classType)
|
||||||
|
iv.getfield(className, PROPERTY_NAME, "Ljava/util/HashMap;")
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCache()
|
||||||
|
val lCacheIsNull = Label()
|
||||||
|
iv.ifnull(lCacheIsNull)
|
||||||
|
|
||||||
|
loadCache()
|
||||||
|
iv.invokevirtual("java/util/HashMap", "clear", "()V", false)
|
||||||
|
|
||||||
|
iv.visitLabel(lCacheIsNull)
|
||||||
|
iv.areturn(Type.VOID_TYPE)
|
||||||
|
FunctionCodegen.endVisit(methodVisitor, CLEAR_CACHE_METHOD_NAME, classOrObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun SyntheticPartsGenerateContext.generateCachedFindViewByIdFunction() {
|
||||||
val classType = state.getTypeMapper().mapClass(descriptor)
|
val classType = state.getTypeMapper().mapClass(descriptor)
|
||||||
val className = classType.getInternalName()
|
val className = classType.getInternalName()
|
||||||
|
|
||||||
val viewType = Type.getObjectType("android/view/View")
|
val viewType = Type.getObjectType("android/view/View")
|
||||||
|
|
||||||
classBuilder.newField(org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Default.NO_ORIGIN, ACC_PRIVATE, PROPERTY_NAME, "Ljava/util/HashMap;", null, null)
|
|
||||||
|
|
||||||
val methodVisitor = classBuilder.newMethod(
|
val methodVisitor = classBuilder.newMethod(
|
||||||
org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Default.NO_ORIGIN, ACC_PUBLIC, METHOD_NAME, "(I)Landroid/view/View;", null, null)
|
JvmDeclarationOrigin.Default.NO_ORIGIN, ACC_PUBLIC, CACHED_FIND_VIEW_BY_ID_METHOD_NAME, "(I)Landroid/view/View;", null, null)
|
||||||
methodVisitor.visitCode()
|
methodVisitor.visitCode()
|
||||||
val iv = InstructionAdapter(methodVisitor)
|
val iv = InstructionAdapter(methodVisitor)
|
||||||
|
|
||||||
@@ -218,6 +294,6 @@ public class AndroidExpressionCodegenExtension : ExpressionCodegenExtension {
|
|||||||
iv.load(2, viewType)
|
iv.load(2, viewType)
|
||||||
iv.areturn(viewType)
|
iv.areturn(viewType)
|
||||||
|
|
||||||
FunctionCodegen.endVisit(methodVisitor, METHOD_NAME, classOrObject)
|
FunctionCodegen.endVisit(methodVisitor, CACHED_FIND_VIEW_BY_ID_METHOD_NAME, classOrObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
@@ -30,6 +30,8 @@ public object AndroidConst {
|
|||||||
|
|
||||||
val ID_DECLARATION_PREFIX = "@+id/"
|
val ID_DECLARATION_PREFIX = "@+id/"
|
||||||
val ID_USAGE_PREFIX = "@id/"
|
val ID_USAGE_PREFIX = "@id/"
|
||||||
|
|
||||||
|
val CLEAR_FUNCTION_NAME = "clearFindViewByIdCache"
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun nameToIdDeclaration(name: String): String = AndroidConst.ID_DECLARATION_PREFIX + name
|
public fun nameToIdDeclaration(name: String): String = AndroidConst.ID_DECLARATION_PREFIX + name
|
||||||
|
|||||||
+11
-3
@@ -87,7 +87,13 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun parse(): List<String> {
|
public fun parse(generateCommonFiles: Boolean = true): List<String> {
|
||||||
|
val commonFiles = if (generateCommonFiles) {
|
||||||
|
val clearCacheFile = renderLayoutFile("kotlinx.android.synthetic") {} +
|
||||||
|
renderClearCacheFunction("Activity") + renderClearCacheFunction("Fragment")
|
||||||
|
listOf(clearCacheFile)
|
||||||
|
} else listOf()
|
||||||
|
|
||||||
return resourceManager.getLayoutXmlFiles().flatMap { file ->
|
return resourceManager.getLayoutXmlFiles().flatMap { file ->
|
||||||
val widgets = parseSingleFile(file)
|
val widgets = parseSingleFile(file)
|
||||||
if (widgets.isNotEmpty()) {
|
if (widgets.isNotEmpty()) {
|
||||||
@@ -104,7 +110,7 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
|
|||||||
|
|
||||||
listOf(mainLayoutFile, viewLayoutFile)
|
listOf(mainLayoutFile, viewLayoutFile)
|
||||||
} else listOf()
|
} else listOf()
|
||||||
}.filterNotNull()
|
}.filterNotNull() + commonFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun parseToPsi(): List<JetFile>? = cachedJetFiles.getValue()
|
public fun parseToPsi(): List<JetFile>? = cachedJetFiles.getValue()
|
||||||
@@ -113,7 +119,7 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
|
|||||||
|
|
||||||
private fun renderLayoutFile(
|
private fun renderLayoutFile(
|
||||||
packageName: String,
|
packageName: String,
|
||||||
widgets: List<AndroidWidget>,
|
widgets: List<AndroidWidget> = listOf(),
|
||||||
widgetWriter: KotlinStringWriter.(AndroidWidget) -> Unit
|
widgetWriter: KotlinStringWriter.(AndroidWidget) -> Unit
|
||||||
): String {
|
): String {
|
||||||
val stringWriter = KotlinStringWriter()
|
val stringWriter = KotlinStringWriter()
|
||||||
@@ -141,6 +147,8 @@ public abstract class AndroidUIXmlProcessor(protected val project: Project) {
|
|||||||
getterBody = body)
|
getterBody = body)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun renderClearCacheFunction(receiver: String) = "public fun $receiver.${AndroidConst.CLEAR_FUNCTION_NAME}() {}\n"
|
||||||
|
|
||||||
private fun <T> cachedValue(result: () -> CachedValueProvider.Result<T>): CachedValue<T> {
|
private fun <T> cachedValue(result: () -> CachedValueProvider.Result<T>): CachedValue<T> {
|
||||||
return CachedValuesManager.getManager(project).createCachedValue(result, false)
|
return CachedValuesManager.getManager(project).createCachedValue(result, false)
|
||||||
}
|
}
|
||||||
|
|||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package com.myapp
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.os.Bundle
|
||||||
|
import java.io.File
|
||||||
|
import kotlinx.android.synthetic.*
|
||||||
|
|
||||||
|
public class MyActivity : Activity() {
|
||||||
|
{clearFindViewByIdCache()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5 INVOKEVIRTUAL
|
||||||
|
// 1 CHECKCAST
|
||||||
|
// 2 _\$_clearFindViewByIdCache
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package com.myapp
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.os.Bundle
|
||||||
|
import java.io.File
|
||||||
|
import kotlinx.android.synthetic.*
|
||||||
|
|
||||||
|
public fun Activity.a() {
|
||||||
|
clearFindViewByIdCache()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 clearFindViewByIdCache
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
+1
-1
@@ -15,7 +15,7 @@ fun MyActivity.b() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2 GETSTATIC
|
// 2 GETSTATIC
|
||||||
// 5 INVOKEVIRTUAL
|
// 6 INVOKEVIRTUAL
|
||||||
// 3 CHECKCAST
|
// 3 CHECKCAST
|
||||||
// 3 _\$_findCachedViewById
|
// 3 _\$_findCachedViewById
|
||||||
// 1 findViewById
|
// 1 findViewById
|
||||||
+1
-1
@@ -15,7 +15,7 @@ fun Activity.b() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2 GETSTATIC
|
// 2 GETSTATIC
|
||||||
// 5 INVOKEVIRTUAL
|
// 6 INVOKEVIRTUAL
|
||||||
// 3 CHECKCAST
|
// 3 CHECKCAST
|
||||||
// 1 _\$_findCachedViewById
|
// 1 _\$_findCachedViewById
|
||||||
// 3 findViewById
|
// 3 findViewById
|
||||||
+1
-1
@@ -15,7 +15,7 @@ fun Fragment.b() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2 GETSTATIC
|
// 2 GETSTATIC
|
||||||
// 8 INVOKEVIRTUAL
|
// 9 INVOKEVIRTUAL
|
||||||
// 3 CHECKCAST
|
// 3 CHECKCAST
|
||||||
// 1 _\$_findCachedViewById
|
// 1 _\$_findCachedViewById
|
||||||
// 3 findViewById
|
// 3 findViewById
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ fun MyActivity.b() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2 GETSTATIC
|
// 2 GETSTATIC
|
||||||
// 5 INVOKEVIRTUAL
|
// 6 INVOKEVIRTUAL
|
||||||
// 3 CHECKCAST
|
// 3 CHECKCAST
|
||||||
// 3 _\$_findCachedViewById
|
// 3 _\$_findCachedViewById
|
||||||
// 1 findViewById
|
// 1 findViewById
|
||||||
+1
-1
@@ -9,7 +9,7 @@ class MyActivity: Activity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2 GETSTATIC
|
// 2 GETSTATIC
|
||||||
// 5 INVOKEVIRTUAL
|
// 6 INVOKEVIRTUAL
|
||||||
// 3 CHECKCAST
|
// 3 CHECKCAST
|
||||||
// 3 _\$_findCachedViewById
|
// 3 _\$_findCachedViewById
|
||||||
// 1 findViewById
|
// 1 findViewById
|
||||||
+1
-1
@@ -9,7 +9,7 @@ class MyFragment: Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2 GETSTATIC
|
// 2 GETSTATIC
|
||||||
// 6 INVOKEVIRTUAL
|
// 7 INVOKEVIRTUAL
|
||||||
// 3 CHECKCAST
|
// 3 CHECKCAST
|
||||||
// 3 _\$_findCachedViewById
|
// 3 _\$_findCachedViewById
|
||||||
// 1 findViewById
|
// 1 findViewById
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ class MyActivity: Activity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2 GETSTATIC
|
// 2 GETSTATIC
|
||||||
// 5 INVOKEVIRTUAL
|
// 6 INVOKEVIRTUAL
|
||||||
// 3 CHECKCAST
|
// 3 CHECKCAST
|
||||||
// 3 _\$_findCachedViewById
|
// 3 _\$_findCachedViewById
|
||||||
// 1 findViewById
|
// 1 findViewById
|
||||||
+1
-1
@@ -9,7 +9,7 @@ class MyFragment: Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2 GETSTATIC
|
// 2 GETSTATIC
|
||||||
// 6 INVOKEVIRTUAL
|
// 7 INVOKEVIRTUAL
|
||||||
// 3 CHECKCAST
|
// 3 CHECKCAST
|
||||||
// 3 _\$_findCachedViewById
|
// 3 _\$_findCachedViewById
|
||||||
// 1 findViewById
|
// 1 findViewById
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ class MyActivity: Activity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2 GETSTATIC
|
// 2 GETSTATIC
|
||||||
// 5 INVOKEVIRTUAL
|
// 6 INVOKEVIRTUAL
|
||||||
// 3 CHECKCAST
|
// 3 CHECKCAST
|
||||||
// 3 _\$_findCachedViewById
|
// 3 _\$_findCachedViewById
|
||||||
// 1 findViewById
|
// 1 findViewById
|
||||||
+1
-1
@@ -14,7 +14,7 @@ class MyFragment: Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2 GETSTATIC
|
// 2 GETSTATIC
|
||||||
// 9 INVOKEVIRTUAL
|
// 11 INVOKEVIRTUAL
|
||||||
// 4 CHECKCAST
|
// 4 CHECKCAST
|
||||||
// 4 _\$_findCachedViewById
|
// 4 _\$_findCachedViewById
|
||||||
// 2 findViewById
|
// 2 findViewById
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public class MyActivity : Activity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1 GETSTATIC
|
// 1 GETSTATIC
|
||||||
// 4 INVOKEVIRTUAL
|
// 5 INVOKEVIRTUAL
|
||||||
// 2 CHECKCAST
|
// 2 CHECKCAST
|
||||||
// 2 _\$_findCachedViewById
|
// 2 _\$_findCachedViewById
|
||||||
// 1 findViewById
|
// 1 findViewById
|
||||||
+1
-1
@@ -9,7 +9,7 @@ public class MyFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1 GETSTATIC
|
// 1 GETSTATIC
|
||||||
// 5 INVOKEVIRTUAL
|
// 6 INVOKEVIRTUAL
|
||||||
// 2 CHECKCAST
|
// 2 CHECKCAST
|
||||||
// 2 _\$_findCachedViewById
|
// 2 _\$_findCachedViewById
|
||||||
// 1 findViewById
|
// 1 findViewById
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ public class MyActivity : Activity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1 GETSTATIC
|
// 1 GETSTATIC
|
||||||
// 4 INVOKEVIRTUAL
|
// 5 INVOKEVIRTUAL
|
||||||
// 3 CHECKCAST
|
// 3 CHECKCAST
|
||||||
// 1 _\$_findCachedViewById
|
// 1 _\$_findCachedViewById
|
||||||
// 2 findViewById
|
// 2 findViewById
|
||||||
+1
-1
@@ -33,7 +33,7 @@ public abstract class AbstractAndroidXml2KConversionTest : UsefulTestCase() {
|
|||||||
val jetCoreEnvironment = getEnvironment(path)
|
val jetCoreEnvironment = getEnvironment(path)
|
||||||
val parser = CliAndroidUIXmlProcessor(jetCoreEnvironment.getProject(), path + "AndroidManifest.xml", path + "/res")
|
val parser = CliAndroidUIXmlProcessor(jetCoreEnvironment.getProject(), path + "AndroidManifest.xml", path + "/res")
|
||||||
|
|
||||||
val actual = parser.parse()
|
val actual = parser.parse(false)
|
||||||
|
|
||||||
val layoutFiles = File(path).listFiles {
|
val layoutFiles = File(path).listFiles {
|
||||||
it.isFile() && it.name.startsWith("layout") && it.name.endsWith(".kt")
|
it.isFile() && it.name.startsWith("layout") && it.name.endsWith(".kt")
|
||||||
|
|||||||
+12
@@ -48,6 +48,18 @@ public class AndroidBytecodeShapeTestGenerated extends AbstractAndroidBytecodeSh
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("clearCache")
|
||||||
|
public void testClearCache() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("plugins/android-compiler-plugin/testData/codegen/bytecodeShape/clearCache/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("clearCacheBaseClass")
|
||||||
|
public void testClearCacheBaseClass() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("plugins/android-compiler-plugin/testData/codegen/bytecodeShape/clearCacheBaseClass/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("extensionFunctions")
|
@TestMetadata("extensionFunctions")
|
||||||
public void testExtensionFunctions() throws Exception {
|
public void testExtensionFunctions() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("plugins/android-compiler-plugin/testData/codegen/bytecodeShape/extensionFunctions/");
|
String fileName = JetTestUtils.navigationMetadata("plugins/android-compiler-plugin/testData/codegen/bytecodeShape/extensionFunctions/");
|
||||||
|
|||||||
+6
-6
@@ -78,12 +78,6 @@ public class AndroidFindUsagesTestGenerated extends AbstractAndroidFindUsagesTes
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("wrongIdFormat")
|
|
||||||
public void testWrongIdFormat() throws Exception {
|
|
||||||
String fileName = JetTestUtils.navigationMetadata("plugins/android-idea-plugin/testData/android/findUsages/wrongIdFormat/");
|
|
||||||
doTest(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@TestMetadata("simpleFragment")
|
@TestMetadata("simpleFragment")
|
||||||
public void testSimpleFragment() throws Exception {
|
public void testSimpleFragment() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("plugins/android-idea-plugin/testData/android/findUsages/simpleFragment/");
|
String fileName = JetTestUtils.navigationMetadata("plugins/android-idea-plugin/testData/android/findUsages/simpleFragment/");
|
||||||
@@ -95,4 +89,10 @@ public class AndroidFindUsagesTestGenerated extends AbstractAndroidFindUsagesTes
|
|||||||
String fileName = JetTestUtils.navigationMetadata("plugins/android-idea-plugin/testData/android/findUsages/simpleView/");
|
String fileName = JetTestUtils.navigationMetadata("plugins/android-idea-plugin/testData/android/findUsages/simpleView/");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("wrongIdFormat")
|
||||||
|
public void testWrongIdFormat() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("plugins/android-idea-plugin/testData/android/findUsages/wrongIdFormat/");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user