Cleanup inline codegen classes

Fix warnings, typos, formatting, break long lines, remove unused, add
nullability annotations, inline small methods used only once
This commit is contained in:
Alexander Udalov
2016-06-03 21:31:31 +03:00
parent 6a1ffc865d
commit e148dcdee7
33 changed files with 822 additions and 1002 deletions
@@ -23,8 +23,6 @@ import org.jetbrains.kotlin.codegen.AsmUtil;
import org.jetbrains.kotlin.codegen.ClassBuilder;
import org.jetbrains.kotlin.codegen.FieldInfo;
import org.jetbrains.kotlin.codegen.StackValue;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
import org.jetbrains.org.objectweb.asm.*;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
@@ -36,27 +34,16 @@ import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.isThis0;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjectTransformationInfo> {
protected final GenerationState state;
protected final KotlinTypeMapper typeMapper;
private final InliningContext inliningContext;
private final Type oldObjectType;
private final boolean isSameModule;
private final Map<String, List<String>> fieldNames = new HashMap<String, List<String>>();
private MethodNode constructor;
private String sourceInfo;
private String debugInfo;
private SourceMapper sourceMapper;
private final InliningContext inliningContext;
private final Type oldObjectType;
private final boolean isSameModule;
private final Map<String, List<String>> fieldNames = new HashMap<String, List<String>>();
public AnonymousObjectTransformer(
@NotNull AnonymousObjectTransformationInfo transformationInfo,
@NotNull InliningContext inliningContext,
@@ -64,8 +51,6 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
) {
super(transformationInfo, inliningContext.state);
this.isSameModule = isSameModule;
this.state = inliningContext.state;
this.typeMapper = state.getTypeMapper();
this.inliningContext = inliningContext;
this.oldObjectType = Type.getObjectType(transformationInfo.getOldClassName());
}
@@ -94,25 +79,25 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
int access, @NotNull String name, @NotNull String desc, String signature, String[] exceptions
) {
MethodNode node = new MethodNode(access, name, desc, signature, exceptions);
if (name.equals("<init>")){
if (constructor != null)
if (name.equals("<init>")) {
if (constructor != null) {
throw new RuntimeException("Lambda, SAM or anonymous object should have only one constructor");
}
constructor = node;
} else {
}
else {
methodsToTransform.add(node);
}
return node;
}
@Override
public FieldVisitor visitField(
int access, @NotNull String name, @NotNull String desc, String signature, Object value
) {
public FieldVisitor visitField(int access, @NotNull String name, @NotNull String desc, String signature, Object value) {
addUniqueField(name);
if (InlineCodegenUtil.isCapturedFieldName(name)) {
return null;
} else {
}
else {
return classBuilder.newField(JvmDeclarationOrigin.NO_ORIGIN, access, name, desc, signature, value);
}
}
@@ -125,7 +110,6 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
@Override
public void visitEnd() {
}
}, ClassReader.SKIP_FRAMES);
@@ -218,29 +202,30 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
@NotNull ParametersBuilder capturedBuilder,
boolean isConstructor
) {
ReifiedTypeParametersUsages typeParametersToReify = inliningContext.reifedTypeInliner.reifyInstructions(sourceNode);
Parameters parameters = isConstructor ? capturedBuilder.buildParameters() : getMethodParametersWithCaptured(capturedBuilder, sourceNode);
ReifiedTypeParametersUsages typeParametersToReify = inliningContext.reifiedTypeInliner.reifyInstructions(sourceNode);
Parameters parameters =
isConstructor ? capturedBuilder.buildParameters() : getMethodParametersWithCaptured(capturedBuilder, sourceNode);
RegeneratedLambdaFieldRemapper remapper =
new RegeneratedLambdaFieldRemapper(oldObjectType.getInternalName(), transformationInfo.getNewClassName(),
parameters, transformationInfo.getCapturedLambdasToInline(),
parentRemapper, isConstructor);
RegeneratedLambdaFieldRemapper remapper = new RegeneratedLambdaFieldRemapper(
oldObjectType.getInternalName(), transformationInfo.getNewClassName(), parameters,
transformationInfo.getCapturedLambdasToInline(), parentRemapper, isConstructor
);
MethodInliner inliner =
new MethodInliner(
sourceNode,
parameters,
inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")),
remapper,
isSameModule,
"Transformer for " + transformationInfo.getOldClassName(),
sourceMapper,
new InlineCallSiteInfo(
transformationInfo.getOldClassName(),
sourceNode.name,
isConstructor ? transformationInfo.getNewConstructorDescriptor() : sourceNode.desc),
null
);
MethodInliner inliner = new MethodInliner(
sourceNode,
parameters,
inliningContext.subInline(inliningContext.nameGenerator.subGenerator("lambda")),
remapper,
isSameModule,
"Transformer for " + transformationInfo.getOldClassName(),
sourceMapper,
new InlineCallSiteInfo(
transformationInfo.getOldClassName(),
sourceNode.name,
isConstructor ? transformationInfo.getNewConstructorDescriptor() : sourceNode.desc
),
null
);
InlineResult result = inliner.doInline(deferringVisitor, new LocalVarRemapper(parameters, 0), false, LabelOwner.NOT_APPLICABLE);
result.getReifiedTypeParametersUsages().mergeAll(typeParametersToReify);
@@ -258,13 +243,13 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
List<Type> descTypes = new ArrayList<Type>();
Parameters constructorParams = constructorInlineBuilder.buildParameters();
int [] capturedIndexes = new int [constructorParams.getReal().size() + constructorParams.getCaptured().size()];
int[] capturedIndexes = new int[constructorParams.getReal().size() + constructorParams.getCaptured().size()];
int index = 0;
int size = 0;
//complex processing cause it could have super constructor call params
for (ParameterInfo info : constructorParams) {
if (!info.isSkipped()) { //not inlined
if (!info.isSkipped) { //not inlined
if (info.isCaptured() || info instanceof CapturedParamInfo) {
capturedIndexes[index] = size;
index++;
@@ -280,17 +265,16 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
String constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, descTypes.toArray(new Type[descTypes.size()]));
//TODO for inline method make public class
transformationInfo.setNewConstructorDescriptor(constructorDescriptor);
MethodVisitor constructorVisitor = classBuilder.newMethod(NO_ORIGIN,
AsmUtil.NO_FLAG_PACKAGE_PRIVATE,
"<init>", constructorDescriptor,
null, ArrayUtil.EMPTY_STRING_ARRAY);
MethodVisitor constructorVisitor = classBuilder.newMethod(
NO_ORIGIN, AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY
);
final Label newBodyStartLabel = new Label();
constructorVisitor.visitLabel(newBodyStartLabel);
//initialize captured fields
List<NewJavaField> newFieldsWithSkipped = TransformationUtilsKt.getNewFieldsToGenerate(allCapturedBuilder.listCaptured());
List<FieldInfo> fieldInfoWithSkipped = TransformationUtilsKt.transformToFieldInfo(
Type.getObjectType(transformationInfo.getNewClassName()), newFieldsWithSkipped);
List<FieldInfo> fieldInfoWithSkipped =
TransformationUtilsKt.transformToFieldInfo(Type.getObjectType(transformationInfo.getNewClassName()), newFieldsWithSkipped);
int paramIndex = 0;
InstructionAdapter capturedFieldInitializer = new InstructionAdapter(constructorVisitor);
@@ -311,23 +295,28 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
if (fake.getLambda() != null) {
//set remap value to skip this fake (captured with lambda already skipped)
StackValue composed = StackValue.field(fake.getType(),
oldObjectType,
fake.getNewFieldName(),
false,
StackValue.LOCAL_0);
StackValue composed = StackValue.field(
fake.getType(),
oldObjectType,
fake.getNewFieldName(),
false,
StackValue.LOCAL_0
);
fake.setRemapValue(composed);
}
}
MethodNode intermediateMethodNode = new MethodNode(AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY);
MethodNode intermediateMethodNode =
new MethodNode(AsmUtil.NO_FLAG_PACKAGE_PRIVATE, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY);
inlineMethodAndUpdateGlobalResult(parentRemapper, intermediateMethodNode, constructor, constructorInlineBuilder, true);
AbstractInsnNode first = intermediateMethodNode.instructions.getFirst();
final Label oldStartLabel = first instanceof LabelNode ? ((LabelNode) first).getLabel() : null;
intermediateMethodNode.accept(new MethodBodyVisitor(capturedFieldInitializer) {
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
public void visitLocalVariable(
@NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index
) {
if (oldStartLabel == start) {
start = newBodyStartLabel;//patch for jack&jill
}
@@ -335,14 +324,13 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
}
});
constructorVisitor.visitEnd();
AsmUtil.genClosureFields(TransformationUtilsKt.toNameTypePair(TransformationUtilsKt.filterSkipped(newFieldsWithSkipped)), classBuilder);
AsmUtil.genClosureFields(
TransformationUtilsKt.toNameTypePair(TransformationUtilsKt.filterSkipped(newFieldsWithSkipped)), classBuilder
);
}
@NotNull
private Parameters getMethodParametersWithCaptured(
@NotNull ParametersBuilder capturedBuilder,
@NotNull MethodNode sourceNode
) {
private Parameters getMethodParametersWithCaptured(@NotNull ParametersBuilder capturedBuilder, @NotNull MethodNode sourceNode) {
ParametersBuilder builder = ParametersBuilder.initializeBuilderFrom(oldObjectType, sourceNode.desc);
for (CapturedParamInfo param : capturedBuilder.listCaptured()) {
builder.addCapturedParamCopy(param);
@@ -353,26 +341,23 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
@NotNull
private static DeferredMethodVisitor newMethod(@NotNull final ClassBuilder builder, @NotNull final MethodNode original) {
return new DeferredMethodVisitor(
new MethodNode(original.access,
original.name,
original.desc,
original.signature,
ArrayUtil.toStringArray(original.exceptions)),
new MethodNode(
original.access, original.name, original.desc, original.signature,
ArrayUtil.toStringArray(original.exceptions)
),
new Function0<MethodVisitor>() {
@Override
public MethodVisitor invoke() {
return builder.newMethod(
NO_ORIGIN,
original.access,
original.name,
original.desc,
original.signature,
ArrayUtil.toStringArray(original.exceptions));
NO_ORIGIN, original.access, original.name, original.desc, original.signature,
ArrayUtil.toStringArray(original.exceptions)
);
}
});
}
);
}
@NotNull
private List<CapturedParamInfo> extractParametersMappingAndPatchConstructor(
@NotNull MethodNode constructor,
@NotNull ParametersBuilder capturedParamBuilder,
@@ -380,8 +365,8 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
@NotNull final AnonymousObjectTransformationInfo transformationInfo,
@NotNull FieldRemapper parentFieldRemapper
) {
CapturedParamOwner owner = new CapturedParamOwner() {
@NotNull
@Override
public Type getType() {
return Type.getObjectType(transformationInfo.getOldClassName());
@@ -400,7 +385,6 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
FieldInsnNode fieldNode = (FieldInsnNode) cur;
String fieldName = fieldNode.name;
if (fieldNode.getOpcode() == Opcodes.PUTFIELD && InlineCodegenUtil.isCapturedFieldName(fieldName)) {
boolean isPrevVarNode = fieldNode.getPrevious() instanceof VarInsnNode;
boolean isPrevPrevVarNode = isPrevVarNode && fieldNode.getPrevious().getPrevious() instanceof VarInsnNode;
@@ -410,8 +394,13 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
VarInsnNode previous = (VarInsnNode) fieldNode.getPrevious();
int varIndex = previous.var;
LambdaInfo lambdaInfo = indexToLambda.get(varIndex);
String newFieldName = isThis0(fieldName) && shouldRenameThis0(parentFieldRemapper, indexToLambda.values()) ? getNewFieldName(fieldName, true) : fieldName;
CapturedParamInfo info = capturedParamBuilder.addCapturedParam(owner, fieldName, newFieldName, Type.getType(fieldNode.desc), lambdaInfo != null, null);
String newFieldName =
isThis0(fieldName) && shouldRenameThis0(parentFieldRemapper, indexToLambda.values())
? getNewFieldName(fieldName, true)
: fieldName;
CapturedParamInfo info = capturedParamBuilder.addCapturedParam(
owner, fieldName, newFieldName, Type.getType(fieldNode.desc), lambdaInfo != null, null
);
if (lambdaInfo != null) {
info.setLambda(lambdaInfo);
capturedLambdas.add(lambdaInfo);
@@ -440,14 +429,15 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
constructorDesc = Type.getMethodDescriptor(Type.VOID_TYPE);
}
Type [] types = Type.getArgumentTypes(constructorDesc);
Type[] types = Type.getArgumentTypes(constructorDesc);
for (Type type : types) {
LambdaInfo info = indexToLambda.get(constructorParamBuilder.getNextValueParameterIndex());
ParameterInfo parameterInfo = constructorParamBuilder.addNextParameter(type, info != null, null);
ParameterInfo parameterInfo = constructorParamBuilder.addNextParameter(type, info != null);
parameterInfo.setLambda(info);
if (capturedParams.contains(parameterInfo.getIndex())) {
parameterInfo.setCaptured(true);
} else {
}
else {
//otherwise it's super constructor parameter
}
}
@@ -456,7 +446,9 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
//TODO: some of such parameters could be skipped - we should perform additional analysis
Map<String, LambdaInfo> capturedLambdasToInline = new HashMap<String, LambdaInfo>(); //captured var of inlined parameter
List<CapturedParamDesc> allRecapturedParameters = new ArrayList<CapturedParamDesc>();
boolean addCapturedNotAddOuter = parentFieldRemapper.isRoot() || (parentFieldRemapper instanceof InlinedLambdaRemapper && parentFieldRemapper.getParent().isRoot());
boolean addCapturedNotAddOuter =
parentFieldRemapper.isRoot() ||
(parentFieldRemapper instanceof InlinedLambdaRemapper && parentFieldRemapper.getParent().isRoot());
Map<String, CapturedParamInfo> alreadyAdded = new HashMap<String, CapturedParamInfo>();
for (LambdaInfo info : capturedLambdas) {
if (addCapturedNotAddOuter) {
@@ -466,16 +458,20 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
CapturedParamInfo recapturedParamInfo = capturedParamBuilder.addCapturedParam(
desc,
alreadyAddedParam != null ? alreadyAddedParam.getNewFieldName() : getNewFieldName(desc.getFieldName(), false));
StackValue composed = StackValue.field(desc.getType(),
oldObjectType, /*TODO owner type*/
recapturedParamInfo.getNewFieldName(),
false,
StackValue.LOCAL_0);
alreadyAddedParam != null ? alreadyAddedParam.getNewFieldName() : getNewFieldName(desc.getFieldName(), false)
);
StackValue composed = StackValue.field(
desc.getType(),
oldObjectType, /*TODO owner type*/
recapturedParamInfo.getNewFieldName(),
false,
StackValue.LOCAL_0
);
recapturedParamInfo.setRemapValue(composed);
allRecapturedParameters.add(desc);
constructorParamBuilder.addCapturedParam(recapturedParamInfo, recapturedParamInfo.getNewFieldName()).setRemapValue(composed);
constructorParamBuilder.addCapturedParam(recapturedParamInfo, recapturedParamInfo.getNewFieldName())
.setRemapValue(composed);
if (alreadyAddedParam != null) {
recapturedParamInfo.setSkipInConstructor(true);
}
@@ -495,12 +491,14 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
final Type ownerType = Type.getObjectType(parent.getLambdaInternalName());
CapturedParamDesc desc = new CapturedParamDesc(new CapturedParamOwner() {
@NotNull
@Override
public Type getType() {
return ownerType;
}
}, InlineCodegenUtil.THIS, ownerType);
CapturedParamInfo recapturedParamInfo = capturedParamBuilder.addCapturedParam(desc, InlineCodegenUtil.THIS$0/*outer lambda/object*/);
CapturedParamInfo recapturedParamInfo =
capturedParamBuilder.addCapturedParam(desc, InlineCodegenUtil.THIS$0/*outer lambda/object*/);
StackValue composed = StackValue.LOCAL_0;
recapturedParamInfo.setRemapValue(composed);
allRecapturedParameters.add(desc);
@@ -514,7 +512,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
return constructorAdditionalFakeParams;
}
private static boolean shouldRenameThis0(@NotNull FieldRemapper parentFieldRemapper, Collection<LambdaInfo> values) {
private static boolean shouldRenameThis0(@NotNull FieldRemapper parentFieldRemapper, @NotNull Collection<LambdaInfo> values) {
if (isFirstDeclSiteLambdaFieldRemapper(parentFieldRemapper)) {
for (LambdaInfo value : values) {
for (CapturedParamDesc desc : value.getCapturedVars()) {
@@ -528,11 +526,12 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
}
@NotNull
public String getNewFieldName(@NotNull String oldName, boolean originalField) {
private String getNewFieldName(@NotNull String oldName, boolean originalField) {
if (InlineCodegenUtil.THIS$0.equals(oldName)) {
if (!originalField) {
return oldName;
} else {
}
else {
//rename original 'this$0' in declaration site lambda (inside inline function) to use this$0 only for outer lambda/object access on call site
return addUniqueField(oldName + InlineCodegenUtil.INLINE_FUN_THIS_0_SUFFIX);
}
@@ -553,7 +552,7 @@ public class AnonymousObjectTransformer extends ObjectTransformer<AnonymousObjec
return newName;
}
private static boolean isFirstDeclSiteLambdaFieldRemapper(FieldRemapper parentRemapper) {
private static boolean isFirstDeclSiteLambdaFieldRemapper(@NotNull FieldRemapper parentRemapper) {
return !(parentRemapper instanceof RegeneratedLambdaFieldRemapper) && !(parentRemapper instanceof InlinedLambdaRemapper);
}
}
@@ -22,7 +22,6 @@ import org.jetbrains.org.objectweb.asm.signature.SignatureReader
import org.jetbrains.org.objectweb.asm.signature.SignatureVisitor
class AsmTypeRemapper(val typeRemapper: TypeRemapper, val isDefaultGeneration: Boolean, val result: InlineResult) : Remapper() {
override fun map(type: String): String {
return typeRemapper.map(type)
}
@@ -33,7 +32,6 @@ class AsmTypeRemapper(val typeRemapper: TypeRemapper, val isDefaultGeneration: B
}
return object : RemappingSignatureAdapter(v, this) {
override fun visitTypeVariable(name: String) {
/*TODO try to erase absent type variable*/
val mapping = typeRemapper.mapTypeParameter(name) ?: return super.visitTypeVariable(name)
@@ -54,4 +52,4 @@ class AsmTypeRemapper(val typeRemapper: TypeRemapper, val isDefaultGeneration: B
}
}
}
}
}
@@ -44,9 +44,4 @@ public class CapturedParamDesc {
public Type getType() {
return type;
}
@NotNull
public static CapturedParamDesc createDesc(@NotNull CapturedParamOwner containingLambdaInfo, @NotNull String fieldName, @NotNull Type type) {
return new CapturedParamDesc(containingLambdaInfo, fieldName, type);
}
}
@@ -17,35 +17,24 @@
package org.jetbrains.kotlin.codegen.inline;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.StackValue;
import org.jetbrains.org.objectweb.asm.Type;
public class CapturedParamInfo extends ParameterInfo {
public static final CapturedParamInfo STUB = new CapturedParamInfo(CapturedParamDesc.createDesc(new CapturedParamOwner() {
@Override
public Type getType() {
return Type.getObjectType("STUB");
}
}, "STUB", Type.getObjectType("STUB")), true, -1, -1);
public final CapturedParamDesc desc;
private final String newFieldName;
private boolean skipInConstructor;
public CapturedParamInfo(@NotNull CapturedParamDesc desc, boolean skipped, int index, int remapIndex) {
this(desc, desc.getFieldName(), skipped, index, remapIndex);
}
public CapturedParamInfo(@NotNull CapturedParamDesc desc, @NotNull String newFieldName, boolean skipped, int index, int remapIndex) {
super(desc.getType(), skipped, index, remapIndex, index);
this.desc = desc;
this.newFieldName = newFieldName;
}
public CapturedParamInfo(@NotNull CapturedParamDesc desc, @NotNull String newFieldName, boolean skipped, int index, StackValue remapIndex) {
public CapturedParamInfo(
@NotNull CapturedParamDesc desc, @NotNull String newFieldName, boolean skipped, int index, @Nullable StackValue remapIndex
) {
super(desc.getType(), skipped, index, remapIndex, index);
this.desc = desc;
this.newFieldName = newFieldName;
@@ -67,9 +56,9 @@ public class CapturedParamInfo extends ParameterInfo {
}
@NotNull
public CapturedParamInfo clone(int newIndex, StackValue newRamapIndex) {
CapturedParamInfo capturedParamInfo = new CapturedParamInfo(desc, newFieldName, isSkipped, newIndex, newRamapIndex);
capturedParamInfo.setLambda(lambda);
private CapturedParamInfo clone(int newIndex, @Nullable StackValue newRemapIndex) {
CapturedParamInfo capturedParamInfo = new CapturedParamInfo(desc, newFieldName, isSkipped, newIndex, newRemapIndex);
capturedParamInfo.setLambda(getLambda());
capturedParamInfo.setSkipInConstructor(skipInConstructor);
return capturedParamInfo;
}
@@ -16,8 +16,10 @@
package org.jetbrains.kotlin.codegen.inline;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.org.objectweb.asm.Type;
public interface CapturedParamOwner {
@NotNull
Type getType();
}
@@ -17,18 +17,12 @@
package org.jetbrains.kotlin.codegen.inline
import com.google.common.collect.LinkedListMultimap
import java.util.ArrayList
import com.intellij.util.containers.Stack
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.*
import java.util.Comparator
import java.util.Collections
import java.util.*
abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
val tryBlocksMetaInfo: IntervalMetaInfo<TryCatchBlockNodeInfo> = IntervalMetaInfo()
val localVarsMetaInfo: IntervalMetaInfo<LocalVarNodeWrapper> = IntervalMetaInfo()
var nextFreeLocalIndex: Int = parameterSize
@@ -70,11 +64,11 @@ abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
result
}
Collections.sort<TryCatchBlockNodeInfo>(intervals, comp)
Collections.sort(intervals, comp)
return intervals
}
protected fun substituteTryBlockNodes(node: MethodNode) {
fun substituteTryBlockNodes(node: MethodNode) {
node.tryCatchBlocks.clear()
sortTryCatchBlocks(tryBlocksMetaInfo.allIntervals)
for (info in tryBlocksMetaInfo.getMeaningfulIntervals()) {
@@ -82,7 +76,6 @@ abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
}
}
fun substituteLocalVarTable(node: MethodNode) {
node.localVariables.clear()
for (info in localVarsMetaInfo.getMeaningfulIntervals()) {
@@ -92,13 +85,9 @@ abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
}
class IntervalMetaInfo<T : SplittableInterval<T>> {
val intervalStarts = LinkedListMultimap.create<LabelNode, T>()
val intervalEnds = LinkedListMultimap.create<LabelNode, T>()
val allIntervals: ArrayList<T> = arrayListOf()
val currentIntervals: MutableSet<T> = linkedSetOf()
fun addNewInterval(newInfo: T) {
@@ -117,17 +106,10 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
intervalEnds.put(remapped.endLabel, remapped)
}
fun splitCurrentIntervals(by : Interval, keepStart: Boolean): List<SplitPair<T>> {
fun splitCurrentIntervals(by: Interval, keepStart: Boolean): List<SplitPair<T>> {
return currentIntervals.map { split(it, by, keepStart) }
}
fun splitAndRemoveIntervalsFromCurrents(by : Interval) {
val copies = ArrayList(currentIntervals)
copies.forEach {
splitAndRemoveInterval(it, by, true)
}
}
fun processCurrent(curIns: LabelNode, directOrder: Boolean) {
getInterval(curIns, directOrder).forEach {
val added = currentIntervals.add(it)
@@ -140,25 +122,27 @@ class IntervalMetaInfo<T : SplittableInterval<T>> {
}
}
fun split(interval: T, by : Interval, keepStart: Boolean): SplitPair<T> {
fun split(interval: T, by: Interval, keepStart: Boolean): SplitPair<T> {
val split = interval.split(by, keepStart)
if (!keepStart) {
remapStartLabel(split.newPart.startLabel, split.patchedPart)
} else {
}
else {
remapEndLabel(split.newPart.endLabel, split.patchedPart)
}
addNewInterval(split.newPart)
return split
}
fun splitAndRemoveInterval(interval: T, by : Interval, keepStart: Boolean): SplitPair<T> {
fun splitAndRemoveInterval(interval: T, by: Interval, keepStart: Boolean): SplitPair<T> {
val splitPair = split(interval, by, keepStart)
val removed = currentIntervals.remove(splitPair.patchedPart)
assert(removed, {"Wrong interval structure: $splitPair"})
assert(removed) { "Wrong interval structure: $splitPair" }
return splitPair
}
fun getInterval(curIns: LabelNode, isOpen: Boolean) = if (isOpen) intervalStarts.get(curIns) else intervalEnds.get(curIns)
fun getInterval(curIns: LabelNode, isOpen: Boolean) =
if (isOpen) intervalStarts.get(curIns) else intervalEnds.get(curIns)
}
private fun Interval.isMeaningless(): Boolean {
@@ -175,23 +159,16 @@ fun <T : SplittableInterval<T>> IntervalMetaInfo<T>.getMeaningfulIntervals(): Li
}
class DefaultProcessor(val node: MethodNode, parameterSize: Int) : CoveringTryCatchNodeProcessor(parameterSize) {
init {
node.tryCatchBlocks.forEach { addTryNode(it) }
node.localVariables.forEach { addLocalVarNode(it) }
node.tryCatchBlocks.forEach {
tryBlocksMetaInfo.addNewInterval(TryCatchBlockNodeInfo(it, false))
}
node.localVariables.forEach {
localVarsMetaInfo.addNewInterval(LocalVarNodeWrapper(it))
}
}
fun addLocalVarNode(it: LocalVariableNode) {
localVarsMetaInfo.addNewInterval(LocalVarNodeWrapper(it))
}
fun addTryNode(node: TryCatchBlockNode) {
tryBlocksMetaInfo.addNewInterval(TryCatchBlockNodeInfo(node, false))
}
override fun instructionIndex(inst: AbstractInsnNode): Int {
return node.instructions.indexOf(inst)
}
override fun instructionIndex(inst: AbstractInsnNode): Int = node.instructions.indexOf(inst)
}
class LocalVarNodeWrapper(val node: LocalVariableNode) : Interval, SplittableInterval<LocalVarNodeWrapper> {
@@ -216,5 +193,4 @@ class LocalVarNodeWrapper(val node: LocalVariableNode) : Interval, SplittableInt
LocalVariableNode(node.name, node.desc, node.signature, newPartInterval.first, newPartInterval.second, node.index)
))
}
}
}
@@ -20,7 +20,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.StackValue;
import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.Type;
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode;
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode;
import org.jetbrains.org.objectweb.asm.tree.MethodNode;
@@ -29,30 +28,24 @@ import java.util.Collection;
import java.util.List;
public class FieldRemapper {
private final String lambdaInternalName;
protected FieldRemapper parent;
private final String lambdaInternalName;
private final Parameters params;
public FieldRemapper(@Nullable String lambdaInternalName, @Nullable FieldRemapper parent, @NotNull Parameters methodParams) {
this.lambdaInternalName = lambdaInternalName;
this.parent = parent;
params = methodParams;
this.params = methodParams;
}
protected boolean canProcess(@NotNull String fieldOwner, String fieldName, boolean isFolding) {
protected boolean canProcess(@NotNull String fieldOwner, @NotNull String fieldName, boolean isFolding) {
return fieldOwner.equals(getLambdaInternalName()) &&
//don't process general field of anonymous objects
InlineCodegenUtil.isCapturedFieldName(fieldName);
}
@Nullable
public AbstractInsnNode foldFieldAccessChainIfNeeded(
@NotNull List<AbstractInsnNode> capturedFieldAccess,
@NotNull MethodNode node
) {
public AbstractInsnNode foldFieldAccessChainIfNeeded(@NotNull List<AbstractInsnNode> capturedFieldAccess, @NotNull MethodNode node) {
if (capturedFieldAccess.size() == 1) {
//just aload
return null;
@@ -72,33 +65,33 @@ public class FieldRemapper {
int currentInstruction,
@NotNull MethodNode node
) {
AbstractInsnNode transformed = null;
boolean checkParent = !isRoot() && currentInstruction < capturedFieldAccess.size() - 1;
if (checkParent) {
transformed = parent.foldFieldAccessChainIfNeeded(capturedFieldAccess, currentInstruction + 1, node);
}
if (transformed == null) {
//if parent couldn't transform
FieldInsnNode insnNode = (FieldInsnNode) capturedFieldAccess.get(currentInstruction);
if (canProcess(insnNode.owner, insnNode.name, true)) {
insnNode.name = "$$$" + insnNode.name;
insnNode.setOpcode(Opcodes.GETSTATIC);
AbstractInsnNode next = capturedFieldAccess.get(0);
while (next != insnNode) {
AbstractInsnNode toDelete = next;
next = next.getNext();
node.instructions.remove(toDelete);
}
transformed = capturedFieldAccess.get(capturedFieldAccess.size() - 1);
AbstractInsnNode transformed = parent.foldFieldAccessChainIfNeeded(capturedFieldAccess, currentInstruction + 1, node);
if (transformed != null) {
return transformed;
}
}
return transformed;
FieldInsnNode insnNode = (FieldInsnNode) capturedFieldAccess.get(currentInstruction);
if (canProcess(insnNode.owner, insnNode.name, true)) {
insnNode.name = "$$$" + insnNode.name;
insnNode.setOpcode(Opcodes.GETSTATIC);
AbstractInsnNode next = capturedFieldAccess.get(0);
while (next != insnNode) {
AbstractInsnNode toDelete = next;
next = next.getNext();
node.instructions.remove(toDelete);
}
return capturedFieldAccess.get(capturedFieldAccess.size() - 1);
}
return null;
}
@Nullable
public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode) {
return findField(fieldInsnNode, params.getCaptured());
}
@@ -106,13 +99,15 @@ public class FieldRemapper {
@Nullable
protected CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode, @NotNull Collection<CapturedParamInfo> captured) {
for (CapturedParamInfo valueDescriptor : captured) {
if (valueDescriptor.getOriginalFieldName().equals(fieldInsnNode.name) && fieldInsnNode.owner.equals(valueDescriptor.getContainingLambdaName())) {
if (valueDescriptor.getOriginalFieldName().equals(fieldInsnNode.name) &&
valueDescriptor.getContainingLambdaName().equals(fieldInsnNode.owner)) {
return valueDescriptor;
}
}
return null;
}
@NotNull
public FieldRemapper getParent() {
return parent;
}
@@ -131,8 +126,7 @@ public class FieldRemapper {
@Nullable
public StackValue getFieldForInline(@NotNull FieldInsnNode node, @Nullable StackValue prefix) {
CapturedParamInfo field = MethodInliner.findCapturedField(node, this);
return field.getRemapValue();
return MethodInliner.findCapturedField(node, this).getRemapValue();
}
public boolean isInsideInliningLambda() {
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.codegen.inline;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.org.objectweb.asm.Label;
import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
@@ -27,19 +28,16 @@ import java.util.List;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.getLoadStoreArgSize;
public class InlineAdapter extends InstructionAdapter {
private int nextLocalIndex = 0;
private final SourceMapper sourceMapper;
private boolean isLambdaInlining = false;
private final List<CatchBlock> blocks = new ArrayList<CatchBlock>();
private boolean isLambdaInlining = false;
private int nextLocalIndex = 0;
private int nextLocalIndexBeforeInline = -1;
public InlineAdapter(MethodVisitor mv, int localsSize, @NotNull SourceMapper sourceMapper) {
public InlineAdapter(@NotNull MethodVisitor mv, int localsSize, @NotNull SourceMapper sourceMapper) {
super(InlineCodegenUtil.API, mv);
nextLocalIndex = localsSize;
this.nextLocalIndex = localsSize;
this.sourceMapper = sourceMapper;
}
@@ -70,15 +68,15 @@ public class InlineAdapter extends InstructionAdapter {
this.isLambdaInlining = isInlining;
if (isInlining) {
nextLocalIndexBeforeInline = nextLocalIndex;
} else {
}
else {
nextLocalIndex = nextLocalIndexBeforeInline;
}
}
@Override
public void visitTryCatchBlock(Label start,
Label end, Label handler, String type) {
if(!isLambdaInlining) {
public void visitTryCatchBlock(@NotNull Label start, @NotNull Label end, @NotNull Label handler, @Nullable String type) {
if (!isLambdaInlining) {
blocks.add(new CatchBlock(start, end, handler, type));
}
else {
@@ -87,7 +85,7 @@ public class InlineAdapter extends InstructionAdapter {
}
@Override
public void visitLineNumber(int line, Label start) {
public void visitLineNumber(int line, @NotNull Label start) {
if (InlineCodegenUtil.GENERATE_SMAP) {
line = sourceMapper.mapLineNumber(line);
}
@@ -111,7 +109,7 @@ public class InlineAdapter extends InstructionAdapter {
private final Label handler;
private final String type;
public CatchBlock(Label start, Label end, Label handler, String type) {
public CatchBlock(@NotNull Label start, @NotNull Label end, @NotNull Label handler, @Nullable String type) {
this.start = start;
this.end = end;
this.handler = handler;
@@ -34,7 +34,8 @@ inline fun <K, V> SLRUMap<K, V>.getOrPut(key: K, defaultValue: () -> V): V {
val answer = defaultValue()
put(key, answer)
answer
} else {
}
else {
value
}
}
@@ -35,7 +35,6 @@ import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache;
import org.jetbrains.kotlin.name.ClassId;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
import org.jetbrains.kotlin.resolve.BindingContext;
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
@@ -85,7 +84,9 @@ public class InlineCodegen extends CallGenerator {
private final Map<Integer, LambdaInfo> expressionMap = new HashMap<Integer, LambdaInfo>();
private final ReifiedTypeInliner reifiedTypeInliner;
@Nullable private final TypeParameterMappings typeParameterMappings;
@Nullable
private final TypeParameterMappings typeParameterMappings;
private LambdaInfo activeLambda;
@@ -131,35 +132,53 @@ public class InlineCodegen extends CallGenerator {
}
@Override
public void genCallInner(@NotNull Callable callableMethod, @Nullable ResolvedCall<?> resolvedCall, boolean callDefault, @NotNull ExpressionCodegen codegen) {
SMAPAndMethodNode nodeAndSmap = null;
public void genCallInner(
@NotNull Callable callableMethod,
@Nullable ResolvedCall<?> resolvedCall,
boolean callDefault,
@NotNull ExpressionCodegen codegen
) {
if (!state.getInlineCycleReporter().enterIntoInlining(resolvedCall)) {
generateStub(resolvedCall, codegen);
return;
}
SMAPAndMethodNode nodeAndSmap = null;
try {
nodeAndSmap = createMethodNode(callDefault);
nodeAndSmap = createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault);
endCall(inlineCall(nodeAndSmap));
}
catch (CompilationException e) {
throw e;
}
catch (InlineException e) {
throw throwCompilationException(nodeAndSmap, e, false);
}
catch (Exception e) {
boolean generateNodeText = !(e instanceof InlineException);
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(this.codegen.getContext().getContextDescriptor());
throw new CompilationException("Couldn't inline method call '" +
functionDescriptor.getName() +
"' into \n" + (element != null ? element.getText() : "null psi element " + this.codegen.getContext().getContextDescriptor()) +
(generateNodeText ? ("\ncause: " + InlineCodegenUtil.getNodeText(nodeAndSmap != null ? nodeAndSmap.getNode(): null)) : ""),
e, callElement);
throw throwCompilationException(nodeAndSmap, e, true);
}
finally {
state.getInlineCycleReporter().exitFromInliningOf(resolvedCall);
}
}
protected void generateStub(@Nullable ResolvedCall<?> resolvedCall, @NotNull ExpressionCodegen codegen) {
@NotNull
private CompilationException throwCompilationException(
@Nullable SMAPAndMethodNode nodeAndSmap, @NotNull Exception e, boolean generateNodeText
) {
CallableMemberDescriptor contextDescriptor = codegen.getContext().getContextDescriptor();
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(contextDescriptor);
MethodNode node = nodeAndSmap != null ? nodeAndSmap.getNode() : null;
throw new CompilationException(
"Couldn't inline method call '" + functionDescriptor.getName() + "' into\n" +
contextDescriptor + "\n" +
(element != null ? element.getText() : "<no source>") +
(generateNodeText ? ("\nCause: " + InlineCodegenUtil.getNodeText(node)) : ""),
e, callElement
);
}
private void generateStub(@Nullable ResolvedCall<?> resolvedCall, @NotNull ExpressionCodegen codegen) {
leaveTemps();
assert resolvedCall != null;
String message = "Call is part of inline cycle: " + resolvedCall.getCall().getCallElement().getText();
@@ -176,25 +195,19 @@ public class InlineCodegen extends CallGenerator {
codegen.markLineNumberAfterInlineIfNeeded();
}
@NotNull
private SMAPAndMethodNode createMethodNode(boolean callDefault) throws IOException {
return createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault, state);
}
@NotNull
static SMAPAndMethodNode createMethodNode(
@NotNull final FunctionDescriptor functionDescriptor,
@NotNull JvmMethodSignature jvmSignature,
@NotNull ExpressionCodegen codegen,
@NotNull CodegenContext context,
boolean callDefault,
@NotNull final GenerationState state
boolean callDefault
) {
KotlinTypeMapper typeMapper = state.getTypeMapper();
final Method asmMethod = callDefault
? typeMapper.mapDefaultMethod(functionDescriptor, context.getContextKind())
: jvmSignature.getAsmMethod();
final GenerationState state = codegen.getState();
final Method asmMethod =
callDefault
? state.getTypeMapper().mapDefaultMethod(functionDescriptor, context.getContextKind())
: jvmSignature.getAsmMethod();
MethodId methodId = new MethodId(DescriptorUtils.getFqNameSafe(functionDescriptor.getContainingDeclaration()), asmMethod);
@@ -202,15 +215,18 @@ public class InlineCodegen extends CallGenerator {
return doCreateMethodNodeFromSource(functionDescriptor, jvmSignature, codegen, context, callDefault, state, asmMethod);
}
SMAPAndMethodNode resultInCache =
InlineCacheKt
.getOrPut(state.getInlineCache().getMethodNodeById(), methodId, new Function0<SMAPAndMethodNode>() {
@Override
public SMAPAndMethodNode invoke() {
return doCreateMethodNodeFromCompiled(functionDescriptor,
state, asmMethod);
}
});
SMAPAndMethodNode resultInCache = InlineCacheKt.getOrPut(
state.getInlineCache().getMethodNodeById(), methodId, new Function0<SMAPAndMethodNode>() {
@Override
public SMAPAndMethodNode invoke() {
SMAPAndMethodNode result = doCreateMethodNodeFromCompiled(functionDescriptor, state, asmMethod);
if (result == null) {
throw new IllegalStateException("Couldn't obtain compiled function body for " + functionDescriptor);
}
return result;
}
}
);
return resultInCache.copyWithNewNode(cloneMethodNode(resultInCache.getNode()));
}
@@ -220,12 +236,13 @@ public class InlineCodegen extends CallGenerator {
methodNode.instructions.resetLabels();
MethodNode result = new MethodNode(
API, methodNode.access, methodNode.name, methodNode.desc, methodNode.signature,
methodNode.exceptions.toArray(ArrayUtil.EMPTY_STRING_ARRAY));
ArrayUtil.toStringArray(methodNode.exceptions)
);
methodNode.accept(result);
return result;
}
@NotNull
@Nullable
private static SMAPAndMethodNode doCreateMethodNodeFromCompiled(
@NotNull FunctionDescriptor functionDescriptor,
@NotNull final GenerationState state,
@@ -233,7 +250,6 @@ public class InlineCodegen extends CallGenerator {
) {
KotlinTypeMapper typeMapper = state.getTypeMapper();
SMAPAndMethodNode nodeAndSMAP;
if (isBuiltInArrayIntrinsic(functionDescriptor)) {
ClassId classId = IntrinsicArrayConstructorsKt.getClassId();
byte[] bytes = InlineCacheKt.getOrPut(state.getInlineCache().getClassBytes(), classId, new Function0<byte[]>() {
@@ -243,24 +259,13 @@ public class InlineCodegen extends CallGenerator {
}
});
nodeAndSMAP = InlineCodegenUtil.getMethodNode(
bytes,
asmMethod.getName(),
asmMethod.getDescriptor(),
classId
);
if (nodeAndSMAP == null) {
throw new IllegalStateException("Couldn't obtain array constructor body for " + descriptorName(functionDescriptor));
}
return nodeAndSMAP;
return InlineCodegenUtil.getMethodNode(bytes, asmMethod.getName(), asmMethod.getDescriptor(), classId);
}
assert functionDescriptor instanceof DeserializedSimpleFunctionDescriptor;
assert functionDescriptor instanceof DeserializedSimpleFunctionDescriptor : "Not a deserialized function: " + functionDescriptor;
KotlinTypeMapper.ContainingClassesInfo containingClasses = typeMapper.getContainingClassesForDeserializedCallable(
(DeserializedSimpleFunctionDescriptor) functionDescriptor);
KotlinTypeMapper.ContainingClassesInfo containingClasses =
typeMapper.getContainingClassesForDeserializedCallable((DeserializedSimpleFunctionDescriptor) functionDescriptor);
final ClassId containerId = containingClasses.getImplClassId();
@@ -280,15 +285,7 @@ public class InlineCodegen extends CallGenerator {
}
});
nodeAndSMAP = InlineCodegenUtil.getMethodNode(
bytes, asmMethod.getName(), asmMethod.getDescriptor(), containerId
);
if (nodeAndSMAP == null) {
throw new IllegalStateException("Couldn't obtain compiled function body for " + descriptorName(functionDescriptor));
}
return nodeAndSMAP;
return InlineCodegenUtil.getMethodNode(bytes, asmMethod.getName(), asmMethod.getDescriptor(), containerId);
}
@NotNull
@@ -304,27 +301,31 @@ public class InlineCodegen extends CallGenerator {
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
if (!(element instanceof KtNamedFunction)) {
throw new IllegalStateException("Couldn't find declaration for function " + descriptorName(functionDescriptor));
throw new IllegalStateException("Couldn't find declaration for function " + functionDescriptor);
}
KtNamedFunction inliningFunction = (KtNamedFunction) element;
MethodNode node = new MethodNode(InlineCodegenUtil.API,
getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0),
asmMethod.getName(),
asmMethod.getDescriptor(),
null,
null);
MethodNode node = new MethodNode(
InlineCodegenUtil.API,
getMethodAsmFlags(functionDescriptor, context.getContextKind()) | (callDefault ? Opcodes.ACC_STATIC : 0),
asmMethod.getName(),
asmMethod.getDescriptor(),
null, null
);
//for maxLocals calculation
MethodVisitor maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node);
MethodContext methodContext = context.getParentContext().intoFunction(functionDescriptor);
CodegenContext parentContext = context.getParentContext();
assert parentContext != null : "Context has no parent: " + context;
MethodContext methodContext = parentContext.intoFunction(functionDescriptor);
SMAP smap;
if (callDefault) {
Type implementationOwner = state.getTypeMapper().mapImplementationOwner(functionDescriptor);
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(codegen.getParentCodegen(), inliningFunction,
(FieldOwnerContext) methodContext.getParentContext(),
implementationOwner.getInternalName());
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(
codegen.getParentCodegen(), inliningFunction, (FieldOwnerContext) methodContext.getParentContext(),
implementationOwner.getInternalName()
);
FunctionCodegen.generateDefaultImplBody(
methodContext, functionDescriptor, maxCalcAdapter, DefaultParameterValueLoader.DEFAULT,
inliningFunction, parentCodegen, asmMethod
@@ -332,7 +333,7 @@ public class InlineCodegen extends CallGenerator {
smap = createSMAPWithDefaultMapping(inliningFunction, parentCodegen.getOrCreateSourceMapper().getResultMappings());
}
else {
smap = generateMethodBody(maxCalcAdapter, functionDescriptor, methodContext, inliningFunction, jvmSignature, false, codegen, state);
smap = generateMethodBody(maxCalcAdapter, functionDescriptor, methodContext, inliningFunction, jvmSignature, false, codegen);
}
maxCalcAdapter.visitMaxs(-1, -1);
maxCalcAdapter.visitEnd();
@@ -347,7 +348,8 @@ public class InlineCodegen extends CallGenerator {
functionDescriptor.getContainingDeclaration() instanceof BuiltInsPackageFragment;
}
private InlineResult inlineCall(SMAPAndMethodNode nodeAndSmap) {
@NotNull
private InlineResult inlineCall(@NotNull SMAPAndMethodNode nodeAndSmap) {
DefaultSourceMapper defaultSourceMapper = codegen.getParentCodegen().getOrCreateSourceMapper();
defaultSourceMapper.setCallSiteMarker(new CallSiteMarker(codegen.getLastLineNumber()));
MethodNode node = nodeAndSmap.getNode();
@@ -363,7 +365,8 @@ public class InlineCodegen extends CallGenerator {
InliningContext info = new RootInliningContext(
expressionMap, state, codegen.getInlineNameGenerator().subGenerator(jvmSignature.getAsmMethod().getName()),
codegen.getContext(), callElement, getInlineCallSiteInfo(), reifiedTypeInliner, typeParameterMappings);
callElement, getInlineCallSiteInfo(), reifiedTypeInliner, typeParameterMappings
);
MethodInliner inliner = new MethodInliner(
node, parameters, info, new FieldRemapper(null, null, parameters), isSameModule,
@@ -374,7 +377,6 @@ public class InlineCodegen extends CallGenerator {
LocalVarRemapper remapper = new LocalVarRemapper(parameters, initialFrameSize);
MethodNode adapter = InlineCodegenUtil.createEmptyMethodNode();
//hack to keep linenumber info, otherwise jdi will skip begin of linenumber chain
adapter.visitInsn(Opcodes.NOP);
@@ -392,7 +394,9 @@ public class InlineCodegen extends CallGenerator {
};
List<MethodInliner.PointForExternalFinallyBlocks> infos = MethodInliner.processReturns(adapter, labelOwner, true, null);
generateAndInsertFinallyBlocks(adapter, infos, ((StackValue.Local)remapper.remap(parameters.getArgsSizeOnStack() + 1).value).index);
generateAndInsertFinallyBlocks(
adapter, infos, ((StackValue.Local) remapper.remap(parameters.getArgsSizeOnStack() + 1).value).index
);
removeStaticInitializationTrigger(adapter);
removeFinallyMarkers(adapter);
@@ -405,7 +409,7 @@ public class InlineCodegen extends CallGenerator {
return result;
}
private static void removeStaticInitializationTrigger(MethodNode methodNode) {
private static void removeStaticInitializationTrigger(@NotNull MethodNode methodNode) {
InsnList insnList = methodNode.instructions;
AbstractInsnNode insn = insnList.getFirst();
while (insn != null) {
@@ -420,6 +424,7 @@ public class InlineCodegen extends CallGenerator {
}
}
@NotNull
private InlineCallSiteInfo getInlineCallSiteInfo() {
MethodContext context = codegen.getContext();
MemberCodegen<?> parentCodegen = codegen.getParentCodegen();
@@ -433,7 +438,9 @@ public class InlineCodegen extends CallGenerator {
}
JvmMethodSignature signature = typeMapper.mapSignatureSkipGeneric(context.getFunctionDescriptor(), context.getContextKind());
return new InlineCallSiteInfo(parentCodegen.getClassName(), signature.getAsmMethod().getName(), signature.getAsmMethod().getDescriptor());
return new InlineCallSiteInfo(
parentCodegen.getClassName(), signature.getAsmMethod().getName(), signature.getAsmMethod().getDescriptor()
);
}
private void generateClosuresBodies() {
@@ -442,25 +449,29 @@ public class InlineCodegen extends CallGenerator {
}
}
private SMAPAndMethodNode generateLambdaBody(LambdaInfo info) {
@NotNull
private SMAPAndMethodNode generateLambdaBody(@NotNull LambdaInfo info) {
KtExpression declaration = info.getFunctionWithBodyOrCallableReference();
FunctionDescriptor descriptor = info.getFunctionDescriptor();
MethodContext parentContext = codegen.getContext();
MethodContext context = parentContext.intoClosure(descriptor, codegen, typeMapper).intoInlinedLambda(descriptor, info.isCrossInline);
MethodContext context =
codegen.getContext().intoClosure(descriptor, codegen, typeMapper).intoInlinedLambda(descriptor, info.isCrossInline);
JvmMethodSignature jvmMethodSignature = typeMapper.mapSignatureSkipGeneric(descriptor);
Method asmMethod = jvmMethodSignature.getAsmMethod();
MethodNode methodNode = new MethodNode(InlineCodegenUtil.API, getMethodAsmFlags(descriptor, context.getContextKind()), asmMethod.getName(), asmMethod.getDescriptor(), null, null);
MethodNode methodNode = new MethodNode(
InlineCodegenUtil.API, getMethodAsmFlags(descriptor, context.getContextKind()),
asmMethod.getName(), asmMethod.getDescriptor(), null, null
);
MethodVisitor adapter = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode);
SMAP smap = generateMethodBody(adapter, descriptor, context, declaration, jvmMethodSignature, true, codegen, state);
SMAP smap = generateMethodBody(adapter, descriptor, context, declaration, jvmMethodSignature, true, codegen);
adapter.visitMaxs(-1, -1);
return new SMAPAndMethodNode(methodNode, smap);
}
@NotNull
private static SMAP generateMethodBody(
@NotNull MethodVisitor adapter,
@NotNull FunctionDescriptor descriptor,
@@ -468,32 +479,29 @@ public class InlineCodegen extends CallGenerator {
@NotNull KtExpression expression,
@NotNull JvmMethodSignature jvmMethodSignature,
boolean isLambda,
@NotNull ExpressionCodegen codegen,
@NotNull GenerationState state
@NotNull ExpressionCodegen codegen
) {
FakeMemberCodegen parentCodegen =
new FakeMemberCodegen(codegen.getParentCodegen(), expression,
(FieldOwnerContext) context.getParentContext(),
isLambda ? codegen.getParentCodegen().getClassName()
: state.getTypeMapper().mapImplementationOwner(descriptor).getInternalName());
GenerationState state = codegen.getState();
// Wrapping for preventing marking actual parent codegen as containing reified markers
FakeMemberCodegen parentCodegen = new FakeMemberCodegen(
codegen.getParentCodegen(), expression, (FieldOwnerContext) context.getParentContext(),
isLambda ? codegen.getParentCodegen().getClassName()
: state.getTypeMapper().mapImplementationOwner(descriptor).getInternalName()
);
FunctionGenerationStrategy strategy =
expression instanceof KtCallableReferenceExpression ?
new FunctionReferenceGenerationStrategy(
state,
descriptor,
CallUtilKt.getResolvedCallWithAssert(((KtCallableReferenceExpression) expression).getCallableReference(),
codegen.getBindingContext()
)) :
CallUtilKt.getResolvedCallWithAssert(
((KtCallableReferenceExpression) expression).getCallableReference(), codegen.getBindingContext()
)
) :
new FunctionGenerationStrategy.FunctionDefault(state, descriptor, (KtDeclarationWithBody) expression);
FunctionCodegen.generateMethodBody(
adapter, descriptor, context, jvmMethodSignature,
strategy,
// Wrapping for preventing marking actual parent codegen as containing reifier markers
parentCodegen
);
FunctionCodegen.generateMethodBody(adapter, descriptor, context, jvmMethodSignature, strategy, parentCodegen);
if (isLambda) {
codegen.propagateChildReifiedTypeParametersUsages(parentCodegen.getReifiedTypeParametersUsages());
@@ -514,13 +522,18 @@ public class InlineCodegen extends CallGenerator {
}
private static class FakeMemberCodegen extends MemberCodegen {
private final MemberCodegen delegate;
private final String className;
@NotNull final MemberCodegen delegate;
@NotNull private final String className;
public FakeMemberCodegen(@NotNull MemberCodegen wrapped, @NotNull KtElement declaration, @NotNull FieldOwnerContext codegenContext, @NotNull String className) {
@SuppressWarnings("unchecked")
public FakeMemberCodegen(
@NotNull MemberCodegen wrapped,
@NotNull KtElement declaration,
@NotNull FieldOwnerContext codegenContext,
@NotNull String className
) {
super(wrapped, declaration, codegenContext);
delegate = wrapped;
this.delegate = wrapped;
this.className = className;
}
@@ -554,11 +567,7 @@ public class InlineCodegen extends CallGenerator {
}
@Override
public void afterParameterPut(
@NotNull Type type,
@Nullable StackValue stackValue,
int parameterIndex
) {
public void afterParameterPut(@NotNull Type type, @Nullable StackValue stackValue, int parameterIndex) {
putArgumentOrCapturedToLocalVal(type, stackValue, -1, parameterIndex);
}
@@ -588,11 +597,7 @@ public class InlineCodegen extends CallGenerator {
}
/*descriptor is null for captured vars*/
public static boolean shouldPutValue(
@NotNull Type type,
@Nullable StackValue stackValue
) {
private static boolean shouldPutValue(@NotNull Type type, @Nullable StackValue stackValue) {
if (stackValue == null) {
//default or vararg
return true;
@@ -626,7 +631,7 @@ public class InlineCodegen extends CallGenerator {
return true;
}
private void recordParameterValueInLocalVal(ParameterInfo... infos) {
private void recordParameterValueInLocalVal(@NotNull ParameterInfo... infos) {
int[] index = new int[infos.length];
for (int i = 0; i < infos.length; i++) {
ParameterInfo info = infos[i];
@@ -650,34 +655,33 @@ public class InlineCodegen extends CallGenerator {
@Override
public void putHiddenParams() {
if ((getMethodAsmFlags(functionDescriptor, context.getContextKind()) & Opcodes.ACC_STATIC) == 0) {
invocationParamBuilder.addNextParameter(AsmTypes.OBJECT_TYPE, false, null);
invocationParamBuilder.addNextParameter(AsmTypes.OBJECT_TYPE, false);
}
for (JvmMethodParameterSignature param : jvmSignature.getValueParameters()) {
if (param.getKind() == JvmMethodParameterKind.VALUE) {
break;
}
invocationParamBuilder.addNextParameter(param.getAsmType(), false, null);
invocationParamBuilder.addNextParameter(param.getAsmType(), false);
}
invocationParamBuilder.markValueParametesStart();
invocationParamBuilder.markValueParametersStart();
List<ParameterInfo> hiddenParameters = invocationParamBuilder.buildParameters().getReal();
recordParameterValueInLocalVal(hiddenParameters.toArray(new ParameterInfo[hiddenParameters.size()]));
}
public void leaveTemps() {
FrameMap frameMap = codegen.getFrameMap();
private void leaveTemps() {
List<ParameterInfo> infos = invocationParamBuilder.listAllParams();
for (ListIterator<? extends ParameterInfo> iterator = infos.listIterator(infos.size()); iterator.hasPrevious(); ) {
ParameterInfo param = iterator.previous();
if (!param.isSkippedOrRemapped()) {
frameMap.leaveTemp(param.type);
codegen.getFrameMap().leaveTemp(param.type);
}
}
}
/*lambda or callable reference*/
public boolean isInliningParameter(KtExpression expression, ValueParameterDescriptor valueParameterDescriptor) {
private boolean isInliningParameter(@NotNull KtExpression expression, @NotNull ValueParameterDescriptor valueParameterDescriptor) {
//TODO deparenthisise typed
KtExpression deparenthesized = KtPsiUtil.deparenthesize(expression);
@@ -691,17 +695,16 @@ public class InlineCodegen extends CallGenerator {
isInlinableParameterExpression(deparenthesized);
}
protected static boolean isInlinableParameterExpression(KtExpression deparenthesized) {
private static boolean isInlinableParameterExpression(@Nullable KtExpression deparenthesized) {
return deparenthesized instanceof KtLambdaExpression ||
deparenthesized instanceof KtNamedFunction ||
deparenthesized instanceof KtCallableReferenceExpression;
}
public void rememberClosure(KtExpression expression, Type type, ValueParameterDescriptor parameter) {
private void rememberClosure(@NotNull KtExpression expression, @NotNull Type type, @NotNull ValueParameterDescriptor parameter) {
KtExpression lambda = KtPsiUtil.deparenthesize(expression);
assert isInlinableParameterExpression(lambda) : "Couldn't find inline expression in " + expression.getText();
LambdaInfo info = new LambdaInfo(lambda, typeMapper, parameter.isCrossinline());
ParameterInfo closureInfo = invocationParamBuilder.addNextValueParameter(type, true, null, parameter.getIndex());
@@ -710,7 +713,7 @@ public class InlineCodegen extends CallGenerator {
}
@NotNull
protected static Set<String> getDeclarationLabels(@Nullable PsiElement lambdaOrFun, @NotNull DeclarationDescriptor descriptor) {
public static Set<String> getDeclarationLabels(@Nullable PsiElement lambdaOrFun, @NotNull DeclarationDescriptor descriptor) {
Set<String> result = new HashSet<String>();
if (lambdaOrFun != null) {
@@ -737,18 +740,25 @@ public class InlineCodegen extends CallGenerator {
activeLambda = null;
}
public static CodegenContext getContext(@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state, @Nullable KtFile sourceFile) {
@NotNull
public static CodegenContext getContext(
@NotNull DeclarationDescriptor descriptor, @NotNull GenerationState state, @Nullable KtFile sourceFile
) {
if (descriptor instanceof PackageFragmentDescriptor) {
return new PackageContext((PackageFragmentDescriptor) descriptor, state.getRootContext(), null, sourceFile);
}
CodegenContext parent = getContext(descriptor.getContainingDeclaration(), state, sourceFile);
DeclarationDescriptor container = descriptor.getContainingDeclaration();
assert container != null : "No container for descriptor: " + descriptor;
CodegenContext parent = getContext(container, state, sourceFile);
if (descriptor instanceof ScriptDescriptor) {
List<ScriptDescriptor> earlierScripts = state.getReplSpecific().getEarlierScriptsForReplInterpreter();
return parent.intoScript((ScriptDescriptor) descriptor,
earlierScripts == null ? Collections.emptyList() : earlierScripts,
(ClassDescriptor) descriptor, state.getTypeMapper());
return parent.intoScript(
(ScriptDescriptor) descriptor,
earlierScripts == null ? Collections.emptyList() : earlierScripts,
(ClassDescriptor) descriptor, state.getTypeMapper()
);
}
else if (descriptor instanceof ClassDescriptor) {
OwnerKind kind = DescriptorUtils.isInterface(descriptor) ? OwnerKind.DEFAULT_IMPLS : OwnerKind.IMPLEMENTATION;
@@ -758,11 +768,7 @@ public class InlineCodegen extends CallGenerator {
return parent.intoFunction((FunctionDescriptor) descriptor);
}
throw new IllegalStateException("Couldn't build context for " + descriptorName(descriptor));
}
private static String descriptorName(DeclarationDescriptor descriptor) {
return DescriptorRenderer.SHORT_NAMES_IN_TYPES.render(descriptor);
throw new IllegalStateException("Couldn't build context for " + descriptor);
}
@Override
@@ -782,18 +788,11 @@ public class InlineCodegen extends CallGenerator {
}
@Override
public void putValueIfNeeded(
@NotNull Type parameterType,
@NotNull StackValue value
) {
public void putValueIfNeeded(@NotNull Type parameterType, @NotNull StackValue value) {
putValueIfNeeded(parameterType, value, -1);
}
private void putValueIfNeeded(
@NotNull Type parameterType,
@NotNull StackValue value,
int index
) {
private void putValueIfNeeded(@NotNull Type parameterType, @NotNull StackValue value, int index) {
if (shouldPutValue(parameterType, value)) {
value.put(parameterType, codegen.v);
}
@@ -801,17 +800,14 @@ public class InlineCodegen extends CallGenerator {
}
@Override
public void putCapturedValueOnStack(
@NotNull StackValue stackValue, @NotNull Type valueType, int paramIndex
) {
public void putCapturedValueOnStack(@NotNull StackValue stackValue, @NotNull Type valueType, int paramIndex) {
if (shouldPutValue(stackValue.type, stackValue)) {
stackValue.put(stackValue.type, codegen.v);
}
putArgumentOrCapturedToLocalVal(stackValue.type, stackValue, paramIndex, paramIndex);
}
public void generateAndInsertFinallyBlocks(
private void generateAndInsertFinallyBlocks(
@NotNull MethodNode intoNode,
@NotNull List<MethodInliner.PointForExternalFinallyBlocks> insertPoints,
int offsetForFinallyLocalVar
@@ -879,7 +875,7 @@ public class InlineCodegen extends CallGenerator {
//processor.substituteLocalVarTable(intoNode);
}
public void removeFinallyMarkers(@NotNull MethodNode intoNode) {
private void removeFinallyMarkers(@NotNull MethodNode intoNode) {
if (InlineCodegenUtil.isFinallyMarkerRequired(codegen.getContext())) return;
InsnList instructions = intoNode.instructions;
@@ -898,6 +894,7 @@ public class InlineCodegen extends CallGenerator {
}
}
@NotNull
public static SourceMapper createNestedSourceMapper(@NotNull SMAPAndMethodNode nodeAndSmap, @NotNull SourceMapper parent) {
return new NestedSourceMapper(parent, nodeAndSmap.getSortedRanges(), nodeAndSmap.getClassSMAP().getSourceInfo());
}
@@ -912,7 +909,8 @@ public class InlineCodegen extends CallGenerator {
if (incrementalCache == null) return;
String classFilePath = InlineCodegenUtilsKt.getClassFilePath(sourceDescriptor, state.getTypeMapper(), incrementalCache);
String sourceFilePath = InlineCodegenUtilsKt.getSourceFilePath(targetDescriptor);
incrementalCache.registerInline(classFilePath, jvmSignature.toString(), sourceFilePath);
Method method = jvmSignature.getAsmMethod();
incrementalCache.registerInline(classFilePath, method.getName() + method.getDescriptor(), sourceFilePath);
}
@Override
@@ -65,7 +65,7 @@ class InlineCodegenForDefaultBody(
}
override fun genCallInner(callableMethod: Callable, resolvedCall: ResolvedCall<*>?, callDefault: Boolean, codegen: ExpressionCodegen) {
val nodeAndSmap = InlineCodegen.createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault, state)
val nodeAndSmap = InlineCodegen.createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault)
val childSourceMapper = InlineCodegen.createNestedSourceMapper(nodeAndSmap, sourceMapper)
val node = nodeAndSmap.node
@@ -111,4 +111,4 @@ class InlineCodegenForDefaultBody(
override fun reorderArgumentsIfNeeded(actualArgsWithDeclIndex: List<ArgumentAndDeclIndex>, valueParameterTypes: List<Type>) {
throw UnsupportedOperationException("Shouldn't be called")
}
}
}
@@ -17,12 +17,10 @@
package org.jetbrains.kotlin.codegen.inline;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import kotlin.text.StringsKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import org.jetbrains.kotlin.backend.common.output.OutputFile;
import org.jetbrains.kotlin.codegen.MemberCodegen;
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
@@ -31,7 +29,6 @@ import org.jetbrains.kotlin.codegen.context.CodegenContextUtil;
import org.jetbrains.kotlin.codegen.context.InlineLambdaContext;
import org.jetbrains.kotlin.codegen.context.MethodContext;
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;
@@ -63,19 +60,19 @@ public class InlineCodegenUtil {
public static final boolean GENERATE_SMAP = true;
public static final int API = Opcodes.ASM5;
public static final String CAPTURED_FIELD_PREFIX = "$";
public static final String NON_CAPTURED_FIELD_PREFIX = "$$";
private static final String CAPTURED_FIELD_PREFIX = "$";
private static final String NON_CAPTURED_FIELD_PREFIX = "$$";
public static final String THIS$0 = "this$0";
public static final String THIS = "this";
public static final String RECEIVER$0 = "receiver$0";
public static final String NON_LOCAL_RETURN = "$$$$$NON_LOCAL_RETURN$$$$$";
private static final String RECEIVER$0 = "receiver$0";
private static final String NON_LOCAL_RETURN = "$$$$$NON_LOCAL_RETURN$$$$$";
public static final String FIRST_FUN_LABEL = "$$$$$ROOT$$$$$";
public static final String NUMBERED_FUNCTION_PREFIX = "kotlin/jvm/functions/Function";
public static final String INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker";
public static final String INLINE_MARKER_BEFORE_METHOD_NAME = "beforeInlineCall";
public static final String INLINE_MARKER_AFTER_METHOD_NAME = "afterInlineCall";
public static final String INLINE_MARKER_FINALLY_START = "finallyStart";
public static final String INLINE_MARKER_FINALLY_END = "finallyEnd";
private static final String INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker";
private static final String INLINE_MARKER_BEFORE_METHOD_NAME = "beforeInlineCall";
private static final String INLINE_MARKER_AFTER_METHOD_NAME = "afterInlineCall";
private static final String INLINE_MARKER_FINALLY_START = "finallyStart";
private static final String INLINE_MARKER_FINALLY_END = "finallyEnd";
public static final String INLINE_TRANSFORMATION_SUFFIX = "$inlined";
public static final String INLINE_FUN_THIS_0_SUFFIX = "$inline_fun";
public static final String INLINE_FUN_VAR_SUFFIX = "$iv";
@@ -93,6 +90,7 @@ public class InlineCodegenUtil {
final int[] lines = new int[2];
lines[0] = Integer.MAX_VALUE;
lines[1] = Integer.MIN_VALUE;
//noinspection PointlessBitwiseExpression
cr.accept(new ClassVisitor(API) {
@Override
public void visit(int version, int access, @NotNull String name, String signature, String superName, String[] interfaces) {
@@ -174,7 +172,7 @@ public class InlineCodegenUtil {
}
@Nullable
public static VirtualFile findVirtualFileImprecise(@NotNull GenerationState state, @NotNull String internalClassName) {
private static VirtualFile findVirtualFileImprecise(@NotNull GenerationState state, @NotNull String internalClassName) {
FqName packageFqName = JvmClassName.byInternalName(internalClassName).getPackageFqName();
String classNameWithDollars = StringsKt.substringAfterLast(internalClassName, "/", internalClassName);
//TODO: we cannot construct proper classId at this point, we need to read InnerClasses info from class file
@@ -182,6 +180,7 @@ public class InlineCodegenUtil {
return findVirtualFile(state, new ClassId(packageFqName, Name.identifier(classNameWithDollars)));
}
@NotNull
public static String getInlineName(
@NotNull CodegenContext codegenContext,
@NotNull KotlinTypeMapper typeMapper,
@@ -190,6 +189,7 @@ public class InlineCodegenUtil {
return getInlineName(codegenContext, codegenContext.getContextDescriptor(), typeMapper, fileClassesManager);
}
@NotNull
private static String getInlineName(
@NotNull CodegenContext codegenContext,
@NotNull DeclarationDescriptor currentDescriptor,
@@ -197,21 +197,24 @@ public class InlineCodegenUtil {
@NotNull JvmFileClassesProvider fileClassesProvider
) {
if (currentDescriptor instanceof PackageFragmentDescriptor) {
PsiFile file = getContainingFile(codegenContext);
PsiFile file = DescriptorToSourceUtils.getContainingFile(codegenContext.getContextDescriptor());
Type implementationOwnerType;
if (file == null) {
implementationOwnerType = CodegenContextUtil.getImplementationOwnerClassType(codegenContext);
} else {
}
else {
implementationOwnerType = FileClasses.getFileClassType(fileClassesProvider, (KtFile) file);
}
if (implementationOwnerType == null) {
DeclarationDescriptor contextDescriptor = codegenContext.getContextDescriptor();
//noinspection ConstantConditions
throw new RuntimeException("Couldn't find declaration for " +
contextDescriptor.getContainingDeclaration().getName() + "." + contextDescriptor.getName() +
"; context: " + codegenContext);
throw new RuntimeException(
"Couldn't find declaration for " +
contextDescriptor.getContainingDeclaration().getName() + "." + contextDescriptor.getName() +
"; context: " + codegenContext
);
}
return implementationOwnerType.getInternalName();
@@ -219,12 +222,12 @@ public class InlineCodegenUtil {
else if (currentDescriptor instanceof ClassifierDescriptor) {
Type type = typeMapper.mapType((ClassifierDescriptor) currentDescriptor);
return type.getInternalName();
} else if (currentDescriptor instanceof FunctionDescriptor) {
}
else if (currentDescriptor instanceof FunctionDescriptor) {
ClassDescriptor descriptor =
typeMapper.getBindingContext().get(CodegenBinding.CLASS_FOR_CALLABLE, (FunctionDescriptor) currentDescriptor);
if (descriptor != null) {
Type type = typeMapper.mapType(descriptor);
return type.getInternalName();
return typeMapper.mapType(descriptor).getInternalName();
}
}
@@ -246,14 +249,15 @@ public class InlineCodegenUtil {
}
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);
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);
}
public static boolean isAnonymousClass(String internalName) {
public static boolean isAnonymousClass(@NotNull String internalName) {
String shortName = getLastNamePart(internalName);
int index = shortName.lastIndexOf("$");
@@ -271,16 +275,6 @@ public class InlineCodegenUtil {
return index < 0 ? internalName : internalName.substring(index + 1);
}
@Nullable
public static PsiFile getContainingFile(CodegenContext codegenContext) {
DeclarationDescriptor contextDescriptor = codegenContext.getContextDescriptor();
PsiElement psiElement = DescriptorToSourceUtils.descriptorToDeclaration(contextDescriptor);
if (psiElement != null) {
return psiElement.getContainingFile();
}
return null;
}
@NotNull
public static MethodVisitor wrapWithMaxLocalCalc(@NotNull MethodNode methodNode) {
return new MaxStackFrameSizeAndLocalsCalculator(API, methodNode.access, methodNode.desc, methodNode);
@@ -317,7 +311,8 @@ public class InlineCodegenUtil {
return getMarkedReturnLabelOrNull(returnIns) != null;
}
public static @Nullable String getMarkedReturnLabelOrNull(@NotNull AbstractInsnNode returnInsn) {
@Nullable
public static String getMarkedReturnLabelOrNull(@NotNull AbstractInsnNode returnInsn) {
if (!isReturnOpcode(returnInsn.getOpcode())) {
return null;
}
@@ -335,30 +330,33 @@ public class InlineCodegenUtil {
iv.invokestatic(NON_LOCAL_RETURN, labelName, "()V", false);
}
@NotNull
public static Type getReturnType(int opcode) {
switch (opcode) {
case Opcodes.RETURN: return Type.VOID_TYPE;
case Opcodes.IRETURN: return Type.INT_TYPE;
case Opcodes.DRETURN: return Type.DOUBLE_TYPE;
case Opcodes.FRETURN: return Type.FLOAT_TYPE;
case Opcodes.LRETURN: return Type.LONG_TYPE;
default: return AsmTypes.OBJECT_TYPE;
}
}
public static void insertNodeBefore(@NotNull MethodNode from, @NotNull InsnList instructions, @NotNull AbstractInsnNode beforeNode) {
ListIterator<AbstractInsnNode> iterator = from.instructions.iterator();
while (iterator.hasNext()) {
AbstractInsnNode next = iterator.next();
instructions.insertBefore(beforeNode, next);
case Opcodes.RETURN:
return Type.VOID_TYPE;
case Opcodes.IRETURN:
return Type.INT_TYPE;
case Opcodes.DRETURN:
return Type.DOUBLE_TYPE;
case Opcodes.FRETURN:
return Type.FLOAT_TYPE;
case Opcodes.LRETURN:
return Type.LONG_TYPE;
default:
return AsmTypes.OBJECT_TYPE;
}
}
public static void insertNodeBefore(@NotNull MethodNode from, @NotNull MethodNode to, @NotNull AbstractInsnNode beforeNode) {
insertNodeBefore(from, to.instructions, beforeNode);
ListIterator<AbstractInsnNode> iterator = from.instructions.iterator();
while (iterator.hasNext()) {
AbstractInsnNode next = iterator.next();
to.instructions.insertBefore(beforeNode, next);
}
}
@NotNull
public static MethodNode createEmptyMethodNode() {
return new MethodNode(API, 0, "fake", "()V", null, null);
}
@@ -374,11 +372,7 @@ public class InlineCodegenUtil {
@NotNull
public static String getNodeText(@Nullable MethodNode node) {
return getNodeText(node, new Textifier());
}
@NotNull
public static String getNodeText(@Nullable MethodNode node, @NotNull Textifier textifier) {
Textifier textifier = new Textifier();
if (node == null) {
return "Not generated";
}
@@ -386,7 +380,7 @@ public class InlineCodegenUtil {
StringWriter sw = new StringWriter();
textifier.print(new PrintWriter(sw));
sw.flush();
return node.name + " " + node.desc + ": \n " + sw.getBuffer().toString();
return node.name + " " + node.desc + ":\n" + sw.getBuffer().toString();
}
@NotNull
@@ -397,13 +391,11 @@ public class InlineCodegenUtil {
if (outputFile != null) {
return new ClassReader(outputFile.asByteArray());
}
else {
VirtualFile file = findVirtualFileImprecise(state, internalName);
if (file == null) {
throw new RuntimeException("Couldn't find virtual file for " + internalName);
}
VirtualFile file = findVirtualFileImprecise(state, internalName);
if (file != null) {
return new ClassReader(file.contentsToByteArray());
}
throw new RuntimeException("Couldn't find virtual file for " + internalName);
}
catch (IOException e) {
throw new RuntimeException(e);
@@ -424,10 +416,10 @@ public class InlineCodegenUtil {
}
public static boolean isFinallyMarker(@Nullable AbstractInsnNode node) {
return isFinallyMarker(node, INLINE_MARKER_FINALLY_END) || isFinallyMarker(node, INLINE_MARKER_FINALLY_START);
return node != null && (isFinallyStart(node) || isFinallyEnd(node));
}
public static boolean isFinallyMarker(@Nullable AbstractInsnNode node, String name) {
private static boolean isFinallyMarker(@NotNull AbstractInsnNode node, String name) {
if (!(node instanceof MethodInsnNode)) return false;
MethodInsnNode method = (MethodInsnNode) node;
return INLINE_MARKER_CLASS_NAME.equals(method.owner) && name.equals(method.name);
@@ -437,69 +429,51 @@ public class InlineCodegenUtil {
return context.isInlineMethodContext() || context instanceof InlineLambdaContext;
}
public static int getConstant(AbstractInsnNode ins) {
public static int getConstant(@NotNull AbstractInsnNode ins) {
int opcode = ins.getOpcode();
Integer value;
if (opcode >= Opcodes.ICONST_0 && opcode <= Opcodes.ICONST_5) {
value = opcode - Opcodes.ICONST_0;
return opcode - Opcodes.ICONST_0;
}
else if (opcode == Opcodes.BIPUSH || opcode == Opcodes.SIPUSH) {
IntInsnNode index = (IntInsnNode) ins;
value = index.operand;
return ((IntInsnNode) ins).operand;
}
else {
LdcInsnNode index = (LdcInsnNode) ins;
value = (Integer) index.cst;
}
return value;
}
public static class LabelTextifier extends Textifier {
public LabelTextifier() {
super(API);
}
@Nullable
@TestOnly
@SuppressWarnings("UnusedDeclaration")
public String getLabelNameIfExists(@NotNull Label l) {
return labelNames == null ? null : labelNames.get(l);
return (Integer) index.cst;
}
}
public static void addInlineMarker(
@NotNull InstructionAdapter v,
boolean isStartNotEnd
) {
v.visitMethodInsn(Opcodes.INVOKESTATIC, INLINE_MARKER_CLASS_NAME,
(isStartNotEnd ? INLINE_MARKER_BEFORE_METHOD_NAME : INLINE_MARKER_AFTER_METHOD_NAME),
"()V", false);
public static void addInlineMarker(@NotNull InstructionAdapter v, boolean isStartNotEnd) {
v.visitMethodInsn(
Opcodes.INVOKESTATIC, INLINE_MARKER_CLASS_NAME,
isStartNotEnd ? INLINE_MARKER_BEFORE_METHOD_NAME : INLINE_MARKER_AFTER_METHOD_NAME,
"()V", false
);
}
public static boolean isInlineMarker(AbstractInsnNode insn) {
public static boolean isInlineMarker(@NotNull AbstractInsnNode insn) {
return isInlineMarker(insn, null);
}
public static boolean isInlineMarker(AbstractInsnNode insn, String name) {
if (insn instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insn;
return insn.getOpcode() == Opcodes.INVOKESTATIC &&
methodInsnNode.owner.equals(INLINE_MARKER_CLASS_NAME) &&
(name != null ? methodInsnNode.name.equals(name)
: methodInsnNode.name.equals(INLINE_MARKER_BEFORE_METHOD_NAME) ||
methodInsnNode.name.equals(INLINE_MARKER_AFTER_METHOD_NAME));
}
else {
private static boolean isInlineMarker(@NotNull AbstractInsnNode insn, @Nullable String name) {
if (!(insn instanceof MethodInsnNode)) {
return false;
}
MethodInsnNode methodInsnNode = (MethodInsnNode) insn;
return insn.getOpcode() == Opcodes.INVOKESTATIC &&
methodInsnNode.owner.equals(INLINE_MARKER_CLASS_NAME) &&
(name != null ? methodInsnNode.name.equals(name)
: methodInsnNode.name.equals(INLINE_MARKER_BEFORE_METHOD_NAME) ||
methodInsnNode.name.equals(INLINE_MARKER_AFTER_METHOD_NAME));
}
public static boolean isBeforeInlineMarker(AbstractInsnNode insn) {
public static boolean isBeforeInlineMarker(@NotNull AbstractInsnNode insn) {
return isInlineMarker(insn, INLINE_MARKER_BEFORE_METHOD_NAME);
}
public static boolean isAfterInlineMarker(AbstractInsnNode insn) {
public static boolean isAfterInlineMarker(@NotNull AbstractInsnNode insn) {
return isInlineMarker(insn, INLINE_MARKER_AFTER_METHOD_NAME);
}
@@ -511,44 +485,27 @@ public class InlineCodegenUtil {
return opcode >= Opcodes.ISTORE && opcode <= Opcodes.ASTORE;
}
public static int calcMarkerShift(Parameters parameters, MethodNode node) {
public static int calcMarkerShift(@NotNull Parameters parameters, @NotNull MethodNode node) {
int markerShiftTemp = getIndexAfterLastMarker(node);
return markerShiftTemp - parameters.getRealArgsSizeOnStack() + parameters.getArgsSizeOnStack();
}
protected static int getIndexAfterLastMarker(MethodNode node) {
int markerShiftTemp = -1;
private static int getIndexAfterLastMarker(@NotNull MethodNode node) {
int result = -1;
for (LocalVariableNode variable : node.localVariables) {
if (isFakeLocalVariableForInline(variable.name)) {
markerShiftTemp = Math.max(markerShiftTemp, variable.index + 1);
result = Math.max(result, variable.index + 1);
}
}
return markerShiftTemp;
}
public static boolean isFakeLocalVariableForInline(@NotNull String name) {
return name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) || name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT);
}
public static boolean isThis0(String name) {
return THIS$0.equals(name);
}
@Nullable
public static AbstractInsnNode getPrevMeaningful(@NotNull AbstractInsnNode node) {
AbstractInsnNode result = node.getPrevious();
while (result != null && !UtilKt.isMeaningful(result)) {
result = result.getPrevious();
}
return result;
}
public static void removeInterval(@NotNull MethodNode node, @NotNull AbstractInsnNode startInc, @NotNull AbstractInsnNode endInc) {
while (startInc != endInc) {
AbstractInsnNode next = startInc.getNext();
node.instructions.remove(startInc);
startInc = next;
}
node.instructions.remove(startInc);
public static boolean isFakeLocalVariableForInline(@NotNull String name) {
return name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) ||
name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT);
}
public static boolean isThis0(@NotNull String name) {
return THIS$0.equals(name);
}
}
@@ -24,7 +24,6 @@ import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode;
import java.util.Collection;
public class InlinedLambdaRemapper extends FieldRemapper {
public InlinedLambdaRemapper(
@NotNull String lambdaInternalName,
@NotNull FieldRemapper parent,
@@ -34,16 +33,13 @@ public class InlinedLambdaRemapper extends FieldRemapper {
}
@Override
public boolean canProcess(@NotNull String fieldOwner, String fieldName, boolean isFolding) {
return isFolding && super.canProcess(fieldOwner, fieldName, isFolding);
public boolean canProcess(@NotNull String fieldOwner, @NotNull String fieldName, boolean isFolding) {
return isFolding && super.canProcess(fieldOwner, fieldName, true);
}
@Override
@Nullable
public CapturedParamInfo findField(
@NotNull FieldInsnNode fieldInsnNode,
@NotNull Collection<CapturedParamInfo> captured
) {
public CapturedParamInfo findField(@NotNull FieldInsnNode fieldInsnNode, @NotNull Collection<CapturedParamInfo> captured) {
return parent.findField(fieldInsnNode, captured);
}
@@ -52,14 +48,12 @@ public class InlinedLambdaRemapper extends FieldRemapper {
return true;
}
@Nullable
@Override
public StackValue getFieldForInline(@NotNull FieldInsnNode node, @Nullable StackValue prefix) {
if (parent.isRoot()) {
return super.getFieldForInline(node, prefix);
} else {
return parent.getFieldForInline(node, prefix);
}
return parent.getFieldForInline(node, prefix);
}
}
@@ -25,87 +25,76 @@ import java.util.HashMap;
import java.util.Map;
public class InliningContext {
@Nullable
private final InliningContext parent;
public final Map<Integer, LambdaInfo> expressionMap;
private final Map<Integer, LambdaInfo> expressionMap;
public final GenerationState state;
public final NameGenerator nameGenerator;
public final TypeRemapper typeRemapper;
public final ReifiedTypeInliner reifedTypeInliner;
public final ReifiedTypeInliner reifiedTypeInliner;
public final boolean isInliningLambda;
public final boolean classRegeneration;
protected InliningContext(
public InliningContext(
@Nullable InliningContext parent,
@NotNull Map<Integer, LambdaInfo> map,
@NotNull Map<Integer, LambdaInfo> expressionMap,
@NotNull GenerationState state,
@NotNull NameGenerator nameGenerator,
@NotNull TypeRemapper typeRemapper,
@NotNull ReifiedTypeInliner reifedTypeInliner,
@NotNull ReifiedTypeInliner reifiedTypeInliner,
boolean isInliningLambda,
boolean classRegeneration
) {
this.parent = parent;
expressionMap = map;
this.expressionMap = expressionMap;
this.state = state;
this.nameGenerator = nameGenerator;
this.typeRemapper = typeRemapper;
this.reifedTypeInliner = reifedTypeInliner;
this.reifiedTypeInliner = reifiedTypeInliner;
this.isInliningLambda = isInliningLambda;
this.classRegeneration = classRegeneration;
}
public InliningContext subInline(NameGenerator generator) {
return subInline(generator, Collections.<String, String>emptyMap());
@NotNull
public InliningContext subInline(@NotNull NameGenerator generator) {
return subInline(generator, Collections.<String, String>emptyMap(), isInliningLambda);
}
public InliningContext subInlineLambda(LambdaInfo lambdaInfo) {
@NotNull
public InliningContext subInlineLambda(@NotNull LambdaInfo lambdaInfo) {
Map<String, String> map = new HashMap<String, String>();
map.put(lambdaInfo.getLambdaClassType().getInternalName(), null); //mark lambda inlined
return subInline(nameGenerator.subGenerator("lambda"), map, true);
}
private InliningContext subInline(NameGenerator generator, Map<String, String> additionalTypeMappings) {
return subInline(generator, additionalTypeMappings, isInliningLambda);
}
@NotNull
public InliningContext subInlineWithClassRegeneration(
@NotNull NameGenerator generator,
@NotNull Map<String, String> newTypeMappings,
@NotNull InlineCallSiteInfo callSiteInfo
) {
return new RegeneratedClassContext(this, expressionMap, state, generator,
TypeRemapper.createFrom(typeRemapper, newTypeMappings),
reifedTypeInliner, isInliningLambda, callSiteInfo);
}
public InliningContext subInline(NameGenerator generator, Map<String, String> additionalTypeMappings, boolean isInliningLambda) {
return subInline(generator, additionalTypeMappings, isInliningLambda, classRegeneration);
return new RegeneratedClassContext(
this, expressionMap, state, generator, TypeRemapper.createFrom(typeRemapper, newTypeMappings),
reifiedTypeInliner, isInliningLambda, callSiteInfo
);
}
@NotNull
private InliningContext subInline(
NameGenerator generator,
Map<String, String> additionalTypeMappings,
boolean isInliningLambda,
boolean isRegeneration
@NotNull NameGenerator generator, @NotNull Map<String, String> additionalTypeMappings, boolean isInliningLambda
) {
//isInliningLambda && !this.isInliningLambda for root inline lambda
return new InliningContext(this, expressionMap, state, generator,
TypeRemapper.createFrom(
typeRemapper,
additionalTypeMappings,
//root inline lambda
isInliningLambda && !this.isInliningLambda
),
reifedTypeInliner, isInliningLambda, isRegeneration);
return new InliningContext(
this, expressionMap, state, generator,
TypeRemapper.createFrom(
typeRemapper,
additionalTypeMappings,
//root inline lambda
isInliningLambda && !this.isInliningLambda
),
reifiedTypeInliner, isInliningLambda, classRegeneration
);
}
public boolean isRoot() {
@@ -114,12 +103,8 @@ public class InliningContext {
@NotNull
public RootInliningContext getRoot() {
if (isRoot()) {
return (RootInliningContext) this;
}
else {
return parent.getRoot();
}
//noinspection ConstantConditions
return isRoot() ? (RootInliningContext) this : parent.getRoot();
}
@Nullable
@@ -127,11 +112,7 @@ public class InliningContext {
return parent;
}
public boolean isInliningLambdaRootContext() {
//noinspection ConstantConditions
return isInliningLambda && !getParent().isInliningLambda;
}
@NotNull
public InlineCallSiteInfo getCallSiteInfo() {
assert parent != null : "At least root context should return proper value";
return parent.getCallSiteInfo();
@@ -40,36 +40,26 @@ import java.util.Set;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
public class LambdaInfo implements CapturedParamOwner, LabelOwner {
public final KtExpression expression;
private final KotlinTypeMapper typeMapper;
@NotNull
public final Set<String> labels;
private final CalculatedClosure closure;
public final boolean isCrossInline;
private SMAPAndMethodNode node;
private List<CapturedParamDesc> capturedVars;
private final FunctionDescriptor functionDescriptor;
private final ClassDescriptor classDescriptor;
private final Type closureClassType;
LambdaInfo(@NotNull KtExpression expr, @NotNull KotlinTypeMapper typeMapper, boolean isCrossInline) {
private SMAPAndMethodNode node;
private List<CapturedParamDesc> capturedVars;
public LambdaInfo(@NotNull KtExpression expression, @NotNull KotlinTypeMapper typeMapper, boolean isCrossInline) {
this.isCrossInline = isCrossInline;
this.expression = expr instanceof KtLambdaExpression ?
((KtLambdaExpression) expr).getFunctionLiteral() : expr;
this.expression = expression instanceof KtLambdaExpression ?
((KtLambdaExpression) expression).getFunctionLiteral() : expression;
this.typeMapper = typeMapper;
BindingContext bindingContext = typeMapper.getBindingContext();
functionDescriptor = bindingContext.get(BindingContext.FUNCTION, expression);
functionDescriptor = bindingContext.get(BindingContext.FUNCTION, this.expression);
assert functionDescriptor != null : "Function is not resolved to descriptor: " + expression.getText();
classDescriptor = anonymousClassForCallable(bindingContext, functionDescriptor);
@@ -78,34 +68,39 @@ public class LambdaInfo implements CapturedParamOwner, LabelOwner {
closure = bindingContext.get(CLOSURE, classDescriptor);
assert closure != null : "Closure for lambda should be not null " + expression.getText();
labels = InlineCodegen.getDeclarationLabels(expr, functionDescriptor);
labels = InlineCodegen.getDeclarationLabels(expression, functionDescriptor);
}
@NotNull
public SMAPAndMethodNode getNode() {
return node;
}
public void setNode(SMAPAndMethodNode node) {
public void setNode(@NotNull SMAPAndMethodNode node) {
this.node = node;
}
@NotNull
public FunctionDescriptor getFunctionDescriptor() {
return functionDescriptor;
}
@NotNull
public KtExpression getFunctionWithBodyOrCallableReference() {
return expression;
}
@NotNull
public ClassDescriptor getClassDescriptor() {
return classDescriptor;
}
@NotNull
public Type getLambdaClassType() {
return closureClassType;
}
@NotNull
public List<CapturedParamDesc> getCapturedVars() {
//lazy initialization cause it would be calculated after object creation
if (capturedVars == null) {
@@ -114,11 +109,12 @@ public class LambdaInfo implements CapturedParamOwner, LabelOwner {
if (closure.getCaptureThis() != null) {
Type type = typeMapper.mapType(closure.getCaptureThis());
EnclosedValueDescriptor descriptor =
new EnclosedValueDescriptor(AsmUtil.CAPTURED_THIS_FIELD,
null,
StackValue.field(type, closureClassType, AsmUtil.CAPTURED_THIS_FIELD, false,
StackValue.LOCAL_0),
type);
new EnclosedValueDescriptor(
AsmUtil.CAPTURED_THIS_FIELD,
/* descriptor = */ null,
StackValue.field(type, closureClassType, AsmUtil.CAPTURED_THIS_FIELD, false, StackValue.LOCAL_0),
type
);
capturedVars.add(getCapturedParamInfo(descriptor));
}
@@ -127,10 +123,10 @@ public class LambdaInfo implements CapturedParamOwner, LabelOwner {
EnclosedValueDescriptor descriptor =
new EnclosedValueDescriptor(
AsmUtil.CAPTURED_RECEIVER_FIELD,
null,
StackValue.field(type, closureClassType, AsmUtil.CAPTURED_RECEIVER_FIELD, false,
StackValue.LOCAL_0),
type);
/* descriptor = */ null,
StackValue.field(type, closureClassType, AsmUtil.CAPTURED_RECEIVER_FIELD, false, StackValue.LOCAL_0),
type
);
capturedVars.add(getCapturedParamInfo(descriptor));
}
@@ -143,29 +139,29 @@ public class LambdaInfo implements CapturedParamOwner, LabelOwner {
@NotNull
private CapturedParamDesc getCapturedParamInfo(@NotNull EnclosedValueDescriptor descriptor) {
return CapturedParamDesc.createDesc(this, descriptor.getFieldName(), descriptor.getType());
return new CapturedParamDesc(this, descriptor.getFieldName(), descriptor.getType());
}
@NotNull
public List<Type> getInvokeParamsWithoutCaptured() {
Type[] types = typeMapper.mapAsmMethod(functionDescriptor).getArgumentTypes();
return Arrays.asList(types);
return Arrays.asList(typeMapper.mapAsmMethod(functionDescriptor).getArgumentTypes());
}
@NotNull
public Parameters addAllParameters(FieldRemapper remapper) {
public Parameters addAllParameters(@NotNull FieldRemapper remapper) {
Method asmMethod = typeMapper.mapAsmMethod(getFunctionDescriptor());
ParametersBuilder builder =
ParametersBuilder.initializeBuilderFrom(AsmTypes.OBJECT_TYPE, asmMethod.getDescriptor(), this);
ParametersBuilder builder = ParametersBuilder.initializeBuilderFrom(AsmTypes.OBJECT_TYPE, asmMethod.getDescriptor(), this);
for (CapturedParamDesc info : getCapturedVars()) {
CapturedParamInfo field = remapper.findField(new FieldInsnNode(0, info.getContainingLambdaName(), info.getFieldName(), ""));
assert field != null : "Captured field not found: " + info.getContainingLambdaName() + "." + info.getFieldName();
builder.addCapturedParam(field, info.getFieldName());
}
return builder.buildParameters();
}
@NotNull
@Override
public Type getType() {
return closureClassType;
@@ -175,6 +171,4 @@ public class LambdaInfo implements CapturedParamOwner, LabelOwner {
public boolean isMyLabel(@NotNull String name) {
return labels.contains(name);
}
}
@@ -28,18 +28,16 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import static org.jetbrains.kotlin.codegen.inline.LocalVarRemapper.RemapStatus.*;
public class LocalVarRemapper {
private final Parameters params;
private final int actualParamsSize;
private final StackValue[] remapValues;
private final int additionalShift;
public LocalVarRemapper(Parameters params, int additionalShift) {
public LocalVarRemapper(@NotNull Parameters params, int additionalShift) {
this.additionalShift = additionalShift;
this.params = params;
remapValues = new StackValue [params.getArgsSizeOnStack()];
remapValues = new StackValue[params.getArgsSizeOnStack()];
int realSize = 0;
for (ParameterInfo info : params) {
@@ -56,7 +54,8 @@ public class LocalVarRemapper {
actualParamsSize = realSize;
}
public RemapInfo doRemap(int index) {
@NotNull
private RemapInfo doRemap(int index) {
int remappedIndex;
if (index < params.getArgsSizeOnStack()) {
@@ -67,16 +66,20 @@ public class LocalVarRemapper {
}
if (info.isRemapped()) {
return new RemapInfo(remapped, info, REMAPPED);
} else {
remappedIndex = ((StackValue.Local)remapped).index;
}
} else {
remappedIndex = actualParamsSize - params.getArgsSizeOnStack() + index; //captured params not used directly in this inlined method, they used in closure
else {
remappedIndex = ((StackValue.Local) remapped).index;
}
}
else {
//captured params are not used directly in this inlined method, they are used in closure
remappedIndex = actualParamsSize - params.getArgsSizeOnStack() + index;
}
return new RemapInfo(StackValue.local(remappedIndex + additionalShift, AsmTypes.OBJECT_TYPE), null, SHIFT);
}
@NotNull
public RemapInfo remap(int index) {
RemapInfo info = doRemap(index);
if (FAIL == info.status) {
@@ -86,23 +89,29 @@ public class LocalVarRemapper {
return info;
}
public void visitIincInsn(int var, int increment, MethodVisitor mv) {
public void visitIincInsn(int var, int increment, @NotNull MethodVisitor mv) {
RemapInfo remap = remap(var);
assert remap.value instanceof StackValue.Local;
assert remap.value instanceof StackValue.Local : "Remapped value should be a local: " + remap.value;
mv.visitIincInsn(((StackValue.Local) remap.value).index, increment);
}
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index, MethodVisitor mv) {
public void visitLocalVariable(
@NotNull String name,
@NotNull String desc,
@Nullable String signature,
@NotNull Label start,
@NotNull Label end,
int index,
MethodVisitor mv
) {
RemapInfo info = doRemap(index);
//add entries only for shifted vars
if (SHIFT == info.status) {
int newIndex = ((StackValue.Local) info.value).index;
mv.visitLocalVariable(name, desc, signature, start, end, newIndex);
mv.visitLocalVariable(name, desc, signature, start, end, ((StackValue.Local) info.value).index);
}
}
public void visitVarInsn(int opcode, int var, InstructionAdapter mv) {
public void visitVarInsn(int opcode, int var, @NotNull InstructionAdapter mv) {
RemapInfo remapInfo = remap(var);
StackValue value = remapInfo.value;
if (value instanceof StackValue.Local) {
@@ -117,7 +126,8 @@ public class LocalVarRemapper {
if (remapInfo.parameterInfo != null) {
StackValue.coerce(value.type, remapInfo.parameterInfo.type, mv);
}
} else {
}
else {
assert remapInfo.parameterInfo != null : "Non local value should have parameter info";
value.put(remapInfo.parameterInfo.type, mv);
}
@@ -134,15 +144,15 @@ public class LocalVarRemapper {
public final ParameterInfo parameterInfo;
public final RemapStatus status;
public RemapInfo(@NotNull StackValue value, @Nullable ParameterInfo info, RemapStatus remapStatus) {
public RemapInfo(@NotNull StackValue value, @Nullable ParameterInfo parameterInfo, @NotNull RemapStatus remapStatus) {
this.value = value;
parameterInfo = info;
this.parameterInfo = parameterInfo;
this.status = remapStatus;
}
public RemapInfo(@NotNull ParameterInfo info) {
public RemapInfo(@NotNull ParameterInfo parameterInfo) {
this.value = null;
parameterInfo = info;
this.parameterInfo = parameterInfo;
this.status = FAIL;
}
}
@@ -54,7 +54,7 @@ public class MethodBodyVisitor extends InstructionAdapter {
}
@Override
public void visitAttribute(Attribute attr) {
public void visitAttribute(@NotNull Attribute attr) {
}
@Override
@@ -43,45 +43,24 @@ import java.util.*;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*;
public class MethodInliner {
private final MethodNode node;
private final Parameters parameters;
private final InliningContext inliningContext;
private final FieldRemapper nodeRemapper;
private final boolean isSameModule;
private final String errorPrefix;
private final SourceMapper sourceMapper;
private final InlineCallSiteInfo inlineCallSiteInfo;
private final KotlinTypeMapper typeMapper;
private final List<InvokeCall> invokeCalls = new ArrayList<InvokeCall>();
//keeps order
private final List<TransformationInfo> transformations = new ArrayList<TransformationInfo>();
//current state
private final Map<String, String> currentTypeMapping = new HashMap<String, String>();
private final InlineResult result;
private int lambdasFinallyBlocks;
private final InlineOnlySmapSkipper inlineOnlySmapSkipper;
/*
*
* @param node
* @param parameters
* @param inliningContext
* @param lambdaType - in case on lambda 'invoke' inlining
*/
public MethodInliner(
@NotNull MethodNode node,
@NotNull Parameters parameters,
@@ -106,6 +85,7 @@ public class MethodInliner {
this.inlineOnlySmapSkipper = smapSkipper;
}
@NotNull
public InlineResult doInline(
@NotNull MethodVisitor adapter,
@NotNull LocalVarRemapper remapper,
@@ -115,6 +95,7 @@ public class MethodInliner {
return doInline(adapter, remapper, remapReturn, labelOwner, 0);
}
@NotNull
private InlineResult doInline(
@NotNull MethodVisitor adapter,
@NotNull LocalVarRemapper remapper,
@@ -129,11 +110,12 @@ public class MethodInliner {
Label end = new Label();
transformedNode = doInline(transformedNode);
removeClosureAssertions(transformedNode);
InsnList instructions = transformedNode.instructions;
instructions.resetLabels();
transformedNode.instructions.resetLabels();
MethodNode resultNode = new MethodNode(InlineCodegenUtil.API, transformedNode.access, transformedNode.name, transformedNode.desc,
transformedNode.signature, ArrayUtil.toStringArray(transformedNode.exceptions));
MethodNode resultNode = new MethodNode(
InlineCodegenUtil.API, transformedNode.access, transformedNode.name, transformedNode.desc,
transformedNode.signature, ArrayUtil.toStringArray(transformedNode.exceptions)
);
RemapVisitor visitor = new RemapVisitor(resultNode, remapper, nodeRemapper);
try {
transformedNode.accept(visitor);
@@ -145,7 +127,10 @@ public class MethodInliner {
resultNode.visitLabel(end);
if (inliningContext.isRoot()) {
InternalFinallyBlockInliner.processInlineFunFinallyBlocks(resultNode, lambdasFinallyBlocks, ((StackValue.Local)remapper.remap(parameters.getArgsSizeOnStack() + 1).value).index);
StackValue remapValue = remapper.remap(parameters.getArgsSizeOnStack() + 1).value;
InternalFinallyBlockInliner.processInlineFunFinallyBlocks(
resultNode, lambdasFinallyBlocks, ((StackValue.Local) remapValue).index
);
}
processReturns(resultNode, labelOwner, remapReturn, end);
@@ -156,8 +141,8 @@ public class MethodInliner {
return result;
}
private MethodNode doInline(final MethodNode node) {
@NotNull
private MethodNode doInline(@NotNull MethodNode node) {
final Deque<InvokeCall> currentInvokes = new LinkedList<InvokeCall>(invokeCalls);
final MethodNode resultNode = new MethodNode(node.access, node.name, node.desc, node.signature, null);
@@ -174,8 +159,8 @@ public class MethodInliner {
final int markerShift = InlineCodegenUtil.calcMarkerShift(parameters, node);
InlineAdapter lambdaInliner = new InlineAdapter(remappingMethodAdapter, parameters.getArgsSizeOnStack(), sourceMapper) {
private TransformationInfo transformationInfo;
private void handleAnonymousObjectRegeneration() {
transformationInfo = iterator.next();
@@ -190,10 +175,7 @@ public class MethodInliner {
currentTypeMapping,
inlineCallSiteInfo
);
ObjectTransformer transformer = transformationInfo.createTransformer(
childInliningContext,
isSameModule
);
ObjectTransformer transformer = transformationInfo.createTransformer(childInliningContext, isSameModule);
InlineResult transformResult = transformer.doTransform(nodeRemapper);
result.addAllClassesToRemove(transformResult);
@@ -213,7 +195,7 @@ public class MethodInliner {
@Override
public void anew(@NotNull Type type) {
if (isAnonymousConstructorCall(type.getInternalName(), "<init>")) {
if (isAnonymousClass(type.getInternalName())) {
handleAnonymousObjectRegeneration();
}
@@ -245,23 +227,25 @@ public class MethodInliner {
setLambdaInlining(true);
SMAP lambdaSMAP = info.getNode().getClassSMAP();
//noinspection ConstantConditions
SourceMapper mapper =
inliningContext.classRegeneration && !inliningContext.isInliningLambda ?
new NestedSourceMapper(sourceMapper, lambdaSMAP.getIntervals(), lambdaSMAP.getSourceInfo())
inliningContext.classRegeneration && !inliningContext.isInliningLambda
? new NestedSourceMapper(sourceMapper, lambdaSMAP.getIntervals(), lambdaSMAP.getSourceInfo())
: new InlineLambdaSourceMapper(sourceMapper.getParent(), info.getNode());
MethodInliner inliner = new MethodInliner(info.getNode().getNode(), lambdaParameters,
inliningContext.subInlineLambda(info),
newCapturedRemapper, true /*cause all calls in same module as lambda*/,
"Lambda inlining " + info.getLambdaClassType().getInternalName(),
mapper, inlineCallSiteInfo, null);
MethodInliner inliner = new MethodInliner(
info.getNode().getNode(), lambdaParameters, inliningContext.subInlineLambda(info),
newCapturedRemapper, true /*cause all calls in same module as lambda*/,
"Lambda inlining " + info.getLambdaClassType().getInternalName(),
mapper, inlineCallSiteInfo, null
);
LocalVarRemapper remapper = new LocalVarRemapper(lambdaParameters, valueParamShift);
InlineResult lambdaResult = inliner.doInline(this.mv, remapper, true, info, invokeCall.finallyDepthShift);//TODO add skipped this and receiver
//TODO add skipped this and receiver
InlineResult lambdaResult = inliner.doInline(this.mv, remapper, true, info, invokeCall.finallyDepthShift);
result.addAllClassesToRemove(lambdaResult);
//return value boxing/unboxing
Method bridge =
typeMapper.mapAsmMethod(ClosureCodegen.getErasedInvokeFunction(info.getFunctionDescriptor()));
Method bridge = typeMapper.mapAsmMethod(ClosureCodegen.getErasedInvokeFunction(info.getFunctionDescriptor()));
Method delegate = typeMapper.mapAsmMethod(info.getFunctionDescriptor());
StackValue.onStack(delegate.getReturnType()).put(bridge.getReturnType(), this);
setLambdaInlining(false);
@@ -274,31 +258,38 @@ public class MethodInliner {
else if (isAnonymousConstructorCall(owner, name)) { //TODO add method
//TODO add proper message
assert transformationInfo instanceof AnonymousObjectTransformationInfo :
"<init> call doesn't correspond to object transformation info: " + owner + "." + name + ", info " + transformationInfo;
"<init> call doesn't correspond to object transformation info: " +
owner + "." + name + ", info " + transformationInfo;
if (transformationInfo.shouldRegenerate(isSameModule)) {
//put additional captured parameters on stack
for (CapturedParamDesc capturedParamDesc : ((AnonymousObjectTransformationInfo) transformationInfo).getAllRecapturedParameters()) {
visitFieldInsn(Opcodes.GETSTATIC, capturedParamDesc.getContainingLambdaName(),
"$$$" + capturedParamDesc.getFieldName(), capturedParamDesc.getType().getDescriptor());
AnonymousObjectTransformationInfo info = (AnonymousObjectTransformationInfo) transformationInfo;
for (CapturedParamDesc capturedParamDesc : info.getAllRecapturedParameters()) {
visitFieldInsn(
Opcodes.GETSTATIC, capturedParamDesc.getContainingLambdaName(),
"$$$" + capturedParamDesc.getFieldName(), capturedParamDesc.getType().getDescriptor()
);
}
super.visitMethodInsn(opcode, transformationInfo.getNewClassName(), name,
((AnonymousObjectTransformationInfo) transformationInfo).getNewConstructorDescriptor(), itf);
super.visitMethodInsn(opcode, transformationInfo.getNewClassName(), name, info.getNewConstructorDescriptor(), itf);
//TODO: add new inner class also for other contexts
if (inliningContext.getParent() instanceof RegeneratedClassContext) {
inliningContext.getParent().typeRemapper.addAdditionalMappings(transformationInfo.getOldClassName(), transformationInfo.getNewClassName());
inliningContext.getParent().typeRemapper.addAdditionalMappings(
transformationInfo.getOldClassName(), transformationInfo.getNewClassName()
);
}
transformationInfo = null;
} else {
super.visitMethodInsn(opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf);
}
else {
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
else if (!inliningContext.isInliningLambda && ReifiedTypeInliner.isNeedClassReificationMarker(new MethodInsnNode(opcode, owner, name, desc, false))) {
else if (!inliningContext.isInliningLambda &&
ReifiedTypeInliner.isNeedClassReificationMarker(new MethodInsnNode(opcode, owner, name, desc, false))) {
//we shouldn't process here content of inlining lambda it should be reified at external level
}
else {
super.visitMethodInsn(opcode, changeOwnerForExternalPackage(owner, opcode), name, desc, itf);
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
@@ -315,7 +306,6 @@ public class MethodInliner {
lambdasFinallyBlocks = resultNode.tryCatchBlocks.size();
super.visitMaxs(stack, locals);
}
};
node.accept(lambdaInliner);
@@ -324,18 +314,20 @@ public class MethodInliner {
}
@NotNull
public static CapturedParamInfo findCapturedField(FieldInsnNode node, FieldRemapper fieldRemapper) {
public static CapturedParamInfo findCapturedField(@NotNull FieldInsnNode node, @NotNull FieldRemapper fieldRemapper) {
assert node.name.startsWith("$$$") : "Captured field template should start with $$$ prefix";
FieldInsnNode fin = new FieldInsnNode(node.getOpcode(), node.owner, node.name.substring(3), node.desc);
CapturedParamInfo field = fieldRemapper.findField(fin);
if (field == null) {
throw new IllegalStateException("Couldn't find captured field " + node.owner + "." + node.name + " in " + fieldRemapper.getLambdaInternalName());
throw new IllegalStateException(
"Couldn't find captured field " + node.owner + "." + node.name + " in " + fieldRemapper.getLambdaInternalName()
);
}
return field;
}
@NotNull
public MethodNode prepareNode(@NotNull MethodNode node, int finallyDeepShift) {
private MethodNode prepareNode(@NotNull MethodNode node, int finallyDeepShift) {
final int capturedParamsSize = parameters.getCapturedArgsSizeOnStack();
final int realParametersSize = parameters.getRealArgsSizeOnStack();
Type[] types = Type.getArgumentTypes(node.desc);
@@ -345,8 +337,10 @@ public class MethodInliner {
Type[] allTypes = ArrayUtil.mergeArrays(types, capturedTypes.toArray(new Type[capturedTypes.size()]));
node.instructions.resetLabels();
MethodNode transformedNode = new MethodNode(InlineCodegenUtil.API, node.access, node.name, Type.getMethodDescriptor(returnType, allTypes), node.signature, null) {
MethodNode transformedNode = new MethodNode(
InlineCodegenUtil.API, node.access, node.name, Type.getMethodDescriptor(returnType, allTypes), node.signature, null
) {
@SuppressWarnings("ConstantConditions")
private final boolean GENERATE_DEBUG_INFO = InlineCodegenUtil.GENERATE_SMAP && inlineOnlySmapSkipper == null;
private final boolean isInliningLambda = nodeRemapper.isInsideInliningLambda();
@@ -372,7 +366,7 @@ public class MethodInliner {
@Override
public void visitLineNumber(int line, @NotNull Label start) {
if(isInliningLambda || GENERATE_DEBUG_INFO) {
if (isInliningLambda || GENERATE_DEBUG_INFO) {
super.visitLineNumber(line, start);
}
}
@@ -382,9 +376,8 @@ public class MethodInliner {
@NotNull String name, @NotNull String desc, String signature, @NotNull Label start, @NotNull Label end, int index
) {
if (isInliningLambda || GENERATE_DEBUG_INFO) {
String varSuffix = inliningContext.isRoot() &&
!InlineCodegenUtil.isFakeLocalVariableForInline(name) ?
INLINE_FUN_VAR_SUFFIX : "";
String varSuffix =
inliningContext.isRoot() && !InlineCodegenUtil.isFakeLocalVariableForInline(name) ? INLINE_FUN_VAR_SUFFIX : "";
String varName = !varSuffix.isEmpty() && name.equals("this") ? name + "_" : name;
super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index));
}
@@ -400,7 +393,9 @@ public class MethodInliner {
}
@NotNull
protected MethodNode markPlacesForInlineAndRemoveInlinable(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, int finallyDeepShift) {
private MethodNode markPlacesForInlineAndRemoveInlinable(
@NotNull MethodNode node, @NotNull LabelOwner labelOwner, int finallyDeepShift
) {
node = prepareNode(node, finallyDeepShift);
Frame<SourceValue>[] sources = analyzeMethodNodeBeforeInline(node);
@@ -460,9 +455,7 @@ public class MethodInliner {
}
transformations.add(
buildConstructorInvocation(
owner, desc, lambdaMapping, awaitClassReification
)
buildConstructorInvocation(owner, desc, lambdaMapping, awaitClassReification)
);
awaitClassReification = false;
}
@@ -482,7 +475,8 @@ public class MethodInliner {
else if (isWhenMappingAccess(className, fieldInsnNode.name)) {
transformations.add(
new WhenMappingTransformationInfo(
className, inliningContext.nameGenerator, isAlreadyRegenerated(className), fieldInsnNode)
className, inliningContext.nameGenerator, isAlreadyRegenerated(className), fieldInsnNode
)
);
}
@@ -498,7 +492,8 @@ public class MethodInliner {
//NB: Cause we generate exception table for default handler using gaps (see ExpressionCodegen.visitTryExpression)
//it may occurs that interval for default handler starts before catch start label, so this label seems as dead,
//but as result all this labels will be merged into one (see KT-5863)
} else {
}
else {
toDelete.add(prevNode);
}
}
@@ -522,6 +517,7 @@ public class MethodInliner {
return node;
}
@NotNull
private Frame<SourceValue>[] analyzeMethodNodeBeforeInline(@NotNull MethodNode node) {
try {
new MandatoryMethodTransformer().transform("fake", node);
@@ -545,14 +541,12 @@ public class MethodInliner {
}
};
Frame<SourceValue>[] sources;
try {
sources = analyzer.analyze("fake", node);
return analyzer.analyze("fake", node);
}
catch (AnalyzerException e) {
throw wrapException(e, node, "couldn't inline method call");
}
return sources;
}
private static boolean isEmptyTryInterval(@NotNull TryCatchBlockNode tryCatchBlockNode) {
@@ -595,7 +589,8 @@ public class MethodInliner {
int varIndex = ((VarInsnNode) insnNode).var;
return getLambdaIfExists(varIndex);
}
else if (insnNode instanceof FieldInsnNode) {
if (insnNode instanceof FieldInsnNode) {
FieldInsnNode fieldInsnNode = (FieldInsnNode) insnNode;
if (fieldInsnNode.name.startsWith("$$$")) {
return findCapturedField(fieldInsnNode, nodeRemapper).getLambda();
@@ -605,6 +600,7 @@ public class MethodInliner {
return null;
}
@Nullable
private LambdaInfo getLambdaIfExists(int varIndex) {
if (varIndex < parameters.getArgsSizeOnStack()) {
return parameters.getParameterByDeclarationSlot(varIndex).getLambda();
@@ -612,13 +608,14 @@ public class MethodInliner {
return null;
}
private static void removeClosureAssertions(MethodNode node) {
private static void removeClosureAssertions(@NotNull MethodNode node) {
AbstractInsnNode cur = node.instructions.getFirst();
while (cur != null && cur.getNext() != null) {
AbstractInsnNode next = cur.getNext();
if (next.getType() == AbstractInsnNode.METHOD_INSN) {
MethodInsnNode methodInsnNode = (MethodInsnNode) next;
if (methodInsnNode.name.equals("checkParameterIsNotNull") && methodInsnNode.owner.equals(IntrinsicMethods.INTRINSICS_CLASS_NAME)) {
if (methodInsnNode.name.equals("checkParameterIsNotNull") &&
methodInsnNode.owner.equals(IntrinsicMethods.INTRINSICS_CLASS_NAME)) {
AbstractInsnNode prev = cur.getPrevious();
assert cur.getOpcode() == Opcodes.LDC : "checkParameterIsNotNull should go after LDC but " + cur;
@@ -676,7 +673,7 @@ public class MethodInliner {
}
@NotNull
public static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode aload0) {
private static List<AbstractInsnNode> getCapturedFieldAccessChain(@NotNull VarInsnNode aload0) {
List<AbstractInsnNode> fieldAccessChain = new ArrayList<AbstractInsnNode>();
fieldAccessChain.add(aload0);
AbstractInsnNode next = aload0.getNext();
@@ -697,7 +694,9 @@ public class MethodInliner {
return fieldAccessChain;
}
public static void putStackValuesIntoLocals(List<Type> directOrder, int shift, InstructionAdapter iv, String descriptor) {
private static void putStackValuesIntoLocals(
@NotNull List<Type> directOrder, int shift, @NotNull InstructionAdapter iv, @NotNull String descriptor
) {
Type[] actualParams = Type.getArgumentTypes(descriptor);
assert actualParams.length == directOrder.size() : "Number of expected and actual params should be equals!";
@@ -719,42 +718,21 @@ public class MethodInliner {
}
}
//TODO: check it's external module
//TODO?: assert method exists in facade?
public String changeOwnerForExternalPackage(String type, int opcode) {
//if (isSameModule || (opcode & Opcodes.INVOKESTATIC) == 0) {
// return type;
//}
//JvmClassName name = JvmClassName.byInternalName(type);
//String packageClassInternalName = PackageClassUtils.getPackageClassInternalName(name.getPackageFqName());
//if (type.startsWith(packageClassInternalName + '$')) {
// VirtualFile virtualFile = InlineCodegenUtil.findVirtualFile(inliningContext.state.getProject(), type);
// if (virtualFile != null) {
// KotlinJvmBinaryClass klass = KotlinBinaryClassCache.getKotlinBinaryClass(virtualFile);
// if (klass != null && klass.getClassHeader().getSyntheticClassKind() == KotlinSyntheticClass.Kind.PACKAGE_PART) {
// return packageClassInternalName;
// }
// }
//}
return type;
}
@NotNull
public RuntimeException wrapException(@NotNull Throwable originalException, @NotNull MethodNode node, @NotNull String errorSuffix) {
private RuntimeException wrapException(@NotNull Throwable originalException, @NotNull MethodNode node, @NotNull String errorSuffix) {
if (originalException instanceof InlineException) {
return new InlineException(errorPrefix + ": " + errorSuffix, originalException);
}
else {
return new InlineException(errorPrefix + ": " + errorSuffix + "\ncause: " +
getNodeText(node), originalException);
return new InlineException(errorPrefix + ": " + errorSuffix + "\nCause: " + getNodeText(node), originalException);
}
}
@NotNull
//process local and global returns (local substituted with goto end-label global kept unchanged)
public static List<PointForExternalFinallyBlocks> processReturns(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) {
public static List<PointForExternalFinallyBlocks> processReturns(
@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, @Nullable Label endLabel
) {
if (!remapReturn) {
return Collections.emptyList();
}
@@ -785,9 +763,9 @@ public class MethodInliner {
//generate finally block before nonLocalReturn flag/return/goto
LabelNode label = new LabelNode();
instructions.insert(insnNode, label);
result.add(new PointForExternalFinallyBlocks(getInstructionToInsertFinallyBefore(insnNode, isLocalReturn),
getReturnType(insnNode.getOpcode()),
label));
result.add(new PointForExternalFinallyBlocks(
getInstructionToInsertFinallyBefore(insnNode, isLocalReturn), getReturnType(insnNode.getOpcode()), label
));
}
insnNode = insnNode.getNext();
}
@@ -860,7 +838,7 @@ public class MethodInliner {
}
}
public void transform(MethodNode methodNode) {
public void transform(@NotNull MethodNode methodNode) {
int returnVariableIndex = -1;
if (needsReturnVariable) {
returnVariableIndex = methodNode.maxLocals;
@@ -872,6 +850,7 @@ public class MethodInliner {
}
}
@NotNull
public static LocalReturnsNormalizer createFor(
@NotNull MethodNode methodNode,
@NotNull LabelOwner owner,
@@ -905,21 +884,16 @@ public class MethodInliner {
}
}
@NotNull
private static AbstractInsnNode getInstructionToInsertFinallyBefore(@NotNull AbstractInsnNode nonLocalReturnOrJump, boolean isLocal) {
private static AbstractInsnNode getInstructionToInsertFinallyBefore(@NotNull AbstractInsnNode nonLocalReturnOrJump, boolean isLocal) {
return isLocal ? nonLocalReturnOrJump : nonLocalReturnOrJump.getPrevious();
}
//Place to insert finally blocks from try blocks that wraps inline fun call
public static class PointForExternalFinallyBlocks {
final AbstractInsnNode beforeIns;
final Type returnType;
final LabelNode finallyIntervalEnd;
public final AbstractInsnNode beforeIns;
public final Type returnType;
public final LabelNode finallyIntervalEnd;
public PointForExternalFinallyBlocks(
@NotNull AbstractInsnNode beforeIns,
@@ -930,6 +904,5 @@ public class MethodInliner {
this.returnType = returnType;
this.finallyIntervalEnd = finallyIntervalEnd;
}
}
}
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
@@ -25,7 +24,6 @@ import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue
fun MethodInliner.getLambdaIfExistsAndMarkInstructions(
insnNode: AbstractInsnNode?,
processSwap: Boolean,
@@ -42,14 +42,13 @@ abstract class ObjectTransformer<out T : TransformationInfo>(@JvmField val trans
return RemappingClassBuilder(
classBuilder,
AsmTypeRemapper(inliningContext.typeRemapper, inliningContext.root.typeParameterMappings == null, transformationResult))
AsmTypeRemapper(inliningContext.typeRemapper, inliningContext.root.typeParameterMappings == null, transformationResult)
)
}
fun createClassReader(): ClassReader {
return InlineCodegenUtil.buildClassReaderByInternalName(state, transformationInfo.oldClassName)
}
}
class WhenMappingTransformer(
@@ -72,7 +71,7 @@ class WhenMappingTransformer(
}
override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
return if (name.equals(fieldNode.name)) {
return if (name == fieldNode.name) {
classBuilder.newField(JvmDeclarationOrigin.NO_ORIGIN, access, name, desc, signature, value)
}
else {
@@ -80,18 +79,22 @@ class WhenMappingTransformer(
}
}
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
override fun visitMethod(
access: Int, name: String, desc: String, signature: String?, exceptions: Array<out String>?
): MethodVisitor? {
return MethodNode(access, name, desc, signature, exceptions).apply {
methodNodes.add(this)
}
}
}, ClassReader.SKIP_FRAMES)
assert(methodNodes.size == 1, { "When mapping ${fieldNode.owner} class should contain only one method but: " + methodNodes.joinToString { it.name } })
assert(methodNodes.size == 1) {
"When mapping ${fieldNode.owner} class should contain only one method but: " + methodNodes.joinToString { it.name }
}
val clinit = methodNodes.first()
assert(clinit.name == "<clinit>", { "When mapping should contains only <clinit> method, but contains '${clinit.name}'" })
var transformedClinit = cutOtherMappings(clinit)
val transformedClinit = cutOtherMappings(clinit)
val result = classBuilder.newMethod(
JvmDeclarationOrigin.NO_ORIGIN, transformedClinit.access, transformedClinit.name, transformedClinit.desc,
transformedClinit.signature, transformedClinit.exceptions.toTypedArray()
@@ -102,7 +105,6 @@ class WhenMappingTransformer(
return transformationResult
}
private fun cutOtherMappings(node: MethodNode): MethodNode {
val myArrayAccess = InsnSequence(node.instructions).first {
it is FieldInsnNode && it.name.equals(transformationInfo.fieldNode.name)
@@ -123,9 +125,9 @@ class WhenMappingTransformer(
return result
}
private fun isValues(node: AbstractInsnNode) = node is MethodInsnNode &&
node.opcode == Opcodes.INVOKESTATIC &&
node.name == "values" &&
node.desc == "()[" + Type.getObjectType(node.owner).descriptor
private fun isValues(node: AbstractInsnNode) =
node is MethodInsnNode &&
node.opcode == Opcodes.INVOKESTATIC &&
node.name == "values" &&
node.desc == "()[" + Type.getObjectType(node.owner).descriptor
}
@@ -22,28 +22,22 @@ import org.jetbrains.kotlin.codegen.StackValue;
import org.jetbrains.org.objectweb.asm.Type;
class ParameterInfo {
protected final int index;
protected final int declarationIndex;
private final int index;
public final int declarationIndex;
public final Type type;
//for skipped parameter: e.g. inlined lambda
public final boolean isSkipped;
private boolean isCaptured;
public final Type type;
//for skipped parameter: e.g. inlined lambda
public boolean isSkipped;
private LambdaInfo lambda;
//in case when parameter could be extracted from outer context (e.g. from local var)
private StackValue remapValue;
public LambdaInfo lambda;
ParameterInfo(Type type, boolean skipped, int index, int remapValue, int declarationIndex) {
public ParameterInfo(@NotNull Type type, boolean skipped, int index, int remapValue, int declarationIndex) {
this(type, skipped, index, remapValue == -1 ? null : StackValue.local(remapValue, type), declarationIndex);
}
ParameterInfo(@NotNull Type type, boolean skipped, int index, @Nullable StackValue remapValue, int declarationIndex) {
public ParameterInfo(@NotNull Type type, boolean skipped, int index, @Nullable StackValue remapValue, int declarationIndex) {
this.type = type;
this.isSkipped = skipped;
this.remapValue = remapValue;
@@ -52,7 +46,7 @@ class ParameterInfo {
}
public boolean isSkippedOrRemapped() {
return isSkipped || remapValue != null;
return isSkipped || isRemapped();
}
public boolean isRemapped() {
@@ -68,10 +62,6 @@ class ParameterInfo {
return index;
}
public boolean isSkipped() {
return isSkipped;
}
@NotNull
public Type getType() {
return type;
@@ -82,12 +72,14 @@ class ParameterInfo {
return lambda;
}
@NotNull
public ParameterInfo setLambda(@Nullable LambdaInfo lambda) {
this.lambda = lambda;
return this;
}
public ParameterInfo setRemapValue(StackValue remapValue) {
@NotNull
public ParameterInfo setRemapValue(@Nullable StackValue remapValue) {
this.remapValue = remapValue;
return this;
}
@@ -96,13 +88,7 @@ class ParameterInfo {
return isCaptured;
}
public ParameterInfo setSkipped(boolean skipped) {
isSkipped = skipped;
return this;
}
public void setCaptured(boolean isCaptured) {
this.isCaptured = isCaptured;
}
}
@@ -45,7 +45,7 @@ internal class Parameters(val real: List<ParameterInfo>, val captured: List<Capt
}
}
fun getDeclarationSlot(info : ParameterInfo): Int {
fun getDeclarationSlot(info: ParameterInfo): Int {
return paramToDeclByteCodeIndex[info]!!
}
@@ -54,10 +54,7 @@ internal class Parameters(val real: List<ParameterInfo>, val captured: List<Capt
}
private fun get(index: Int): ParameterInfo {
if (index < real.size) {
return real.get(index)
}
return captured.get(index - real.size)
return real.getOrNull(index) ?: captured[index - real.size]
}
override fun iterator(): Iterator<ParameterInfo> {
@@ -71,7 +68,7 @@ internal class Parameters(val real: List<ParameterInfo>, val captured: List<Capt
companion object {
fun shift(capturedParams: List<CapturedParamInfo>, realSize: Int): List<CapturedParamInfo> {
return capturedParams.withIndex().map { it.value.newIndex(it.index+ realSize) }
return capturedParams.withIndex().map { it.value.newIndex(it.index + realSize) }
}
}
}
@@ -18,13 +18,9 @@ package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.org.objectweb.asm.Type
import java.lang.Deprecated
import java.util.ArrayList
import java.util.Collections
internal class ParametersBuilder private constructor(){
import java.util.*
internal class ParametersBuilder private constructor() {
private val valueAndHiddenParams = arrayListOf<ParameterInfo>()
private val capturedParams = arrayListOf<CapturedParamInfo>()
private var valueParamStart = 0
@@ -40,38 +36,31 @@ internal class ParametersBuilder private constructor(){
return info
}
fun addNextParameter(type: Type, skipped: Boolean, remapValue: StackValue?): ParameterInfo {
return addParameter(ParameterInfo(type, skipped, nextValueParameterIndex, remapValue, valueAndHiddenParams.size))
fun addNextParameter(type: Type, skipped: Boolean): ParameterInfo {
return addParameter(ParameterInfo(type, skipped, nextValueParameterIndex, null, valueAndHiddenParams.size))
}
fun addNextValueParameter(type: Type, skipped: Boolean, remapValue: StackValue?, parameterIndex: Int): ParameterInfo {
return addParameter(ParameterInfo(type, skipped, nextValueParameterIndex, remapValue,
if (parameterIndex == -1) valueAndHiddenParams.size else { parameterIndex + valueParamStart }))
return addParameter(ParameterInfo(
type, skipped, nextValueParameterIndex, remapValue,
if (parameterIndex == -1) valueAndHiddenParams.size else parameterIndex + valueParamStart
))
}
fun addCapturedParam(
original: CapturedParamInfo,
newFieldName: String): CapturedParamInfo {
val info = CapturedParamInfo(original.desc, newFieldName, original.isSkipped, nextCapturedIndex(), original.getIndex())
info.setLambda(original.getLambda())
fun addCapturedParam(original: CapturedParamInfo, newFieldName: String): CapturedParamInfo {
val info = CapturedParamInfo(original.desc, newFieldName, original.isSkipped, nextCapturedIndex(), original.index)
info.lambda = original.lambda
return addCapturedParameter(info)
}
private fun nextCapturedIndex(): Int {
return nextCaptured
private fun nextCapturedIndex(): Int = nextCaptured
fun addCapturedParam(desc: CapturedParamDesc, newFieldName: String): CapturedParamInfo {
return addCapturedParameter(CapturedParamInfo(desc, newFieldName, false, nextCapturedIndex(), null))
}
fun addCapturedParam(
desc: CapturedParamDesc,
newFieldName: String): CapturedParamInfo {
val info = CapturedParamInfo(desc, newFieldName, false, nextCapturedIndex(), null)
return addCapturedParameter(info)
}
fun addCapturedParamCopy(
copyFrom: CapturedParamInfo): CapturedParamInfo {
val info = copyFrom.newIndex(nextCapturedIndex())
return addCapturedParameter(info)
fun addCapturedParamCopy(copyFrom: CapturedParamInfo): CapturedParamInfo {
return addCapturedParameter(copyFrom.newIndex(nextCapturedIndex()))
}
fun addCapturedParam(
@@ -80,11 +69,13 @@ internal class ParametersBuilder private constructor(){
newFieldName: String,
type: Type,
skipped: Boolean,
original: ParameterInfo?): CapturedParamInfo {
val info = CapturedParamInfo(CapturedParamDesc.createDesc(containingLambda, fieldName, type), newFieldName, skipped, nextCapturedIndex(),
if (original != null) original.getIndex() else -1)
original: ParameterInfo?
): CapturedParamInfo {
val info = CapturedParamInfo(
CapturedParamDesc(containingLambda, fieldName, type), newFieldName, skipped, nextCapturedIndex(), original?.index ?: -1
)
if (original != null) {
info.setLambda(original.getLambda())
info.lambda = original.lambda
}
return addCapturedParameter(info)
}
@@ -101,7 +92,7 @@ internal class ParametersBuilder private constructor(){
return info
}
fun markValueParametesStart(){
fun markValueParametersStart() {
this.valueParamStart = valueAndHiddenParams.size
}
@@ -119,23 +110,24 @@ internal class ParametersBuilder private constructor(){
}
companion object {
@JvmStatic
fun newBuilder(): ParametersBuilder {
return ParametersBuilder()
}
@JvmOverloads @JvmStatic
fun initializeBuilderFrom(objectType: Type, descriptor: String, inlineLambda: LambdaInfo? = null, addThis: Boolean = true): ParametersBuilder {
@JvmOverloads
@JvmStatic
fun initializeBuilderFrom(
objectType: Type, descriptor: String, inlineLambda: LambdaInfo? = null, addThis: Boolean = true
): ParametersBuilder {
val builder = newBuilder()
if (addThis) {
//skipped this for inlined lambda cause it will be removed
builder.addThis(objectType, inlineLambda != null).setLambda(inlineLambda)
builder.addThis(objectType, inlineLambda != null).lambda = inlineLambda
}
val types = Type.getArgumentTypes(descriptor)
for (type in types) {
builder.addNextParameter(type, false, null)
for (type in Type.getArgumentTypes(descriptor)) {
builder.addNextParameter(type, false)
}
return builder
}
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState;
import java.util.Map;
public class RegeneratedClassContext extends InliningContext {
private InlineCallSiteInfo callSiteInfo;
private final InlineCallSiteInfo callSiteInfo;
public RegeneratedClassContext(
@Nullable InliningContext parent,
@@ -39,6 +39,8 @@ public class RegeneratedClassContext extends InliningContext {
this.callSiteInfo = callSiteInfo;
}
@NotNull
@Override
public InlineCallSiteInfo getCallSiteInfo() {
return callSiteInfo;
}
@@ -34,11 +34,11 @@ public class RegeneratedLambdaFieldRemapper extends FieldRemapper {
private final boolean isConstructor;
public RegeneratedLambdaFieldRemapper(
String oldOwnerType,
String newOwnerType,
Parameters parameters,
Map<String, LambdaInfo> recapturedLambdas,
FieldRemapper remapper,
@NotNull String oldOwnerType,
@NotNull String newOwnerType,
@NotNull Parameters parameters,
@NotNull Map<String, LambdaInfo> recapturedLambdas,
@NotNull FieldRemapper remapper,
boolean isConstructor
) {
super(oldOwnerType, remapper, parameters);
@@ -50,12 +50,12 @@ public class RegeneratedLambdaFieldRemapper extends FieldRemapper {
}
@Override
public boolean canProcess(@NotNull String fieldOwner, String fieldName, boolean isFolding) {
public boolean canProcess(@NotNull String fieldOwner, @NotNull String fieldName, boolean isFolding) {
return super.canProcess(fieldOwner, fieldName, isFolding) || isRecapturedLambdaType(fieldOwner, isFolding);
}
private boolean isRecapturedLambdaType(String owner, boolean isFolding) {
return recapturedLambdas.containsKey(owner) && (isFolding || false == parent instanceof InlinedLambdaRemapper);
private boolean isRecapturedLambdaType(@NotNull String owner, boolean isFolding) {
return recapturedLambdas.containsKey(owner) && (isFolding || !(parent instanceof InlinedLambdaRemapper));
}
@Nullable
@@ -64,9 +64,8 @@ public class RegeneratedLambdaFieldRemapper extends FieldRemapper {
boolean searchInParent = !canProcess(fieldInsnNode.owner, fieldInsnNode.name, false);
if (searchInParent) {
return parent.findField(fieldInsnNode);
} else {
return findFieldInMyCaptured(fieldInsnNode);
}
return findFieldInMyCaptured(fieldInsnNode);
}
@Override
@@ -75,10 +74,11 @@ public class RegeneratedLambdaFieldRemapper extends FieldRemapper {
}
@Nullable
public CapturedParamInfo findFieldInMyCaptured(@NotNull FieldInsnNode fieldInsnNode) {
private CapturedParamInfo findFieldInMyCaptured(@NotNull FieldInsnNode fieldInsnNode) {
return super.findField(fieldInsnNode, parameters.getCaptured());
}
@NotNull
@Override
public String getNewLambdaInternalName() {
return newOwnerType;
@@ -98,18 +98,23 @@ public class RegeneratedLambdaFieldRemapper extends FieldRemapper {
boolean searchInParent = false;
if (field == null) {
field = findFieldInMyCaptured(new FieldInsnNode(Opcodes.GETSTATIC, oldOwnerType, "this$0", Type.getObjectType(parent.getLambdaInternalName()).getDescriptor()));
field = findFieldInMyCaptured(new FieldInsnNode(
Opcodes.GETSTATIC, oldOwnerType, InlineCodegenUtil.THIS$0,
Type.getObjectType(parent.getLambdaInternalName()).getDescriptor()
));
searchInParent = true;
if (field == null) {
throw new IllegalStateException("Couldn't find captured this " + getLambdaInternalName() + " for " + node.name);
}
}
StackValue result = StackValue.field(field.isSkipped ?
Type.getObjectType(parent.parent.getNewLambdaInternalName()) : field.getType(),
Type.getObjectType(getNewLambdaInternalName()), /*TODO owner type*/
field.getNewFieldName(), false,
prefix == null ? StackValue.LOCAL_0 : prefix);
StackValue result = StackValue.field(
field.isSkipped ?
Type.getObjectType(parent.parent.getNewLambdaInternalName()) : field.getType(),
Type.getObjectType(getNewLambdaInternalName()), /*TODO owner type*/
field.getNewFieldName(), false,
prefix == null ? StackValue.LOCAL_0 : prefix
);
return searchInParent ? parent.getFieldForInline(node, result) : result;
}
@@ -39,7 +39,8 @@ class ReificationArgument(
ReificationArgument(
replacement.parameterName,
this.nullable || (replacement.nullable && this.arrayDepth == 0),
this.arrayDepth + replacement.arrayDepth)
this.arrayDepth + replacement.arrayDepth
)
fun reify(replacementAsmType: Type, kotlinType: KotlinType) =
Pair(Type.getType("[".repeat(arrayDepth) + replacementAsmType), kotlinType.arrayOf(arrayDepth).makeNullableIfNeeded(nullable))
@@ -57,7 +58,6 @@ class ReificationArgument(
}
class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?) {
enum class OperationKind {
NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS;
@@ -65,8 +65,8 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
}
companion object {
@JvmField val REIFIED_OPERATION_MARKER_METHOD_NAME = "reifiedOperationMarker"
@JvmField val NEED_CLASS_REIFICATION_MARKER_METHOD_NAME = "needClassReification"
const val REIFIED_OPERATION_MARKER_METHOD_NAME = "reifiedOperationMarker"
const val NEED_CLASS_REIFICATION_MARKER_METHOD_NAME = "needClassReification"
private fun isOperationReifiedMarker(insn: AbstractInsnNode) =
isReifiedMarker(insn) { it == REIFIED_OPERATION_MARKER_METHOD_NAME }
@@ -76,10 +76,12 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
return insn.owner == IntrinsicMethods.INTRINSICS_CLASS_NAME && namePredicate(insn.name)
}
@JvmStatic fun isNeedClassReificationMarker(insn: AbstractInsnNode): Boolean =
@JvmStatic
fun isNeedClassReificationMarker(insn: AbstractInsnNode): Boolean =
isReifiedMarker(insn) { s -> s == NEED_CLASS_REIFICATION_MARKER_METHOD_NAME }
@JvmStatic fun putNeedClassReificationMarker(v: MethodVisitor) {
@JvmStatic
fun putNeedClassReificationMarker(v: MethodVisitor) {
v.visitMethodInsn(
Opcodes.INVOKESTATIC,
IntrinsicMethods.INTRINSICS_CLASS_NAME, NEED_CLASS_REIFICATION_MARKER_METHOD_NAME,
@@ -102,7 +104,7 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
val instructions = node.instructions
maxStackSize = 0
var result = ReifiedTypeParametersUsages()
val result = ReifiedTypeParametersUsages()
for (insn in instructions.toArray()) {
if (isOperationReifiedMarker(insn)) {
val newName: String? = processReifyMarker(insn as MethodInsnNode, instructions)
@@ -144,7 +146,8 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
}
return null
} else {
}
else {
val newReificationArgument = reificationArgument.combine(mapping.reificationArgument!!)
instructions.set(insn.previous!!, LdcInsnNode(newReificationArgument.asString()))
return mapping.reificationArgument.parameterName
@@ -154,43 +157,45 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
private fun processNewArray(insn: MethodInsnNode, parameter: Type) =
processNextTypeInsn(insn, parameter, Opcodes.ANEWARRAY)
private fun processAs(insn: MethodInsnNode,
instructions: InsnList,
kotlinType: KotlinType,
asmType: Type,
safe: Boolean) =
rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { stubCheckcast: AbstractInsnNode ->
if (stubCheckcast !is TypeInsnNode) return false
private fun processAs(
insn: MethodInsnNode,
instructions: InsnList,
kotlinType: KotlinType,
asmType: Type,
safe: Boolean
) = rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { stubCheckcast: AbstractInsnNode ->
if (stubCheckcast !is TypeInsnNode) return false
val newMethodNode = MethodNode(InlineCodegenUtil.API)
generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe)
val newMethodNode = MethodNode(InlineCodegenUtil.API)
generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe)
instructions.insert(insn, newMethodNode.instructions)
instructions.remove(stubCheckcast)
instructions.insert(insn, newMethodNode.instructions)
instructions.remove(stubCheckcast)
// TODO: refine max stack calculation (it's not always as big as +4)
maxStackSize = Math.max(maxStackSize, 4)
// TODO: refine max stack calculation (it's not always as big as +4)
maxStackSize = Math.max(maxStackSize, 4)
return true
}
return true
}
private fun processIs(insn: MethodInsnNode,
instructions: InsnList,
kotlinType: KotlinType,
asmType: Type) =
rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { stubInstanceOf: AbstractInsnNode ->
if (stubInstanceOf !is TypeInsnNode) return false
private fun processIs(
insn: MethodInsnNode,
instructions: InsnList,
kotlinType: KotlinType,
asmType: Type
) = rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { stubInstanceOf: AbstractInsnNode ->
if (stubInstanceOf !is TypeInsnNode) return false
val newMethodNode = MethodNode(InlineCodegenUtil.API)
generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType)
val newMethodNode = MethodNode(InlineCodegenUtil.API)
generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType)
instructions.insert(insn, newMethodNode.instructions)
instructions.remove(stubInstanceOf)
instructions.insert(insn, newMethodNode.instructions)
instructions.remove(stubInstanceOf)
// TODO: refine max stack calculation (it's not always as big as +2)
maxStackSize = Math.max(maxStackSize, 2)
return true
}
// TODO: refine max stack calculation (it's not always as big as +2)
maxStackSize = Math.max(maxStackSize, 2)
return true
}
inline private fun rewriteNextTypeInsn(
marker: MethodInsnNode,
@@ -214,7 +219,6 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
next.cst = parameter
return true
}
}
private val MethodInsnNode.reificationArgument: ReificationArgument?
@@ -242,16 +246,18 @@ class TypeParameterMappings() {
private val mappingsByName = hashMapOf<String, TypeParameterMapping>()
fun addParameterMappingToType(name: String, type: KotlinType, asmType: Type, signature: String, isReified: Boolean) {
mappingsByName[name] = TypeParameterMapping(name, type, asmType, reificationArgument = null, signature = signature, isReified = isReified)
mappingsByName[name] = TypeParameterMapping(
name, type, asmType, reificationArgument = null, signature = signature, isReified = isReified
)
}
fun addParameterMappingForFurtherReification(name: String, type: KotlinType, reificationArgument: ReificationArgument, isReified: Boolean) {
mappingsByName[name] = TypeParameterMapping(name, type, asmType = null, reificationArgument = reificationArgument, signature = null, isReified = isReified)
mappingsByName[name] = TypeParameterMapping(
name, type, asmType = null, reificationArgument = reificationArgument, signature = null, isReified = isReified
)
}
operator fun get(name: String): TypeParameterMapping? {
return mappingsByName[name]
}
operator fun get(name: String): TypeParameterMapping? = mappingsByName[name]
fun hasReifiedParameters() = mappingsByName.values.any { it.isReified }
@@ -261,7 +267,8 @@ class TypeParameterMappings() {
}
class TypeParameterMapping(
val name: String, val type: KotlinType,
val name: String,
val type: KotlinType,
val asmType: Type?,
val reificationArgument: ReificationArgument?,
val signature: String?,
@@ -28,14 +28,14 @@ public class RemapVisitor extends MethodBodyVisitor {
private final FieldRemapper nodeRemapper;
private final InstructionAdapter instructionAdapter;
protected RemapVisitor(
MethodVisitor mv,
LocalVarRemapper localVarRemapper,
FieldRemapper nodeRemapper
public RemapVisitor(
@NotNull MethodVisitor mv,
@NotNull LocalVarRemapper remapper,
@NotNull FieldRemapper nodeRemapper
) {
super(mv);
this.instructionAdapter = new InstructionAdapter(mv);
this.remapper = localVarRemapper;
this.remapper = remapper;
this.nodeRemapper = nodeRemapper;
}
@@ -58,19 +58,13 @@ public class RemapVisitor extends MethodBodyVisitor {
@Override
public void visitFieldInsn(int opcode, @NotNull String owner, @NotNull String name, @NotNull String desc) {
if (name.startsWith("$$$")) {
if (nodeRemapper instanceof RegeneratedLambdaFieldRemapper || nodeRemapper.isRoot()) {
FieldInsnNode fin = new FieldInsnNode(opcode, owner, name, desc);
StackValue inline = nodeRemapper.getFieldForInline(fin, null);
assert inline != null : "Captured field should have not null stackValue " + fin;
inline.put(inline.type, this);
}
else {
super.visitFieldInsn(opcode, owner, name, desc);
}
}
else {
super.visitFieldInsn(opcode, owner, name, desc);
if (name.startsWith("$$$") && (nodeRemapper instanceof RegeneratedLambdaFieldRemapper || nodeRemapper.isRoot())) {
FieldInsnNode fin = new FieldInsnNode(opcode, owner, name, desc);
StackValue inline = nodeRemapper.getFieldForInline(fin, null);
assert inline != null : "Captured field should have not null stackValue " + fin;
inline.put(inline.type, this);
return;
}
super.visitFieldInsn(opcode, owner, name, desc);
}
}
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.psi.KtElement;
import java.util.Map;
public class RootInliningContext extends InliningContext {
public final CodegenContext startContext;
private final InlineCallSiteInfo inlineCallSiteInfo;
public final TypeParameterMappings typeParameterMappings;
public final KtElement callElement;
@@ -34,7 +33,6 @@ public class RootInliningContext extends InliningContext {
@NotNull Map<Integer, LambdaInfo> map,
@NotNull GenerationState state,
@NotNull NameGenerator nameGenerator,
@NotNull CodegenContext startContext,
@NotNull KtElement callElement,
@NotNull InlineCallSiteInfo classNameToInline,
@NotNull ReifiedTypeInliner inliner,
@@ -42,11 +40,11 @@ public class RootInliningContext extends InliningContext {
) {
super(null, map, state, nameGenerator, TypeRemapper.createRoot(typeParameterMappings), inliner, false, false);
this.callElement = callElement;
this.startContext = startContext;
this.inlineCallSiteInfo = classNameToInline;
this.typeParameterMappings = typeParameterMappings;
}
@NotNull
@Override
public InlineCallSiteInfo getCallSiteInfo() {
return inlineCallSiteInfo;
@@ -55,7 +55,9 @@ class SMAPBuilder(
val combinedMapping = FileMapping(source, path)
realMappings.forEach { fileMapping ->
fileMapping.lineMappings.filter { it.callSiteMarker != null }.forEach { rangeMapping ->
combinedMapping.addRangeMapping(RangeMapping(rangeMapping.callSiteMarker!!.lineNumber, rangeMapping.dest, rangeMapping.range))
combinedMapping.addRangeMapping(RangeMapping(
rangeMapping.callSiteMarker!!.lineNumber, rangeMapping.dest, rangeMapping.range
))
}
}
@@ -95,9 +97,11 @@ open class NestedSourceMapper(
if (mappedLineNumber > 0) {
return mappedLineNumber
} else {
val rangeForMapping = (if (lastVisitedRange?.contains(lineNumber) ?: false) lastVisitedRange!! else findMappingIfExists(lineNumber))
?: error("Can't find range to map line $lineNumber in ${sourceInfo.source}:${sourceInfo.pathOrCleanFQN}")
}
else {
val rangeForMapping =
(if (lastVisitedRange?.contains(lineNumber) ?: false) lastVisitedRange!! else findMappingIfExists(lineNumber))
?: error("Can't find range to map line $lineNumber in ${sourceInfo.source}: ${sourceInfo.pathOrCleanFQN}")
val sourceLineNumber = rangeForMapping.mapDestToSource(lineNumber)
val newLineNumber = parent.mapLineNumber(sourceLineNumber, rangeForMapping.parent!!.name, rangeForMapping.parent!!.path)
if (newLineNumber > 0) {
@@ -132,9 +136,7 @@ open class InlineLambdaSourceMapper(
//don't remap origin lambda line numbers
return lineNumber
}
else {
return super.mapLineNumber(lineNumber)
}
return super.mapLineNumber(lineNumber)
}
}
@@ -152,7 +154,6 @@ interface SourceMapper {
}
fun endMapping() {
}
companion object {
@@ -179,11 +180,8 @@ object IdenticalSourceMapper : SourceMapper {
class CallSiteMarker(val lineNumber: Int)
open class DefaultSourceMapper(val sourceInfo: SourceInfo) : SourceMapper {
private var maxUsedValue: Int = sourceInfo.linesInFile
private var lastMappedWithChanges: RawFileMapping? = null
private var fileMappings: LinkedHashMap<String, RawFileMapping> = linkedMapOf()
protected val origin: RawFileMapping
@@ -197,7 +195,6 @@ open class DefaultSourceMapper(val sourceInfo: SourceInfo) : SourceMapper {
override val resultMappings: List<FileMapping>
get() = fileMappings.values.map { it.toFileMapping() }
init {
val name = sourceInfo.source
val path = sourceInfo.pathOrCleanFQN
@@ -206,8 +203,8 @@ open class DefaultSourceMapper(val sourceInfo: SourceInfo) : SourceMapper {
fileMappings.put(createKey(name, path), origin)
}
constructor(sourceInfo: SourceInfo, fileMappings: List<FileMapping>): this(sourceInfo) {
fileMappings.asSequence().drop(1)
constructor(sourceInfo: SourceInfo, fileMappings: List<FileMapping>) : this(sourceInfo) {
fileMappings.asSequence().drop(1)
//default one mapped through sourceInfo
.forEach { fileMapping ->
val newFileMapping = getOrRegisterNewSource(fileMapping.name, fileMapping.path)
@@ -227,7 +224,7 @@ open class DefaultSourceMapper(val sourceInfo: SourceInfo) : SourceMapper {
override fun mapLineNumber(lineNumber: Int): Int {
if (lineNumber < 0) {
//no source information, so just skip this linenumber
return - 1
return -1
}
//TODO maybe add assertion that linenumber contained in fileMappings
return lineNumber
@@ -238,8 +235,7 @@ open class DefaultSourceMapper(val sourceInfo: SourceInfo) : SourceMapper {
//no source information, so just skip this linenumber
return -1
}
val mappedLineIndex = createMapping(getOrRegisterNewSource(sourceName, sourcePath), source)
return mappedLineIndex
return createMapping(getOrRegisterNewSource(sourceName, sourcePath), source)
}
private fun createMapping(fileMapping: RawFileMapping, lineNumber: Int): Int {
@@ -263,6 +259,7 @@ class SMAP(val fileMappings: List<FileMapping>) {
val intervals = fileMappings.flatMap { it.lineMappings }.sortedWith(RangeMapping.Comparator)
val sourceInfo: SourceInfo
init {
val defaultMapping = default.lineMappings.first()
sourceInfo = SourceInfo(default.name, default.path, defaultMapping.source + defaultMapping.range - 1)
@@ -349,7 +346,7 @@ data class RangeMapping(val source: Int, val dest: Int, var range: Int = 1, var
get() = dest + range - 1
operator fun contains(destLine: Int): Boolean {
return if (skip) true else dest <= destLine && destLine < dest + range
return skip || (dest <= destLine && destLine < dest + range)
}
fun mapDestToSource(destLine: Int): Int {
@@ -365,12 +362,7 @@ data class RangeMapping(val source: Int, val dest: Int, var range: Int = 1, var
if (o1 == o2) return 0
val res = o1.dest - o2.dest
if (res == 0) {
return o1.range - o2.range
}
else {
return res
}
return if (res == 0) o1.range - o2.range else res
}
}
@@ -64,7 +64,7 @@ object SMAPParser {
/*only simple mapping now*/
val targetSplit = lineMapping.indexOf(':')
val originalPart = lineMapping.substring(0, targetSplit)
var rangeSeparator = originalPart.indexOf(',').let { if (it < 0) targetSplit else it }
val rangeSeparator = originalPart.indexOf(',').let { if (it < 0) targetSplit else it }
val fileSeparator = lineMapping.indexOf('#')
val originalIndex = originalPart.substring(0, fileSeparator).toInt()
@@ -17,10 +17,8 @@
package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
import java.util.*
interface TransformationInfo {
val oldClassName: String
val newClassName: String
@@ -29,10 +27,7 @@ interface TransformationInfo {
fun canRemoveAfterTransformation(): Boolean
fun createTransformer(
inliningContext: InliningContext,
sameModule: Boolean
): ObjectTransformer<*>
fun createTransformer(inliningContext: InliningContext, sameModule: Boolean): ObjectTransformer<*>
}
class WhenMappingTransformationInfo(
@@ -41,22 +36,17 @@ class WhenMappingTransformationInfo(
val alreadyRegenerated: Boolean,
val fieldNode: FieldInsnNode
) : TransformationInfo {
override val newClassName by lazy {
nameGenerator.genWhenClassNamePrefix() + TRANSFORMED_WHEN_MAPPING_MARKER + oldClassName.substringAfterLast("/").substringAfterLast(TRANSFORMED_WHEN_MAPPING_MARKER)
nameGenerator.genWhenClassNamePrefix() + TRANSFORMED_WHEN_MAPPING_MARKER +
oldClassName.substringAfterLast("/").substringAfterLast(TRANSFORMED_WHEN_MAPPING_MARKER)
}
override fun shouldRegenerate(sameModule: Boolean): Boolean {
return !alreadyRegenerated && !sameModule
}
override fun shouldRegenerate(sameModule: Boolean): Boolean = !alreadyRegenerated && !sameModule
override fun canRemoveAfterTransformation(): Boolean {
return true
}
override fun canRemoveAfterTransformation(): Boolean = true
override fun createTransformer(inliningContext: InliningContext, sameModule: Boolean): ObjectTransformer<*> {
return WhenMappingTransformer(this, inliningContext)
}
override fun createTransformer(inliningContext: InliningContext, sameModule: Boolean): ObjectTransformer<*> =
WhenMappingTransformer(this, inliningContext)
companion object {
const val TRANSFORMED_WHEN_MAPPING_MARKER = "\$wm$"
@@ -71,8 +61,8 @@ class AnonymousObjectTransformationInfo internal constructor(
private val alreadyRegenerated: Boolean,
val constructorDesc: String?,
private val isStaticOrigin: Boolean,
nameGenerator: NameGenerator) : TransformationInfo {
nameGenerator: NameGenerator
) : TransformationInfo {
override val newClassName: String by lazy {
nameGenerator.genLambdaClassName()
}
@@ -89,14 +79,10 @@ class AnonymousObjectTransformationInfo internal constructor(
alreadyRegenerated: Boolean,
isStaticOrigin: Boolean,
nameGenerator: NameGenerator
) : this(
ownerInternalName, needReification,
HashMap<Int, LambdaInfo>(), false, alreadyRegenerated, null, isStaticOrigin, nameGenerator) {
}
) : this(ownerInternalName, needReification, hashMapOf(), false, alreadyRegenerated, null, isStaticOrigin, nameGenerator)
override fun shouldRegenerate(sameModule: Boolean): Boolean {
return !alreadyRegenerated && (!lambdasToInline.isEmpty() || !sameModule || capturedOuterRegenerated || needReification)
}
override fun shouldRegenerate(sameModule: Boolean): Boolean =
!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
@@ -104,7 +90,6 @@ class AnonymousObjectTransformationInfo internal constructor(
return !isStaticOrigin
}
override fun createTransformer(inliningContext: InliningContext, sameModule: Boolean): ObjectTransformer<*> {
return AnonymousObjectTransformer(this, inliningContext, sameModule)
}
override fun createTransformer(inliningContext: InliningContext, sameModule: Boolean): ObjectTransformer<*> =
AnonymousObjectTransformer(this, inliningContext, sameModule)
}
@@ -16,9 +16,9 @@
package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.firstLabelInChain
import org.jetbrains.org.objectweb.asm.tree.LabelNode
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*
import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode
enum class TryCatchPosition {
START,
@@ -26,9 +26,9 @@ enum class TryCatchPosition {
INNER
}
class SplitPair<out T: Interval>(val patchedPart: T, val newPart: T)
class SplitPair<out T : Interval>(val patchedPart: T, val newPart: T)
class SimpleInterval(override val startLabel: LabelNode, override val endLabel: LabelNode ) : Interval
class SimpleInterval(override val startLabel: LabelNode, override val endLabel: LabelNode) : Interval
interface Interval {
val startLabel: LabelNode
@@ -36,20 +36,21 @@ interface Interval {
/*note that some intervals are mutable */
fun isEmpty(): Boolean = startLabel == endLabel
}
interface SplittableInterval<T: Interval> : Interval {
interface SplittableInterval<out T : Interval> : Interval {
fun split(splitBy: Interval, keepStart: Boolean): SplitPair<T>
}
interface IntervalWithHandler : Interval {
val handler: LabelNode
val type: String?
}
class TryCatchBlockNodeInfo(val node: TryCatchBlockNode, val onlyCopyNotProcess: Boolean) : IntervalWithHandler, SplittableInterval<TryCatchBlockNodeInfo> {
class TryCatchBlockNodeInfo(
val node: TryCatchBlockNode,
val onlyCopyNotProcess: Boolean
) : IntervalWithHandler, SplittableInterval<TryCatchBlockNodeInfo> {
override val startLabel: LabelNode
get() = node.start
override val endLabel: LabelNode
@@ -77,16 +78,16 @@ class TryCatchBlockNodeInfo(val node: TryCatchBlockNode, val onlyCopyNotProcess:
}
}
class TryCatchBlockNodePosition(val nodeInfo: TryCatchBlockNodeInfo, var position: TryCatchPosition) : IntervalWithHandler by nodeInfo {
}
class TryCatchBlockNodePosition(
val nodeInfo: TryCatchBlockNodeInfo,
var position: TryCatchPosition
) : IntervalWithHandler by nodeInfo
class TryBlockCluster<T : IntervalWithHandler>(val blocks: MutableList<T>) {
val defaultHandler: T?
get() = blocks.firstOrNull() { it.type == null }
}
fun <T : IntervalWithHandler> doClustering(blocks: List<T>): List<TryBlockCluster<T>> {
data class TryBlockInterval(val startLabel: LabelNode, val endLabel: LabelNode)
@@ -16,16 +16,17 @@
package org.jetbrains.kotlin.codegen.inline
import java.util.HashMap
import java.util.*
class TypeParameter(val oldName: String, val newName: String?, val isReified: Boolean, val signature: String?)
class TypeParameter(val oldName: String, val newName: String?, val isReified: Boolean, val signature: String?)
//typeMapping data could be changed outside through method processing
class TypeRemapper private constructor(private val typeMapping: MutableMap<String, String>, val parent: TypeRemapper?, val isInlineLambda: Boolean = false) {
class TypeRemapper private constructor(
private val typeMapping: MutableMap<String, String>,
val parent: TypeRemapper?,
val isInlineLambda: Boolean = false
) {
private var additionalMappings: MutableMap<String, String> = hashMapOf()
private val typeParametersMapping: MutableMap<String, TypeParameter> = hashMapOf()
fun addMapping(type: String, newType: String) {
@@ -37,11 +38,11 @@ class TypeRemapper private constructor(private val typeMapping: MutableMap<Strin
}
fun map(type: String): String {
return typeMapping[type] ?: additionalMappings?.get(type) ?: type
return typeMapping[type] ?: additionalMappings[type] ?: type
}
fun addAdditionalMappings(oldName: String, newName: String) {
additionalMappings!!.put(oldName, newName)
additionalMappings[oldName] = newName
}
fun registerTypeParameter(name: String) {
@@ -52,7 +53,9 @@ class TypeRemapper private constructor(private val typeMapping: MutableMap<Strin
}
fun registerTypeParameter(mapping: TypeParameterMapping) {
typeParametersMapping[mapping.name] = TypeParameter(mapping.name, mapping.reificationArgument?.parameterName, mapping.isReified, mapping.signature)
typeParametersMapping[mapping.name] = TypeParameter(
mapping.name, mapping.reificationArgument?.parameterName, mapping.isReified, mapping.signature
)
}
fun mapTypeParameter(name: String): TypeParameter? {
@@ -60,14 +63,13 @@ class TypeRemapper private constructor(private val typeMapping: MutableMap<Strin
}
companion object {
@JvmStatic
fun createRoot(formalTypeParameters: TypeParameterMappings?): TypeRemapper {
val typeRemapper = TypeRemapper(HashMap<String, String>(), null)
formalTypeParameters?.forEach {
typeRemapper.registerTypeParameter(it)
return TypeRemapper(HashMap<String, String>(), null).apply {
formalTypeParameters?.forEach {
registerTypeParameter(it)
}
}
return typeRemapper
}
@JvmStatic
@@ -82,9 +84,9 @@ class TypeRemapper private constructor(private val typeMapping: MutableMap<Strin
}
private fun createNewAndMerge(remapper: TypeRemapper, additionalTypeMappings: Map<String, String>): MutableMap<String, String> {
val map = HashMap(remapper.typeMapping)
map += additionalTypeMappings
return map
return HashMap(remapper.typeMapping).apply {
this += additionalTypeMappings
}
}
}
}