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