Support when mapping transformation on inlining

This commit is contained in:
Michael Bogdanov
2016-03-11 12:08:23 +03:00
parent b88260b4f9
commit bec6126b06
14 changed files with 396 additions and 98 deletions
@@ -52,8 +52,6 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
private final Type oldObjectType;
private final Type newLambdaType;
private final boolean isSameModule;
private final Map<String, List<String>> fieldNames = new HashMap<String, List<String>>();
@@ -61,8 +59,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
public AnonymousObjectTransformer(
@NotNull AnonymousObjectTransformationInfo transformationInfo,
@NotNull InliningContext inliningContext,
boolean isSameModule,
@NotNull Type newLambdaType
boolean isSameModule
) {
super(transformationInfo, inliningContext.state);
this.isSameModule = isSameModule;
@@ -70,12 +67,11 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
this.typeMapper = state.getTypeMapper();
this.inliningContext = inliningContext;
this.oldObjectType = Type.getObjectType(transformationInfo.getOldClassName());
this.newLambdaType = newLambdaType;
}
@Override
@NotNull
public InlineResult doTransform(@NotNull AnonymousObjectTransformationInfo transformationInfo, @NotNull FieldRemapper parentRemapper) {
public InlineResult doTransform(@NotNull FieldRemapper parentRemapper) {
final List<InnerClassNode> innerClassNodes = new ArrayList<InnerClassNode>();
ClassBuilder classBuilder = createRemappingClassBuilderViaFactory(inliningContext);
final List<MethodNode> methodsToTransform = new ArrayList<MethodNode>();
@@ -161,7 +157,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
for (MethodNode next : methodsToTransform) {
MethodVisitor deferringVisitor = newMethod(classBuilder, next);
InlineResult funResult =
inlineMethodAndUpdateGlobalResult(transformationInfo, parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false);
inlineMethodAndUpdateGlobalResult(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, false);
Type returnType = Type.getReturnType(next.desc);
if (!AsmUtil.isPrimitive(returnType)) {
@@ -178,7 +174,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
method.visitEnd();
}
generateConstructorAndFields(classBuilder, allCapturedParamBuilder, constructorParamBuilder, transformationInfo, parentRemapper, additionalFakeParams);
generateConstructorAndFields(classBuilder, allCapturedParamBuilder, constructorParamBuilder, parentRemapper, additionalFakeParams);
SourceMapper.Companion.flushToClassBuilder(sourceMapper, classBuilder);
@@ -201,14 +197,13 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
@NotNull
private InlineResult inlineMethodAndUpdateGlobalResult(
@NotNull AnonymousObjectTransformationInfo transformationInfo,
@NotNull FieldRemapper parentRemapper,
@NotNull MethodVisitor deferringVisitor,
@NotNull MethodNode next,
@NotNull ParametersBuilder allCapturedParamBuilder,
boolean isConstructor
) {
InlineResult funResult = inlineMethod(transformationInfo, parentRemapper, deferringVisitor, next, allCapturedParamBuilder, isConstructor);
InlineResult funResult = inlineMethod(parentRemapper, deferringVisitor, next, allCapturedParamBuilder, isConstructor);
transformationResult.addAllClassesToRemove(funResult);
transformationResult.getReifiedTypeParametersUsages().mergeAll(funResult.getReifiedTypeParametersUsages());
return funResult;
@@ -216,7 +211,6 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
@NotNull
private InlineResult inlineMethod(
@NotNull AnonymousObjectTransformationInfo transformationInfo,
@NotNull FieldRemapper parentRemapper,
@NotNull MethodVisitor deferringVisitor,
@NotNull MethodNode sourceNode,
@@ -227,7 +221,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
Parameters parameters = isConstructor ? capturedBuilder.buildParameters() : getMethodParametersWithCaptured(capturedBuilder, sourceNode);
RegeneratedLambdaFieldRemapper remapper =
new RegeneratedLambdaFieldRemapper(oldObjectType.getInternalName(), newLambdaType.getInternalName(),
new RegeneratedLambdaFieldRemapper(oldObjectType.getInternalName(), transformationInfo.getNewClassName(),
parameters, transformationInfo.getCapturedLambdasToInline(),
parentRemapper, isConstructor);
@@ -257,7 +251,6 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
@NotNull ClassBuilder classBuilder,
@NotNull ParametersBuilder allCapturedBuilder,
@NotNull ParametersBuilder constructorInlineBuilder,
@NotNull AnonymousObjectTransformationInfo transformationInfo,
@NotNull FieldRemapper parentRemapper,
@NotNull List<CapturedParamInfo> constructorAdditionalFakeParams
) {
@@ -293,7 +286,8 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
//initialize captured fields
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;
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();
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.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.fileClasses.FileClasses;
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
@@ -244,6 +245,10 @@ public class InlineCodegenUtil {
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) {
return JvmAbi.INSTANCE_FIELD.equals(fieldName) && isAnonymousClass(internalName);
}
@@ -63,7 +63,7 @@ public class MethodInliner {
private final List<InvokeCall> invokeCalls = new ArrayList<InvokeCall>();
//keeps order
private final List<AnonymousObjectTransformationInfo> anonymousObjectRegenerations = new ArrayList<AnonymousObjectTransformationInfo>();
private final List<TransformationInfo> transformations = new ArrayList<TransformationInfo>();
//current state
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 Iterator<AnonymousObjectTransformationInfo> iterator = anonymousObjectRegenerations.iterator();
final Iterator<TransformationInfo> iterator = transformations.iterator();
final TypeRemapper remapper = TypeRemapper.createFrom(currentTypeMapping);
final RemappingMethodAdapter remappingMethodAdapter = new RemappingMethodAdapter(
@@ -173,7 +173,7 @@ public class MethodInliner {
final int markerShift = InlineCodegenUtil.calcMarkerShift(parameters, node);
InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.getArgsSizeOnStack(), sourceMapper) {
private AnonymousObjectTransformationInfo transformationInfo;
private TransformationInfo transformationInfo;
private void handleAnonymousObjectRegeneration() {
transformationInfo = iterator.next();
@@ -182,17 +182,18 @@ public class MethodInliner {
String oldClassName = transformationInfo.getOldClassName();
String newClassName = transformationInfo.getNewClassName();
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.addChangedType(oldClassName, newClassName);
@@ -271,17 +272,18 @@ public class MethodInliner {
else if (isAnonymousConstructorCall(owner, name)) { //TODO add method
assert transformationInfo != null : "<init> call not corresponds to new call" + owner + " " + name;
if (transformationInfo.shouldRegenerate(isSameModule)) {
//put additional captured parameters on stack
for (CapturedParamDesc capturedParamDesc : transformationInfo.getAllRecapturedParameters()) {
visitFieldInsn(Opcodes.GETSTATIC, capturedParamDesc.getContainingLambdaName(),
"$$$" + capturedParamDesc.getFieldName(), capturedParamDesc.getType().getDescriptor());
if (transformationInfo instanceof AnonymousObjectTransformationInfo) {
//put additional captured parameters on stack
for (CapturedParamDesc capturedParamDesc : ((AnonymousObjectTransformationInfo) transformationInfo).getAllRecapturedParameters()) {
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
if (inliningContext.getParent() instanceof RegeneratedClassContext) {
inliningContext.getParent().typeRemapper.addAdditionalMappings(transformationInfo.getOldClassName(), newInternalName);
inliningContext.getParent().typeRemapper.addAdditionalMappings(transformationInfo.getOldClassName(), transformationInfo.getNewClassName());
}
transformationInfo = null;
@@ -299,7 +301,7 @@ public class MethodInliner {
@Override
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();
}
super.visitFieldInsn(opcode, owner, name, desc);
@@ -486,7 +488,7 @@ public class MethodInliner {
offset += i == 0 ? 1 : argTypes[i - 1].getSize();
}
anonymousObjectRegenerations.add(
transformations.add(
buildConstructorInvocation(
owner, desc, lambdaMapping, awaitClassReification
)
@@ -496,16 +498,23 @@ public class MethodInliner {
}
else if (cur.getOpcode() == Opcodes.GETSTATIC) {
FieldInsnNode fieldInsnNode = (FieldInsnNode) cur;
String owner = fieldInsnNode.owner;
if (isAnonymousSingletonLoad(owner, fieldInsnNode.name)) {
anonymousObjectRegenerations.add(
String className = fieldInsnNode.owner;
if (isAnonymousSingletonLoad(className, fieldInsnNode.name)) {
transformations.add(
new AnonymousObjectTransformationInfo(
owner, awaitClassReification, isAlreadyRegenerated(owner), true,
className, awaitClassReification, isAlreadyRegenerated(className), true,
inliningContext.nameGenerator
)
);
awaitClassReification = false;
}
else if (isWhenMappingAccess(className, fieldInsnNode.name)) {
transformations.add(
new WhenMappingTransformationInfo(
className, inliningContext.nameGenerator, isAlreadyRegenerated(className), fieldInsnNode)
);
}
}
}
AbstractInsnNode prevNode = cur;
@@ -17,15 +17,19 @@
package org.jetbrains.kotlin.codegen.inline
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.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.org.objectweb.asm.ClassReader
import org.jetbrains.org.objectweb.asm.ClassVisitor
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
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
val transformationResult = InlineResult.create()
@@ -51,23 +55,72 @@ abstract class ObjectTransformer<T : TransformationInfo>(val transformationInfo:
class WhenMappingTransformer(
whenObjectRegenerationInfo: WhenMappingTransformationInfo,
state: GenerationState,
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()
//TODO add additional check that class is when mapping
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) {
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)
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)
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
}
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
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
import java.util.*
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 {
nameGenerator.genLambdaClassName() + oldClassName.substringAfterLast("/")
}
override val newClassName by lazy {
nameGenerator.genLambdaClassName() + oldClassName.substringAfterLast("/")
}
override fun shouldRegenerate(sameModule: Boolean): Boolean {
throw UnsupportedOperationException()
}
override fun shouldRegenerate(sameModule: Boolean): Boolean {
return !alreadyRegenerated && !sameModule
}
override fun canRemoveAfterTransformation(): Boolean {
return true
}
override fun canRemoveAfterTransformation(): Boolean {
return true
}
override fun createTransformer(inliningContext: InliningContext, sameModule: Boolean): ObjectTransformer<*> {
return WhenMappingTransformer(this, inliningContext);
}
}
class AnonymousObjectTransformationInfo internal constructor(
override val oldClassName: String,
private val needReification: Boolean,
val lambdasToInline: Map<Int, LambdaInfo>,
private val capturedOuterRegenerated: Boolean,
private val alreadyRegenerated: Boolean,
val constructorDesc: String?,
private val isStaticOrigin: Boolean,
private val nameGenerator: NameGenerator) : TransformationInfo {
override val oldClassName: String,
private val needReification: Boolean,
val lambdasToInline: Map<Int, LambdaInfo>,
private val capturedOuterRegenerated: Boolean,
private val alreadyRegenerated: Boolean,
val constructorDesc: String?,
private val isStaticOrigin: Boolean,
nameGenerator: NameGenerator) : TransformationInfo {
override val newClassName: String by lazy {
nameGenerator.genLambdaClassName()
}
override val newClassName: String by lazy {
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(
ownerInternalName: String,
needReification: Boolean,
alreadyRegenerated: Boolean,
isStaticOrigin: Boolean,
nameGenerator: NameGenerator
) : this(
ownerInternalName, needReification,
HashMap<Int, LambdaInfo>(), false, alreadyRegenerated, null, isStaticOrigin, nameGenerator) {
}
constructor(
ownerInternalName: String,
needReification: Boolean,
alreadyRegenerated: Boolean,
isStaticOrigin: Boolean,
nameGenerator: NameGenerator
) : this(
ownerInternalName, needReification,
HashMap<Int, LambdaInfo>(), false, alreadyRegenerated, null, isStaticOrigin, nameGenerator) {
}
override fun shouldRegenerate(sameModule: Boolean): Boolean {
return !alreadyRegenerated && (!lambdasToInline.isEmpty() || !sameModule || capturedOuterRegenerated || needReification)
}
override fun shouldRegenerate(sameModule: Boolean): Boolean {
return !alreadyRegenerated && (!lambdasToInline.isEmpty() || !sameModule || capturedOuterRegenerated || needReification)
}
override fun canRemoveAfterTransformation(): Boolean {
// Note: It is unsafe to remove anonymous class that is referenced by GETSTATIC within lambda
// because it can be local function from outer scope
return !isStaticOrigin
}
override fun canRemoveAfterTransformation(): Boolean {
// Note: It is unsafe to remove anonymous class that is referenced by GETSTATIC within lambda
// because it can be local function from outer scope
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;
public class WhenByEnumsMapping {
private static final String MAPPING_ARRAY_FIELD_PREFIX = "$EnumSwitchMapping$";
private static final String MAPPINGS_CLASS_NAME_POSTFIX = "$WhenMappings";
public static final String MAPPING_ARRAY_FIELD_PREFIX = "$EnumSwitchMapping$";
public static final String MAPPINGS_CLASS_NAME_POSTFIX = "$WhenMappings";
private final Map<EnumValue, Integer> map = new LinkedHashMap<EnumValue, Integer>();
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)
}
@@ -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)
}
@@ -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);
}
@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")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -888,6 +888,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/splitedExceptionTable.kt");
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")
@@ -169,6 +169,39 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi
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")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)