Support when mapping transformation on inlining
This commit is contained in:
+9
-15
@@ -52,8 +52,6 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
|||||||
|
|
||||||
private final Type oldObjectType;
|
private final Type oldObjectType;
|
||||||
|
|
||||||
private final Type newLambdaType;
|
|
||||||
|
|
||||||
private final boolean isSameModule;
|
private final boolean isSameModule;
|
||||||
|
|
||||||
private final Map<String, List<String>> fieldNames = new HashMap<String, List<String>>();
|
private final Map<String, List<String>> fieldNames = new HashMap<String, List<String>>();
|
||||||
@@ -61,8 +59,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
|||||||
public AnonymousObjectTransformer(
|
public AnonymousObjectTransformer(
|
||||||
@NotNull AnonymousObjectTransformationInfo transformationInfo,
|
@NotNull AnonymousObjectTransformationInfo transformationInfo,
|
||||||
@NotNull InliningContext inliningContext,
|
@NotNull InliningContext inliningContext,
|
||||||
boolean isSameModule,
|
boolean isSameModule
|
||||||
@NotNull Type newLambdaType
|
|
||||||
) {
|
) {
|
||||||
super(transformationInfo, inliningContext.state);
|
super(transformationInfo, inliningContext.state);
|
||||||
this.isSameModule = isSameModule;
|
this.isSameModule = isSameModule;
|
||||||
@@ -70,12 +67,11 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
|||||||
this.typeMapper = state.getTypeMapper();
|
this.typeMapper = state.getTypeMapper();
|
||||||
this.inliningContext = inliningContext;
|
this.inliningContext = inliningContext;
|
||||||
this.oldObjectType = Type.getObjectType(transformationInfo.getOldClassName());
|
this.oldObjectType = Type.getObjectType(transformationInfo.getOldClassName());
|
||||||
this.newLambdaType = newLambdaType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public InlineResult doTransform(@NotNull AnonymousObjectTransformationInfo transformationInfo, @NotNull FieldRemapper parentRemapper) {
|
public InlineResult doTransform(@NotNull FieldRemapper parentRemapper) {
|
||||||
final List<InnerClassNode> innerClassNodes = new ArrayList<InnerClassNode>();
|
final List<InnerClassNode> innerClassNodes = new ArrayList<InnerClassNode>();
|
||||||
ClassBuilder classBuilder = createRemappingClassBuilderViaFactory(inliningContext);
|
ClassBuilder classBuilder = createRemappingClassBuilderViaFactory(inliningContext);
|
||||||
final List<MethodNode> methodsToTransform = new ArrayList<MethodNode>();
|
final List<MethodNode> methodsToTransform = new ArrayList<MethodNode>();
|
||||||
@@ -161,7 +157,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
|||||||
for (MethodNode next : methodsToTransform) {
|
for (MethodNode next : methodsToTransform) {
|
||||||
MethodVisitor deferringVisitor = newMethod(classBuilder, next);
|
MethodVisitor deferringVisitor = newMethod(classBuilder, next);
|
||||||
InlineResult funResult =
|
InlineResult funResult =
|
||||||
inlineMethodAndUpdateGlobalResult(transformationInfo, parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false);
|
inlineMethodAndUpdateGlobalResult(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false);
|
||||||
|
|
||||||
Type returnType = Type.getReturnType(next.desc);
|
Type returnType = Type.getReturnType(next.desc);
|
||||||
if (!AsmUtil.isPrimitive(returnType)) {
|
if (!AsmUtil.isPrimitive(returnType)) {
|
||||||
@@ -178,7 +174,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
|||||||
method.visitEnd();
|
method.visitEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
generateConstructorAndFields(classBuilder, allCapturedParamBuilder, constructorParamBuilder, transformationInfo, parentRemapper, additionalFakeParams);
|
generateConstructorAndFields(classBuilder, allCapturedParamBuilder, constructorParamBuilder, parentRemapper, additionalFakeParams);
|
||||||
|
|
||||||
SourceMapper.Companion.flushToClassBuilder(sourceMapper, classBuilder);
|
SourceMapper.Companion.flushToClassBuilder(sourceMapper, classBuilder);
|
||||||
|
|
||||||
@@ -201,14 +197,13 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private InlineResult inlineMethodAndUpdateGlobalResult(
|
private InlineResult inlineMethodAndUpdateGlobalResult(
|
||||||
@NotNull AnonymousObjectTransformationInfo transformationInfo,
|
|
||||||
@NotNull FieldRemapper parentRemapper,
|
@NotNull FieldRemapper parentRemapper,
|
||||||
@NotNull MethodVisitor deferringVisitor,
|
@NotNull MethodVisitor deferringVisitor,
|
||||||
@NotNull MethodNode next,
|
@NotNull MethodNode next,
|
||||||
@NotNull ParametersBuilder allCapturedParamBuilder,
|
@NotNull ParametersBuilder allCapturedParamBuilder,
|
||||||
boolean isConstructor
|
boolean isConstructor
|
||||||
) {
|
) {
|
||||||
InlineResult funResult = inlineMethod(transformationInfo, parentRemapper, deferringVisitor, next, allCapturedParamBuilder, isConstructor);
|
InlineResult funResult = inlineMethod(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, isConstructor);
|
||||||
transformationResult.addAllClassesToRemove(funResult);
|
transformationResult.addAllClassesToRemove(funResult);
|
||||||
transformationResult.getReifiedTypeParametersUsages().mergeAll(funResult.getReifiedTypeParametersUsages());
|
transformationResult.getReifiedTypeParametersUsages().mergeAll(funResult.getReifiedTypeParametersUsages());
|
||||||
return funResult;
|
return funResult;
|
||||||
@@ -216,7 +211,6 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private InlineResult inlineMethod(
|
private InlineResult inlineMethod(
|
||||||
@NotNull AnonymousObjectTransformationInfo transformationInfo,
|
|
||||||
@NotNull FieldRemapper parentRemapper,
|
@NotNull FieldRemapper parentRemapper,
|
||||||
@NotNull MethodVisitor deferringVisitor,
|
@NotNull MethodVisitor deferringVisitor,
|
||||||
@NotNull MethodNode sourceNode,
|
@NotNull MethodNode sourceNode,
|
||||||
@@ -227,7 +221,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
|||||||
Parameters parameters = isConstructor ? capturedBuilder.buildParameters() : getMethodParametersWithCaptured(capturedBuilder, sourceNode);
|
Parameters parameters = isConstructor ? capturedBuilder.buildParameters() : getMethodParametersWithCaptured(capturedBuilder, sourceNode);
|
||||||
|
|
||||||
RegeneratedLambdaFieldRemapper remapper =
|
RegeneratedLambdaFieldRemapper remapper =
|
||||||
new RegeneratedLambdaFieldRemapper(oldObjectType.getInternalName(), newLambdaType.getInternalName(),
|
new RegeneratedLambdaFieldRemapper(oldObjectType.getInternalName(), transformationInfo.getNewClassName(),
|
||||||
parameters, transformationInfo.getCapturedLambdasToInline(),
|
parameters, transformationInfo.getCapturedLambdasToInline(),
|
||||||
parentRemapper, isConstructor);
|
parentRemapper, isConstructor);
|
||||||
|
|
||||||
@@ -257,7 +251,6 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
|||||||
@NotNull ClassBuilder classBuilder,
|
@NotNull ClassBuilder classBuilder,
|
||||||
@NotNull ParametersBuilder allCapturedBuilder,
|
@NotNull ParametersBuilder allCapturedBuilder,
|
||||||
@NotNull ParametersBuilder constructorInlineBuilder,
|
@NotNull ParametersBuilder constructorInlineBuilder,
|
||||||
@NotNull AnonymousObjectTransformationInfo transformationInfo,
|
|
||||||
@NotNull FieldRemapper parentRemapper,
|
@NotNull FieldRemapper parentRemapper,
|
||||||
@NotNull List<CapturedParamInfo> constructorAdditionalFakeParams
|
@NotNull List<CapturedParamInfo> constructorAdditionalFakeParams
|
||||||
) {
|
) {
|
||||||
@@ -293,7 +286,8 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
|||||||
|
|
||||||
//initialize captured fields
|
//initialize captured fields
|
||||||
List<NewJavaField> newFieldsWithSkipped = TransformationUtilsKt.getNewFieldsToGenerate(allCapturedBuilder.listCaptured());
|
List<NewJavaField> newFieldsWithSkipped = TransformationUtilsKt.getNewFieldsToGenerate(allCapturedBuilder.listCaptured());
|
||||||
List<FieldInfo> fieldInfoWithSkipped = TransformationUtilsKt.transformToFieldInfo(newLambdaType, newFieldsWithSkipped);
|
List<FieldInfo> fieldInfoWithSkipped = TransformationUtilsKt.transformToFieldInfo(
|
||||||
|
Type.getObjectType(transformationInfo.getNewClassName()), newFieldsWithSkipped);
|
||||||
|
|
||||||
int paramIndex = 0;
|
int paramIndex = 0;
|
||||||
InstructionAdapter capturedFieldInitializer = new InstructionAdapter(constructorVisitor);
|
InstructionAdapter capturedFieldInitializer = new InstructionAdapter(constructorVisitor);
|
||||||
@@ -323,7 +317,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inlineMethodAndUpdateGlobalResult(transformationInfo, parentRemapper, capturedFieldInitializer, constructor, constructorInlineBuilder, true);
|
inlineMethodAndUpdateGlobalResult(parentRemapper, capturedFieldInitializer, constructor, constructorInlineBuilder, true);
|
||||||
constructorVisitor.visitEnd();
|
constructorVisitor.visitEnd();
|
||||||
AsmUtil.genClosureFields(TransformationUtilsKt.toNameTypePair(TransformationUtilsKt.filterSkipped(newFieldsWithSkipped)), classBuilder);
|
AsmUtil.genClosureFields(TransformationUtilsKt.toNameTypePair(TransformationUtilsKt.filterSkipped(newFieldsWithSkipped)), classBuilder);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt;
|
|||||||
import org.jetbrains.kotlin.codegen.optimization.common.UtilKt;
|
import org.jetbrains.kotlin.codegen.optimization.common.UtilKt;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||||
|
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
|
||||||
import org.jetbrains.kotlin.descriptors.*;
|
import org.jetbrains.kotlin.descriptors.*;
|
||||||
import org.jetbrains.kotlin.fileClasses.FileClasses;
|
import org.jetbrains.kotlin.fileClasses.FileClasses;
|
||||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
|
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
|
||||||
@@ -244,6 +245,10 @@ public class InlineCodegenUtil {
|
|||||||
return "<init>".equals(methodName) && isAnonymousClass(internalName);
|
return "<init>".equals(methodName) && isAnonymousClass(internalName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isWhenMappingAccess(@NotNull String internalName, @NotNull String fieldName) {
|
||||||
|
return fieldName.startsWith(WhenByEnumsMapping.MAPPING_ARRAY_FIELD_PREFIX) && internalName.endsWith(WhenByEnumsMapping.MAPPINGS_CLASS_NAME_POSTFIX);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isAnonymousSingletonLoad(@NotNull String internalName, @NotNull String fieldName) {
|
public static boolean isAnonymousSingletonLoad(@NotNull String internalName, @NotNull String fieldName) {
|
||||||
return JvmAbi.INSTANCE_FIELD.equals(fieldName) && isAnonymousClass(internalName);
|
return JvmAbi.INSTANCE_FIELD.equals(fieldName) && isAnonymousClass(internalName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public class MethodInliner {
|
|||||||
private final List<InvokeCall> invokeCalls = new ArrayList<InvokeCall>();
|
private final List<InvokeCall> invokeCalls = new ArrayList<InvokeCall>();
|
||||||
|
|
||||||
//keeps order
|
//keeps order
|
||||||
private final List<AnonymousObjectTransformationInfo> anonymousObjectRegenerations = new ArrayList<AnonymousObjectTransformationInfo>();
|
private final List<TransformationInfo> transformations = new ArrayList<TransformationInfo>();
|
||||||
//current state
|
//current state
|
||||||
private final Map<String, String> currentTypeMapping = new HashMap<String, String>();
|
private final Map<String, String> currentTypeMapping = new HashMap<String, String>();
|
||||||
|
|
||||||
@@ -160,7 +160,7 @@ public class MethodInliner {
|
|||||||
|
|
||||||
final MethodNode resultNode = new MethodNode(node.access, node.name, node.desc, node.signature, null);
|
final MethodNode resultNode = new MethodNode(node.access, node.name, node.desc, node.signature, null);
|
||||||
|
|
||||||
final Iterator<AnonymousObjectTransformationInfo> iterator = anonymousObjectRegenerations.iterator();
|
final Iterator<TransformationInfo> iterator = transformations.iterator();
|
||||||
|
|
||||||
final TypeRemapper remapper = TypeRemapper.createFrom(currentTypeMapping);
|
final TypeRemapper remapper = TypeRemapper.createFrom(currentTypeMapping);
|
||||||
final RemappingMethodAdapter remappingMethodAdapter = new RemappingMethodAdapter(
|
final RemappingMethodAdapter remappingMethodAdapter = new RemappingMethodAdapter(
|
||||||
@@ -173,7 +173,7 @@ public class MethodInliner {
|
|||||||
final int markerShift = InlineCodegenUtil.calcMarkerShift(parameters, node);
|
final int markerShift = InlineCodegenUtil.calcMarkerShift(parameters, node);
|
||||||
InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.getArgsSizeOnStack(), sourceMapper) {
|
InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.getArgsSizeOnStack(), sourceMapper) {
|
||||||
|
|
||||||
private AnonymousObjectTransformationInfo transformationInfo;
|
private TransformationInfo transformationInfo;
|
||||||
private void handleAnonymousObjectRegeneration() {
|
private void handleAnonymousObjectRegeneration() {
|
||||||
transformationInfo = iterator.next();
|
transformationInfo = iterator.next();
|
||||||
|
|
||||||
@@ -182,17 +182,18 @@ public class MethodInliner {
|
|||||||
String oldClassName = transformationInfo.getOldClassName();
|
String oldClassName = transformationInfo.getOldClassName();
|
||||||
String newClassName = transformationInfo.getNewClassName();
|
String newClassName = transformationInfo.getNewClassName();
|
||||||
remapper.addMapping(oldClassName, newClassName);
|
remapper.addMapping(oldClassName, newClassName);
|
||||||
AnonymousObjectTransformer transformer =
|
|
||||||
new AnonymousObjectTransformer((AnonymousObjectTransformationInfo) transformationInfo,
|
|
||||||
inliningContext
|
|
||||||
.subInlineWithClassRegeneration(
|
|
||||||
inliningContext.nameGenerator,
|
|
||||||
currentTypeMapping,
|
|
||||||
inlineCallSiteInfo),
|
|
||||||
isSameModule, Type.getObjectType(newClassName)
|
|
||||||
);
|
|
||||||
|
|
||||||
InlineResult transformResult = transformer.doTransform(transformationInfo, nodeRemapper);
|
InliningContext childInliningContext = inliningContext.subInlineWithClassRegeneration(
|
||||||
|
inliningContext.nameGenerator,
|
||||||
|
currentTypeMapping,
|
||||||
|
inlineCallSiteInfo
|
||||||
|
);
|
||||||
|
ObjectTransformer transformer = transformationInfo.createTransformer(
|
||||||
|
childInliningContext,
|
||||||
|
isSameModule
|
||||||
|
);
|
||||||
|
|
||||||
|
InlineResult transformResult = transformer.doTransform(nodeRemapper);
|
||||||
result.addAllClassesToRemove(transformResult);
|
result.addAllClassesToRemove(transformResult);
|
||||||
result.addChangedType(oldClassName, newClassName);
|
result.addChangedType(oldClassName, newClassName);
|
||||||
|
|
||||||
@@ -271,17 +272,18 @@ public class MethodInliner {
|
|||||||
else if (isAnonymousConstructorCall(owner, name)) { //TODO add method
|
else if (isAnonymousConstructorCall(owner, name)) { //TODO add method
|
||||||
assert transformationInfo != null : "<init> call not corresponds to new call" + owner + " " + name;
|
assert transformationInfo != null : "<init> call not corresponds to new call" + owner + " " + name;
|
||||||
if (transformationInfo.shouldRegenerate(isSameModule)) {
|
if (transformationInfo.shouldRegenerate(isSameModule)) {
|
||||||
//put additional captured parameters on stack
|
if (transformationInfo instanceof AnonymousObjectTransformationInfo) {
|
||||||
for (CapturedParamDesc capturedParamDesc : transformationInfo.getAllRecapturedParameters()) {
|
//put additional captured parameters on stack
|
||||||
visitFieldInsn(Opcodes.GETSTATIC, capturedParamDesc.getContainingLambdaName(),
|
for (CapturedParamDesc capturedParamDesc : ((AnonymousObjectTransformationInfo) transformationInfo).getAllRecapturedParameters()) {
|
||||||
"$$$" + capturedParamDesc.getFieldName(), capturedParamDesc.getType().getDescriptor());
|
visitFieldInsn(Opcodes.GETSTATIC, capturedParamDesc.getContainingLambdaName(),
|
||||||
|
"$$$" + capturedParamDesc.getFieldName(), capturedParamDesc.getType().getDescriptor());
|
||||||
|
}
|
||||||
|
super.visitMethodInsn(opcode, transformationInfo.getNewClassName(), name, ((AnonymousObjectTransformationInfo) transformationInfo).getNewConstructorDescriptor(), itf);
|
||||||
}
|
}
|
||||||
String newInternalName = transformationInfo.getNewClassName();
|
|
||||||
super.visitMethodInsn(opcode, newInternalName, name, transformationInfo.getNewConstructorDescriptor(), itf);
|
|
||||||
|
|
||||||
//TODO: add new inner class also for other contexts
|
//TODO: add new inner class also for other contexts
|
||||||
if (inliningContext.getParent() instanceof RegeneratedClassContext) {
|
if (inliningContext.getParent() instanceof RegeneratedClassContext) {
|
||||||
inliningContext.getParent().typeRemapper.addAdditionalMappings(transformationInfo.getOldClassName(), newInternalName);
|
inliningContext.getParent().typeRemapper.addAdditionalMappings(transformationInfo.getOldClassName(), transformationInfo.getNewClassName());
|
||||||
}
|
}
|
||||||
|
|
||||||
transformationInfo = null;
|
transformationInfo = null;
|
||||||
@@ -299,7 +301,7 @@ public class MethodInliner {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitFieldInsn(int opcode, @NotNull String owner, @NotNull String name, @NotNull String desc) {
|
public void visitFieldInsn(int opcode, @NotNull String owner, @NotNull String name, @NotNull String desc) {
|
||||||
if (opcode == Opcodes.GETSTATIC && isAnonymousSingletonLoad(owner, name)) {
|
if (opcode == Opcodes.GETSTATIC && (isAnonymousSingletonLoad(owner, name) || isWhenMappingAccess(owner, name))) {
|
||||||
handleAnonymousObjectRegeneration();
|
handleAnonymousObjectRegeneration();
|
||||||
}
|
}
|
||||||
super.visitFieldInsn(opcode, owner, name, desc);
|
super.visitFieldInsn(opcode, owner, name, desc);
|
||||||
@@ -486,7 +488,7 @@ public class MethodInliner {
|
|||||||
offset += i == 0 ? 1 : argTypes[i - 1].getSize();
|
offset += i == 0 ? 1 : argTypes[i - 1].getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
anonymousObjectRegenerations.add(
|
transformations.add(
|
||||||
buildConstructorInvocation(
|
buildConstructorInvocation(
|
||||||
owner, desc, lambdaMapping, awaitClassReification
|
owner, desc, lambdaMapping, awaitClassReification
|
||||||
)
|
)
|
||||||
@@ -496,16 +498,23 @@ public class MethodInliner {
|
|||||||
}
|
}
|
||||||
else if (cur.getOpcode() == Opcodes.GETSTATIC) {
|
else if (cur.getOpcode() == Opcodes.GETSTATIC) {
|
||||||
FieldInsnNode fieldInsnNode = (FieldInsnNode) cur;
|
FieldInsnNode fieldInsnNode = (FieldInsnNode) cur;
|
||||||
String owner = fieldInsnNode.owner;
|
String className = fieldInsnNode.owner;
|
||||||
if (isAnonymousSingletonLoad(owner, fieldInsnNode.name)) {
|
if (isAnonymousSingletonLoad(className, fieldInsnNode.name)) {
|
||||||
anonymousObjectRegenerations.add(
|
transformations.add(
|
||||||
new AnonymousObjectTransformationInfo(
|
new AnonymousObjectTransformationInfo(
|
||||||
owner, awaitClassReification, isAlreadyRegenerated(owner), true,
|
className, awaitClassReification, isAlreadyRegenerated(className), true,
|
||||||
inliningContext.nameGenerator
|
inliningContext.nameGenerator
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
awaitClassReification = false;
|
awaitClassReification = false;
|
||||||
}
|
}
|
||||||
|
else if (isWhenMappingAccess(className, fieldInsnNode.name)) {
|
||||||
|
transformations.add(
|
||||||
|
new WhenMappingTransformationInfo(
|
||||||
|
className, inliningContext.nameGenerator, isAlreadyRegenerated(className), fieldInsnNode)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AbstractInsnNode prevNode = cur;
|
AbstractInsnNode prevNode = cur;
|
||||||
|
|||||||
@@ -17,15 +17,19 @@
|
|||||||
package org.jetbrains.kotlin.codegen.inline
|
package org.jetbrains.kotlin.codegen.inline
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
import org.jetbrains.org.objectweb.asm.*
|
||||||
import org.jetbrains.org.objectweb.asm.ClassVisitor
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
abstract class ObjectTransformer<T : TransformationInfo>(val transformationInfo: T, val state: GenerationState) {
|
abstract class ObjectTransformer<T : TransformationInfo>(@JvmField val transformationInfo: T, val state: GenerationState) {
|
||||||
|
|
||||||
abstract fun doTransform(info: T, parentRemapper: FieldRemapper): InlineResult
|
abstract fun doTransform(parentRemapper: FieldRemapper): InlineResult
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val transformationResult = InlineResult.create()
|
val transformationResult = InlineResult.create()
|
||||||
@@ -51,23 +55,72 @@ abstract class ObjectTransformer<T : TransformationInfo>(val transformationInfo:
|
|||||||
|
|
||||||
class WhenMappingTransformer(
|
class WhenMappingTransformer(
|
||||||
whenObjectRegenerationInfo: WhenMappingTransformationInfo,
|
whenObjectRegenerationInfo: WhenMappingTransformationInfo,
|
||||||
state: GenerationState,
|
|
||||||
val inliningContext: InliningContext
|
val inliningContext: InliningContext
|
||||||
) : ObjectTransformer<WhenMappingTransformationInfo>(whenObjectRegenerationInfo, state) {
|
) : ObjectTransformer<WhenMappingTransformationInfo>(whenObjectRegenerationInfo, inliningContext.state) {
|
||||||
|
|
||||||
override fun doTransform(info: WhenMappingTransformationInfo, parentRemapper: FieldRemapper): InlineResult {
|
override fun doTransform(parentRemapper: FieldRemapper): InlineResult {
|
||||||
val classReader = createClassReader()
|
val classReader = createClassReader()
|
||||||
//TODO add additional check that class is when mapping
|
//TODO add additional check that class is when mapping
|
||||||
|
|
||||||
val classBuilder = createRemappingClassBuilderViaFactory(inliningContext)
|
val classBuilder = createRemappingClassBuilderViaFactory(inliningContext)
|
||||||
|
/*MAPPING File could contains mappings for several enum classes, we should filter one*/
|
||||||
|
val methodNodes = arrayListOf<MethodNode>()
|
||||||
|
val fieldNode = transformationInfo.fieldNode
|
||||||
classReader.accept(object : ClassVisitor(InlineCodegenUtil.API, classBuilder.visitor) {
|
classReader.accept(object : ClassVisitor(InlineCodegenUtil.API, classBuilder.visitor) {
|
||||||
override fun visit(version: Int, access: Int, name: String, signature: String, superName: String, interfaces: Array<String>) {
|
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array<String>?) {
|
||||||
InlineCodegenUtil.assertVersionNotGreaterThanJava6(version, name)
|
InlineCodegenUtil.assertVersionNotGreaterThanJava6(version, name)
|
||||||
super.visit(version, access, name, signature, superName, interfaces)
|
super.visit(version, access, name, signature, superName, interfaces)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
|
||||||
|
return if (name.equals(fieldNode.name)) {
|
||||||
|
super.visitField(access, name, desc, signature, value)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitMethod(access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
|
||||||
|
val methodNode = MethodNode(access, name, desc, signature, exceptions)
|
||||||
|
methodNodes.add(methodNode)
|
||||||
|
return methodNode
|
||||||
|
}
|
||||||
}, ClassReader.SKIP_FRAMES)
|
}, ClassReader.SKIP_FRAMES)
|
||||||
|
|
||||||
|
assert(methodNodes.size == 1, { "When mapping ${fieldNode.owner} class should contain only one method but: " + methodNodes.joinToString { it.name } })
|
||||||
|
|
||||||
|
var transformedNode = cutOtherMappings(methodNodes.first())
|
||||||
|
val result = classBuilder.visitor.visitMethod(transformedNode.access, transformedNode.name, transformedNode.desc, transformedNode.signature, transformedNode.exceptions.toTypedArray())
|
||||||
|
transformedNode.accept(result)
|
||||||
|
|
||||||
return transformationResult
|
return transformationResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun cutOtherMappings(node: MethodNode): MethodNode {
|
||||||
|
val myArrayAccess = InsnSequence(node.instructions).first {
|
||||||
|
it is FieldInsnNode && it.name.equals(transformationInfo.fieldNode.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val myValuesAccess = generateSequence(myArrayAccess) { it.previous }.first {
|
||||||
|
isValues(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
val nextValuesOrEnd = generateSequence(myArrayAccess) { it.next }.first {
|
||||||
|
isValues(it) || it.opcode == Opcodes.RETURN
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = MethodNode(node.access, node.name, node.desc, node.signature, node.exceptions.toTypedArray())
|
||||||
|
InsnSequence(myValuesAccess, nextValuesOrEnd).forEach { it.accept(result) }
|
||||||
|
result.visitInsn(Opcodes.RETURN)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isValues(node: AbstractInsnNode) = node is MethodInsnNode &&
|
||||||
|
node.opcode == Opcodes.INVOKESTATIC &&
|
||||||
|
node.name == "values" &&
|
||||||
|
node.desc == "()[" + Type.getObjectType(node.owner).descriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,73 +16,91 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.inline
|
package org.jetbrains.kotlin.codegen.inline
|
||||||
|
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
interface TransformationInfo {
|
interface TransformationInfo {
|
||||||
|
|
||||||
val oldClassName: String
|
val oldClassName: String
|
||||||
|
|
||||||
val newClassName: String
|
val newClassName: String
|
||||||
|
|
||||||
fun shouldRegenerate(sameModule: Boolean): Boolean
|
fun shouldRegenerate(sameModule: Boolean): Boolean
|
||||||
|
|
||||||
fun canRemoveAfterTransformation(): Boolean
|
fun canRemoveAfterTransformation(): Boolean
|
||||||
|
|
||||||
|
fun createTransformer(
|
||||||
|
inliningContext: InliningContext,
|
||||||
|
sameModule: Boolean
|
||||||
|
): ObjectTransformer<*>
|
||||||
}
|
}
|
||||||
|
|
||||||
class WhenMappingTransformationInfo(override val oldClassName: String, val nameGenerator: NameGenerator) : TransformationInfo {
|
class WhenMappingTransformationInfo(
|
||||||
|
override val oldClassName: String,
|
||||||
|
val nameGenerator: NameGenerator,
|
||||||
|
val alreadyRegenerated: Boolean,
|
||||||
|
val fieldNode: FieldInsnNode
|
||||||
|
) : TransformationInfo {
|
||||||
|
|
||||||
override val newClassName by lazy {
|
override val newClassName by lazy {
|
||||||
nameGenerator.genLambdaClassName() + oldClassName.substringAfterLast("/")
|
nameGenerator.genLambdaClassName() + oldClassName.substringAfterLast("/")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun shouldRegenerate(sameModule: Boolean): Boolean {
|
override fun shouldRegenerate(sameModule: Boolean): Boolean {
|
||||||
throw UnsupportedOperationException()
|
return !alreadyRegenerated && !sameModule
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun canRemoveAfterTransformation(): Boolean {
|
override fun canRemoveAfterTransformation(): Boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun createTransformer(inliningContext: InliningContext, sameModule: Boolean): ObjectTransformer<*> {
|
||||||
|
return WhenMappingTransformer(this, inliningContext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AnonymousObjectTransformationInfo internal constructor(
|
class AnonymousObjectTransformationInfo internal constructor(
|
||||||
override val oldClassName: String,
|
override val oldClassName: String,
|
||||||
private val needReification: Boolean,
|
private val needReification: Boolean,
|
||||||
val lambdasToInline: Map<Int, LambdaInfo>,
|
val lambdasToInline: Map<Int, LambdaInfo>,
|
||||||
private val capturedOuterRegenerated: Boolean,
|
private val capturedOuterRegenerated: Boolean,
|
||||||
private val alreadyRegenerated: Boolean,
|
private val alreadyRegenerated: Boolean,
|
||||||
val constructorDesc: String?,
|
val constructorDesc: String?,
|
||||||
private val isStaticOrigin: Boolean,
|
private val isStaticOrigin: Boolean,
|
||||||
private val nameGenerator: NameGenerator) : TransformationInfo {
|
nameGenerator: NameGenerator) : TransformationInfo {
|
||||||
|
|
||||||
override val newClassName: String by lazy {
|
override val newClassName: String by lazy {
|
||||||
nameGenerator.genLambdaClassName()
|
nameGenerator.genLambdaClassName()
|
||||||
}
|
}
|
||||||
|
|
||||||
var newConstructorDescriptor: String? = null
|
lateinit var newConstructorDescriptor: String
|
||||||
|
|
||||||
var allRecapturedParameters: List<CapturedParamDesc>? = null
|
lateinit var allRecapturedParameters: List<CapturedParamDesc>
|
||||||
|
|
||||||
var capturedLambdasToInline: Map<String, LambdaInfo>? = null
|
lateinit var capturedLambdasToInline: Map<String, LambdaInfo>
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
ownerInternalName: String,
|
ownerInternalName: String,
|
||||||
needReification: Boolean,
|
needReification: Boolean,
|
||||||
alreadyRegenerated: Boolean,
|
alreadyRegenerated: Boolean,
|
||||||
isStaticOrigin: Boolean,
|
isStaticOrigin: Boolean,
|
||||||
nameGenerator: NameGenerator
|
nameGenerator: NameGenerator
|
||||||
) : this(
|
) : this(
|
||||||
ownerInternalName, needReification,
|
ownerInternalName, needReification,
|
||||||
HashMap<Int, LambdaInfo>(), false, alreadyRegenerated, null, isStaticOrigin, nameGenerator) {
|
HashMap<Int, LambdaInfo>(), false, alreadyRegenerated, null, isStaticOrigin, nameGenerator) {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun shouldRegenerate(sameModule: Boolean): Boolean {
|
override fun shouldRegenerate(sameModule: Boolean): Boolean {
|
||||||
return !alreadyRegenerated && (!lambdasToInline.isEmpty() || !sameModule || capturedOuterRegenerated || needReification)
|
return !alreadyRegenerated && (!lambdasToInline.isEmpty() || !sameModule || capturedOuterRegenerated || needReification)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun canRemoveAfterTransformation(): Boolean {
|
override fun canRemoveAfterTransformation(): Boolean {
|
||||||
// Note: It is unsafe to remove anonymous class that is referenced by GETSTATIC within lambda
|
// Note: It is unsafe to remove anonymous class that is referenced by GETSTATIC within lambda
|
||||||
// because it can be local function from outer scope
|
// because it can be local function from outer scope
|
||||||
return !isStaticOrigin
|
return !isStaticOrigin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun createTransformer(inliningContext: InliningContext, sameModule: Boolean): ObjectTransformer<*> {
|
||||||
|
return AnonymousObjectTransformer(this, inliningContext, sameModule);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class WhenByEnumsMapping {
|
public class WhenByEnumsMapping {
|
||||||
private static final String MAPPING_ARRAY_FIELD_PREFIX = "$EnumSwitchMapping$";
|
public static final String MAPPING_ARRAY_FIELD_PREFIX = "$EnumSwitchMapping$";
|
||||||
private static final String MAPPINGS_CLASS_NAME_POSTFIX = "$WhenMappings";
|
public static final String MAPPINGS_CLASS_NAME_POSTFIX = "$WhenMappings";
|
||||||
|
|
||||||
private final Map<EnumValue, Integer> map = new LinkedHashMap<EnumValue, Integer>();
|
private final Map<EnumValue, Integer> map = new LinkedHashMap<EnumValue, Integer>();
|
||||||
private final ClassDescriptor enumClassDescriptor;
|
private final ClassDescriptor enumClassDescriptor;
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// FILE: 1.kt
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
enum class X {
|
||||||
|
A,
|
||||||
|
B
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun test(x: X, s: (X) -> String): String {
|
||||||
|
return s(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: 2.kt
|
||||||
|
import test.*
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
return test(X.A) {
|
||||||
|
when(it) {
|
||||||
|
X.A-> "O"
|
||||||
|
X.B-> "K"
|
||||||
|
}
|
||||||
|
} + test(X.B) {
|
||||||
|
when(it) {
|
||||||
|
X.A-> "O"
|
||||||
|
X.B-> "K"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// FILE: 1.kt
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
enum class X {
|
||||||
|
A,
|
||||||
|
B
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun test(e: X): String {
|
||||||
|
return when(e) {
|
||||||
|
X.A-> "O"
|
||||||
|
X.B-> "K"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: 2.kt
|
||||||
|
import test.*
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
return test(X.A) + test(X.B)
|
||||||
|
}
|
||||||
+34
@@ -0,0 +1,34 @@
|
|||||||
|
// FILE: 1.kt
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
enum class X {
|
||||||
|
A,
|
||||||
|
B
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Y {
|
||||||
|
A,
|
||||||
|
B
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun test(e: X): String {
|
||||||
|
return when(e) {
|
||||||
|
X.A-> "O"
|
||||||
|
X.B-> "K"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun funForAdditionalMappingArrayInMappingFile(e: Y): String {
|
||||||
|
return when(e) {
|
||||||
|
Y.A-> "O"
|
||||||
|
Y.B-> "K"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: 2.kt
|
||||||
|
import test.*
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
return test(X.A) + test(X.B)
|
||||||
|
}
|
||||||
Vendored
+34
@@ -0,0 +1,34 @@
|
|||||||
|
// FILE: 1.kt
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
enum class X {
|
||||||
|
A,
|
||||||
|
B
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Y {
|
||||||
|
A,
|
||||||
|
B
|
||||||
|
}
|
||||||
|
|
||||||
|
fun funForAdditionalMappingArrayInMappingFile(e: Y): String {
|
||||||
|
return when(e) {
|
||||||
|
Y.A-> "O"
|
||||||
|
Y.B-> "K"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun test(e: X): String {
|
||||||
|
return when(e) {
|
||||||
|
X.A-> "O"
|
||||||
|
X.B-> "K"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: 2.kt
|
||||||
|
import test.*
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
return test(X.A) + test(X.B)
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
enum class X {
|
||||||
|
A,
|
||||||
|
B
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun test(x: X, s: (X) -> String): String {
|
||||||
|
return s(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
return test(X.A) {
|
||||||
|
when(it) {
|
||||||
|
X.A-> "O"
|
||||||
|
X.B-> "K"
|
||||||
|
}
|
||||||
|
} + test(X.B) {
|
||||||
|
when(it) {
|
||||||
|
X.A-> "O"
|
||||||
|
X.B-> "K"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no additional mappings cause when in inline lambda (same module)
|
||||||
|
// 1 class test/.*\$WhenMappings
|
||||||
@@ -169,6 +169,39 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class EnumWhen extends AbstractBlackBoxInlineCodegenTest {
|
||||||
|
public void testAllFilesPresentInEnumWhen() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/anonymousObject/enumWhen"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callSite.kt")
|
||||||
|
public void testCallSite() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen/callSite.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("declSite.kt")
|
||||||
|
public void testDeclSite() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen/declSite.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("declSiteSeveralMappings.kt")
|
||||||
|
public void testDeclSiteSeveralMappings() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen/declSiteSeveralMappings.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("declSiteSeveralMappingsDifOrder.kt")
|
||||||
|
public void testDeclSiteSeveralMappingsDifOrder() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen/declSiteSeveralMappingsDifOrder.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/properRecapturing")
|
@TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/properRecapturing")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -888,6 +888,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/splitedExceptionTable.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/splitedExceptionTable.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("whenMappingOnCallSite.kt")
|
||||||
|
public void testWhenMappingOnCallSite() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/whenMappingOnCallSite.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/bytecodeText/interfaces")
|
@TestMetadata("compiler/testData/codegen/bytecodeText/interfaces")
|
||||||
|
|||||||
+33
@@ -169,6 +169,39 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class EnumWhen extends AbstractCompileKotlinAgainstInlineKotlinTest {
|
||||||
|
public void testAllFilesPresentInEnumWhen() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/anonymousObject/enumWhen"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("callSite.kt")
|
||||||
|
public void testCallSite() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen/callSite.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("declSite.kt")
|
||||||
|
public void testDeclSite() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen/declSite.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("declSiteSeveralMappings.kt")
|
||||||
|
public void testDeclSiteSeveralMappings() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen/declSiteSeveralMappings.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("declSiteSeveralMappingsDifOrder.kt")
|
||||||
|
public void testDeclSiteSeveralMappingsDifOrder() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/anonymousObject/enumWhen/declSiteSeveralMappingsDifOrder.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/properRecapturing")
|
@TestMetadata("compiler/testData/codegen/boxInline/anonymousObject/properRecapturing")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user