Convert InlineCodegenUtil to Kotlin

This commit is contained in:
Mikhael Bogdanov
2017-06-12 10:10:12 +02:00
parent b7b3b3a19c
commit 51d494a271
39 changed files with 664 additions and 699 deletions
@@ -20,7 +20,6 @@ import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.inline.FileMapping; import org.jetbrains.kotlin.codegen.inline.FileMapping;
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
import org.jetbrains.kotlin.codegen.inline.SMAPBuilder; import org.jetbrains.kotlin.codegen.inline.SMAPBuilder;
import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings; import org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings;
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin; import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
@@ -29,6 +28,8 @@ import org.jetbrains.org.objectweb.asm.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.GENERATE_SMAP;
public abstract class AbstractClassBuilder implements ClassBuilder { public abstract class AbstractClassBuilder implements ClassBuilder {
protected static final MethodVisitor EMPTY_METHOD_VISITOR = new MethodVisitor(Opcodes.ASM5) {}; protected static final MethodVisitor EMPTY_METHOD_VISITOR = new MethodVisitor(Opcodes.ASM5) {};
protected static final FieldVisitor EMPTY_FIELD_VISITOR = new FieldVisitor(Opcodes.ASM5) {}; protected static final FieldVisitor EMPTY_FIELD_VISITOR = new FieldVisitor(Opcodes.ASM5) {};
@@ -105,7 +106,7 @@ public abstract class AbstractClassBuilder implements ClassBuilder {
@Override @Override
public void done() { public void done() {
if (!fileMappings.isEmpty() && InlineCodegenUtil.GENERATE_SMAP) { if (!fileMappings.isEmpty() && GENERATE_SMAP) {
FileMapping origin = fileMappings.get(0); FileMapping origin = fileMappings.get(0);
assert sourceName == null || origin.getName().equals(sourceName) : "Error " + origin.getName() + " != " + sourceName; assert sourceName == null || origin.getName().equals(sourceName) : "Error " + origin.getName() + " != " + sourceName;
getVisitor().visitSource(origin.getName(), new SMAPBuilder(origin.getName(), origin.getPath(), fileMappings).build()); getVisitor().visitSource(origin.getName(), new SMAPBuilder(origin.getName(), origin.getPath(), fileMappings).build());
@@ -27,7 +27,6 @@ import org.jetbrains.kotlin.codegen.binding.CalculatedClosure;
import org.jetbrains.kotlin.codegen.context.ClosureContext; import org.jetbrains.kotlin.codegen.context.ClosureContext;
import org.jetbrains.kotlin.codegen.context.EnclosedValueDescriptor; import org.jetbrains.kotlin.codegen.context.EnclosedValueDescriptor;
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt; import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension; import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension;
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter; import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter; import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter;
@@ -60,6 +59,7 @@ import java.util.List;
import static org.jetbrains.kotlin.codegen.AsmUtil.*; import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConst; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isConst;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLOSURE; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.CLOSURE;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.initDefaultSourceMappingIfNeeded;
import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION; import static org.jetbrains.kotlin.codegen.serialization.JvmSerializationBindings.METHOD_FOR_FUNCTION;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*;
import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN; import static org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.NO_ORIGIN;
@@ -158,7 +158,7 @@ public class ClosureCodegen extends MemberCodegen<KtElement> {
superInterfaceAsmTypes superInterfaceAsmTypes
); );
InlineCodegenUtil.initDefaultSourceMappingIfNeeded(context, this, state); initDefaultSourceMappingIfNeeded(context, this, state);
v.visitSource(element.getContainingFile().getName(), null); v.visitSource(element.getContainingFile().getName(), null);
} }
@@ -107,7 +107,7 @@ import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isInt;
import static org.jetbrains.kotlin.codegen.AsmUtil.*; import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.addInlineMarker; import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.*;
import static org.jetbrains.kotlin.resolve.BindingContext.*; import static org.jetbrains.kotlin.resolve.BindingContext.*;
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall; import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall;
import static org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure; import static org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure;
@@ -1414,14 +1414,14 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
Label finallyStart = new Label(); Label finallyStart = new Label();
v.mark(finallyStart); v.mark(finallyStart);
finallyBlockStackElement.addGapLabel(finallyStart); finallyBlockStackElement.addGapLabel(finallyStart);
if (InlineCodegenUtil.isFinallyMarkerRequired(context)) { if (isFinallyMarkerRequired(context)) {
InlineCodegenUtil.generateFinallyMarker(v, finallyDepth, true); generateFinallyMarker(v, finallyDepth, true);
} }
//noinspection ConstantConditions //noinspection ConstantConditions
gen(jetTryExpression.getFinallyBlock().getFinalExpression(), Type.VOID_TYPE); gen(jetTryExpression.getFinallyBlock().getFinalExpression(), Type.VOID_TYPE);
if (InlineCodegenUtil.isFinallyMarkerRequired(context)) { if (isFinallyMarkerRequired(context)) {
InlineCodegenUtil.generateFinallyMarker(v, finallyDepth, false); generateFinallyMarker(v, finallyDepth, false);
} }
} }
@@ -1464,7 +1464,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
generateFinallyBlocksIfNeeded(returnType, afterReturnLabel); generateFinallyBlocksIfNeeded(returnType, afterReturnLabel);
if (isNonLocalReturn) { if (isNonLocalReturn) {
InlineCodegenUtil.generateGlobalReturnFlag(v, nonLocalReturn.labelName); generateGlobalReturnFlag(v, nonLocalReturn.labelName);
v.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); v.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
} }
else { else {
@@ -1502,7 +1502,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
FunctionDescriptor containingFunction = FunctionDescriptor containingFunction =
BindingContextUtils.getContainingFunctionSkipFunctionLiterals(descriptor, true).getFirst(); BindingContextUtils.getContainingFunctionSkipFunctionLiterals(descriptor, true).getFirst();
//FIRST_FUN_LABEL to prevent clashing with existing labels //FIRST_FUN_LABEL to prevent clashing with existing labels
return new NonLocalReturnInfo(typeMapper.mapReturnType(containingFunction), InlineCodegenUtil.FIRST_FUN_LABEL); return new NonLocalReturnInfo(typeMapper.mapReturnType(containingFunction), FIRST_FUN_LABEL);
} else { } else {
//local //local
return null; return null;
@@ -4217,8 +4217,8 @@ The "returned" value of try expression with no finally is either the last expres
public NameGenerator getInlineNameGenerator() { public NameGenerator getInlineNameGenerator() {
NameGenerator nameGenerator = getParentCodegen().getInlineNameGenerator(); NameGenerator nameGenerator = getParentCodegen().getInlineNameGenerator();
Name name = context.getContextDescriptor().getName(); Name name = context.getContextDescriptor().getName();
String inlinedName = name.isSpecial() ? InlineCodegenUtil.SPECIAL_TRANSFORMATION_NAME : name.asString(); String inlinedName = name.isSpecial() ? SPECIAL_TRANSFORMATION_NAME : name.asString();
return nameGenerator.subGenerator(inlinedName + InlineCodegenUtil.INLINE_CALL_TRANSFORMATION_SUFFIX); return nameGenerator.subGenerator(inlinedName + INLINE_CALL_TRANSFORMATION_SUFFIX);
} }
public Type getReturnType() { public Type getReturnType() {
@@ -31,7 +31,6 @@ import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
import org.jetbrains.kotlin.codegen.binding.MutableClosure; import org.jetbrains.kotlin.codegen.binding.MutableClosure;
import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.context.*;
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension; import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension;
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter; import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter; import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter;
import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.GenerationState;
@@ -79,6 +78,7 @@ import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isGenericToArray;
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isNonGenericToArray; import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isNonGenericToArray;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.initDefaultSourceMappingIfNeeded;
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall; import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall;
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getNotNull; import static org.jetbrains.kotlin.resolve.BindingContextUtils.getNotNull;
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration; import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
@@ -217,7 +217,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
v.visitSource(myClass.getContainingKtFile().getName(), null); v.visitSource(myClass.getContainingKtFile().getName(), null);
InlineCodegenUtil.initDefaultSourceMappingIfNeeded(context, this, state); initDefaultSourceMappingIfNeeded(context, this, state);
writeEnclosingMethod(); writeEnclosingMethod();
@@ -25,7 +25,10 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.backend.common.CodegenUtil; import org.jetbrains.kotlin.backend.common.CodegenUtil;
import org.jetbrains.kotlin.codegen.annotation.AnnotatedSimple; import org.jetbrains.kotlin.codegen.annotation.AnnotatedSimple;
import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.context.*;
import org.jetbrains.kotlin.codegen.inline.*; import org.jetbrains.kotlin.codegen.inline.DefaultSourceMapper;
import org.jetbrains.kotlin.codegen.inline.NameGenerator;
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeParametersUsages;
import org.jetbrains.kotlin.codegen.inline.SourceMapper;
import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension; import org.jetbrains.kotlin.codegen.serialization.JvmSerializerExtension;
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;
@@ -71,6 +74,7 @@ import java.util.*;
import static org.jetbrains.kotlin.codegen.AsmUtil.*; import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvm8InterfaceWithDefaultsMember; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isJvm8InterfaceWithDefaultsMember;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isNonDefaultInterfaceMember; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isNonDefaultInterfaceMember;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.getInlineName;
import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED; import static org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED;
import static org.jetbrains.kotlin.resolve.BindingContext.*; import static org.jetbrains.kotlin.resolve.BindingContext.*;
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
@@ -437,7 +441,7 @@ public abstract class MemberCodegen<T extends KtPureElement/* TODO: & KtDeclarat
@NotNull @NotNull
public NameGenerator getInlineNameGenerator() { public NameGenerator getInlineNameGenerator() {
if (inlineNameGenerator == null) { if (inlineNameGenerator == null) {
String prefix = InlineCodegenUtil.getInlineName(context, typeMapper, fileClassesProvider); String prefix = getInlineName(context, typeMapper, fileClassesProvider);
inlineNameGenerator = new NameGenerator(prefix); inlineNameGenerator = new NameGenerator(prefix);
} }
return inlineNameGenerator; return inlineNameGenerator;
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.codegen;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
import org.jetbrains.org.objectweb.asm.MethodVisitor; import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.Opcodes; import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.tree.LocalVariableNode; import org.jetbrains.org.objectweb.asm.tree.LocalVariableNode;
@@ -29,6 +28,9 @@ import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.getNodeText;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.wrapWithMaxLocalCalc;
public abstract class TransformationMethodVisitor extends MethodVisitor { public abstract class TransformationMethodVisitor extends MethodVisitor {
private final MethodNode methodNode; private final MethodNode methodNode;
@@ -46,7 +48,7 @@ public abstract class TransformationMethodVisitor extends MethodVisitor {
this.delegate = delegate; this.delegate = delegate;
this.methodNode = new MethodNode(access, name, desc, signature, exceptions); this.methodNode = new MethodNode(access, name, desc, signature, exceptions);
this.methodNode.localVariables = new ArrayList<>(5); this.methodNode.localVariables = new ArrayList<>(5);
this.mv = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode); this.mv = wrapWithMaxLocalCalc(methodNode);
} }
@Override @Override
@@ -83,7 +85,7 @@ public abstract class TransformationMethodVisitor extends MethodVisitor {
delegate.visitEnd(); delegate.visitEnd();
} }
catch (Throwable t) { catch (Throwable t) {
throw new CompilationException("Couldn't transform method node: " + InlineCodegenUtil.getNodeText(methodNode), t, null); throw new CompilationException("Couldn't transform method node: " + getNodeText(methodNode), t, null);
} }
} }
@@ -21,8 +21,8 @@ import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.TransformationMethodVisitor import org.jetbrains.kotlin.codegen.TransformationMethodVisitor
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil
import org.jetbrains.kotlin.codegen.inline.MaxStackFrameSizeAndLocalsCalculator import org.jetbrains.kotlin.codegen.inline.MaxStackFrameSizeAndLocalsCalculator
import org.jetbrains.kotlin.codegen.inline.isInlineMarker
import org.jetbrains.kotlin.codegen.optimization.DeadCodeEliminationMethodTransformer import org.jetbrains.kotlin.codegen.optimization.DeadCodeEliminationMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.common.* import org.jetbrains.kotlin.codegen.optimization.common.*
import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer
@@ -681,7 +681,7 @@ private fun findSafelyReachableReturns(methodNode: MethodNode): Array<Set<Int>?>
} }
if (!insn.isMeaningful || insn.opcode in SAFE_OPCODES || insn.isInvisibleInDebugVarInsn(methodNode) || if (!insn.isMeaningful || insn.opcode in SAFE_OPCODES || insn.isInvisibleInDebugVarInsn(methodNode) ||
InlineCodegenUtil.isInlineMarker(insn)) { isInlineMarker(insn)) {
setOf() setOf()
} }
else null else null
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.ClassBuilder
import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.coroutines.COROUTINE_IMPL_ASM_TYPE import org.jetbrains.kotlin.codegen.coroutines.COROUTINE_IMPL_ASM_TYPE
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.isThis0
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Companion.NO_ORIGIN import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin.Companion.NO_ORIGIN
import org.jetbrains.org.objectweb.asm.* import org.jetbrains.org.objectweb.asm.*
@@ -49,7 +48,7 @@ class AnonymousObjectTransformer(
val classBuilder = createRemappingClassBuilderViaFactory(inliningContext) val classBuilder = createRemappingClassBuilderViaFactory(inliningContext)
val methodsToTransform = ArrayList<MethodNode>() val methodsToTransform = ArrayList<MethodNode>()
createClassReader().accept(object : ClassVisitor(InlineCodegenUtil.API, classBuilder.visitor) { createClassReader().accept(object : ClassVisitor(API, classBuilder.visitor) {
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) { override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) {
classBuilder.defineClass(null, version, access, name, signature, superName, interfaces) classBuilder.defineClass(null, version, access, name, signature, superName, interfaces)
if (COROUTINE_IMPL_ASM_TYPE.internalName == superName) { if (COROUTINE_IMPL_ASM_TYPE.internalName == superName) {
@@ -79,7 +78,7 @@ class AnonymousObjectTransformer(
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? {
addUniqueField(name) addUniqueField(name)
if (InlineCodegenUtil.isCapturedFieldName(name)) { if (isCapturedFieldName(name)) {
return null return null
} }
else { else {
@@ -103,7 +102,7 @@ class AnonymousObjectTransformer(
//seems we can't do any clever mapping cause we don't know any about original class name //seems we can't do any clever mapping cause we don't know any about original class name
sourceMapper = IdenticalSourceMapper sourceMapper = IdenticalSourceMapper
} }
if (sourceInfo != null && !InlineCodegenUtil.GENERATE_SMAP) { if (sourceInfo != null && !GENERATE_SMAP) {
classBuilder.visitSource(sourceInfo!!, debugInfo) classBuilder.visitSource(sourceInfo!!, debugInfo)
} }
} }
@@ -139,7 +138,7 @@ class AnonymousObjectTransformer(
} }
deferringMethods.forEach { method -> deferringMethods.forEach { method ->
InlineCodegenUtil.removeFinallyMarkers(method.intermediate) removeFinallyMarkers(method.intermediate)
method.visitEnd() method.visitEnd()
} }
@@ -286,7 +285,7 @@ class AnonymousObjectTransformer(
val intermediateMethodNode = MethodNode(constructor!!.access, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY) val intermediateMethodNode = MethodNode(constructor!!.access, "<init>", constructorDescriptor, null, ArrayUtil.EMPTY_STRING_ARRAY)
inlineMethodAndUpdateGlobalResult(parentRemapper, intermediateMethodNode, constructor!!, constructorInlineBuilder, true) inlineMethodAndUpdateGlobalResult(parentRemapper, intermediateMethodNode, constructor!!, constructorInlineBuilder, true)
InlineCodegenUtil.removeFinallyMarkers(intermediateMethodNode) removeFinallyMarkers(intermediateMethodNode)
val first = intermediateMethodNode.instructions.first val first = intermediateMethodNode.instructions.first
val oldStartLabel = if (first is LabelNode) first.label else null val oldStartLabel = if (first is LabelNode) first.label else null
@@ -429,8 +428,8 @@ class AnonymousObjectTransformer(
val parent = parentFieldRemapper.parent as? RegeneratedLambdaFieldRemapper ?: val parent = parentFieldRemapper.parent as? RegeneratedLambdaFieldRemapper ?:
throw AssertionError("Expecting RegeneratedLambdaFieldRemapper, but ${parentFieldRemapper.parent}") throw AssertionError("Expecting RegeneratedLambdaFieldRemapper, but ${parentFieldRemapper.parent}")
val ownerType = Type.getObjectType(parent.originalLambdaInternalName) val ownerType = Type.getObjectType(parent.originalLambdaInternalName)
val desc = CapturedParamDesc(ownerType, InlineCodegenUtil.THIS, ownerType) val desc = CapturedParamDesc(ownerType, THIS, ownerType)
val recapturedParamInfo = capturedParamBuilder.addCapturedParam(desc, InlineCodegenUtil.`THIS$0`/*outer lambda/object*/, false) val recapturedParamInfo = capturedParamBuilder.addCapturedParam(desc, THIS_0/*outer lambda/object*/, false)
val composed = StackValue.LOCAL_0 val composed = StackValue.LOCAL_0
recapturedParamInfo.remapValue = composed recapturedParamInfo.remapValue = composed
allRecapturedParameters.add(desc) allRecapturedParameters.add(desc)
@@ -458,16 +457,16 @@ class AnonymousObjectTransformer(
} }
private fun getNewFieldName(oldName: String, originalField: Boolean): String { private fun getNewFieldName(oldName: String, originalField: Boolean): String {
if (InlineCodegenUtil.`THIS$0` == oldName) { if (THIS_0 == 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 + INLINE_FUN_THIS_0_SUFFIX)
} }
} }
return addUniqueField(oldName + InlineCodegenUtil.INLINE_TRANSFORMATION_SUFFIX) return addUniqueField(oldName + INLINE_TRANSFORMATION_SUFFIX)
} }
private fun addUniqueField(name: String): String { private fun addUniqueField(name: String): String {
@@ -38,7 +38,7 @@ abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) {
open fun processInstruction(curInstr: AbstractInsnNode, directOrder: Boolean) { open fun processInstruction(curInstr: AbstractInsnNode, directOrder: Boolean) {
if (curInstr is VarInsnNode || curInstr is IincInsnNode) { if (curInstr is VarInsnNode || curInstr is IincInsnNode) {
val argSize = InlineCodegenUtil.getLoadStoreArgSize(curInstr.opcode) val argSize = getLoadStoreArgSize(curInstr.opcode)
val varIndex = if (curInstr is VarInsnNode) curInstr.`var` else (curInstr as IincInsnNode).`var` val varIndex = if (curInstr is VarInsnNode) curInstr.`var` else (curInstr as IincInsnNode).`var`
nextFreeLocalIndex = Math.max(nextFreeLocalIndex, varIndex + argSize) nextFreeLocalIndex = Math.max(nextFreeLocalIndex, varIndex + argSize)
} }
@@ -22,7 +22,7 @@ import org.jetbrains.org.objectweb.asm.tree.MethodNode
class DeferredMethodVisitor( class DeferredMethodVisitor(
val intermediate: MethodNode, val intermediate: MethodNode,
val resultNode: () -> MethodVisitor val resultNode: () -> MethodVisitor
) : MethodVisitor(InlineCodegenUtil.API, intermediate) { ) : MethodVisitor(API, intermediate) {
override fun visitEnd() { override fun visitEnd() {
super.visitEnd() super.visitEnd()
@@ -35,7 +35,7 @@ open class FieldRemapper(
protected open fun canProcess(fieldOwner: String, fieldName: String, isFolding: Boolean): Boolean { protected open fun canProcess(fieldOwner: String, fieldName: String, isFolding: Boolean): Boolean {
return fieldOwner == originalLambdaInternalName && return fieldOwner == originalLambdaInternalName &&
//don't process general field of anonymous objects //don't process general field of anonymous objects
InlineCodegenUtil.isCapturedFieldName(fieldName) isCapturedFieldName(fieldName)
} }
fun foldFieldAccessChainIfNeeded(capturedFieldAccess: List<AbstractInsnNode>, node: MethodNode): AbstractInsnNode? = fun foldFieldAccessChainIfNeeded(capturedFieldAccess: List<AbstractInsnNode>, node: MethodNode): AbstractInsnNode? =
@@ -68,7 +68,7 @@ open class FieldRemapper(
val insnNode = capturedFieldAccess[currentInstruction] as FieldInsnNode val insnNode = capturedFieldAccess[currentInstruction] as FieldInsnNode
if (canProcess(insnNode.owner, insnNode.name, true)) { if (canProcess(insnNode.owner, insnNode.name, true)) {
insnNode.name = InlineCodegenUtil.CAPTURED_FIELD_FOLD_PREFIX + getFieldNameForFolding(insnNode) insnNode.name = CAPTURED_FIELD_FOLD_PREFIX + getFieldNameForFolding(insnNode)
insnNode.opcode = Opcodes.GETSTATIC insnNode.opcode = Opcodes.GETSTATIC
node.remove(InsnSequence(capturedFieldAccess[0], insnNode)) node.remove(InsnSequence(capturedFieldAccess[0], insnNode))
@@ -25,7 +25,9 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.getLoadStoreArgSize; import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.API;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.GENERATE_SMAP;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.getLoadStoreArgSize;
public class InlineAdapter extends InstructionAdapter { public class InlineAdapter extends InstructionAdapter {
private final SourceMapper sourceMapper; private final SourceMapper sourceMapper;
@@ -36,7 +38,7 @@ public class InlineAdapter extends InstructionAdapter {
private int nextLocalIndexBeforeInline = -1; private int nextLocalIndexBeforeInline = -1;
public InlineAdapter(@NotNull MethodVisitor mv, int localsSize, @NotNull SourceMapper sourceMapper) { public InlineAdapter(@NotNull MethodVisitor mv, int localsSize, @NotNull SourceMapper sourceMapper) {
super(InlineCodegenUtil.API, mv); super(API, mv);
this.nextLocalIndex = localsSize; this.nextLocalIndex = localsSize;
this.sourceMapper = sourceMapper; this.sourceMapper = sourceMapper;
} }
@@ -86,7 +88,7 @@ public class InlineAdapter extends InstructionAdapter {
@Override @Override
public void visitLineNumber(int line, @NotNull Label start) { public void visitLineNumber(int line, @NotNull Label start) {
if (InlineCodegenUtil.GENERATE_SMAP) { if (GENERATE_SMAP) {
line = sourceMapper.mapLineNumber(line); line = sourceMapper.mapLineNumber(line);
} }
//skip not mapped lines //skip not mapped lines
@@ -26,7 +26,6 @@ import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
import org.jetbrains.kotlin.codegen.context.* import org.jetbrains.kotlin.codegen.context.*
import org.jetbrains.kotlin.codegen.coroutines.createMethodNodeForSuspendCoroutineOrReturn import org.jetbrains.kotlin.codegen.coroutines.createMethodNodeForSuspendCoroutineOrReturn
import org.jetbrains.kotlin.codegen.coroutines.isBuiltInSuspendCoroutineOrReturnInJvm import org.jetbrains.kotlin.codegen.coroutines.isBuiltInSuspendCoroutineOrReturnInJvm
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*
import org.jetbrains.kotlin.codegen.intrinsics.bytecode import org.jetbrains.kotlin.codegen.intrinsics.bytecode
import org.jetbrains.kotlin.codegen.intrinsics.classId import org.jetbrains.kotlin.codegen.intrinsics.classId
import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.GenerationState
@@ -168,7 +167,7 @@ class InlineCodegen(
"Couldn't inline method call '" + functionDescriptor.name + "' into\n" + "Couldn't inline method call '" + functionDescriptor.name + "' into\n" +
DescriptorRenderer.DEBUG_TEXT.render(contextDescriptor) + "\n" + DescriptorRenderer.DEBUG_TEXT.render(contextDescriptor) + "\n" +
(element?.text ?: "<no source>") + (element?.text ?: "<no source>") +
if (generateNodeText) "\nCause: " + InlineCodegenUtil.getNodeText(node) else "", if (generateNodeText) "\nCause: " + getNodeText(node) else "",
e, callElement e, callElement
) )
} }
@@ -230,7 +229,7 @@ class InlineCodegen(
val remapper = LocalVarRemapper(parameters, initialFrameSize) val remapper = LocalVarRemapper(parameters, initialFrameSize)
val adapter = InlineCodegenUtil.createEmptyMethodNode() val adapter = 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)
@@ -245,8 +244,8 @@ class InlineCodegen(
adapter, infos, (remapper.remap(parameters.argsSizeOnStack + 1).value as StackValue.Local).index adapter, infos, (remapper.remap(parameters.argsSizeOnStack + 1).value as StackValue.Local).index
) )
removeStaticInitializationTrigger(adapter) removeStaticInitializationTrigger(adapter)
if (!InlineCodegenUtil.isFinallyMarkerRequired(codegen.getContext())) { if (!isFinallyMarkerRequired(codegen.getContext())) {
InlineCodegenUtil.removeFinallyMarkers(adapter) removeFinallyMarkers(adapter)
} }
adapter.accept(MethodBodyVisitor(codegen.v)) adapter.accept(MethodBodyVisitor(codegen.v))
@@ -539,7 +538,7 @@ class InlineCodegen(
var curInstr: AbstractInsnNode? = intoNode.instructions.first var curInstr: AbstractInsnNode? = intoNode.instructions.first
while (curInstr != null) { while (curInstr != null) {
processor.processInstruction(curInstr, true) processor.processInstruction(curInstr, true)
if (InlineCodegenUtil.isFinallyStart(curInstr)) { if (isFinallyStart(curInstr)) {
//TODO depth index calc could be more precise //TODO depth index calc could be more precise
curFinallyDepth = getConstant(curInstr.previous) curFinallyDepth = getConstant(curInstr.previous)
} }
@@ -548,7 +547,7 @@ class InlineCodegen(
if (extension != null) { if (extension != null) {
val start = Label() val start = Label()
val finallyNode = InlineCodegenUtil.createEmptyMethodNode() val finallyNode = createEmptyMethodNode()
finallyNode.visitLabel(start) finallyNode.visitLabel(start)
val finallyCodegen = ExpressionCodegen(finallyNode, codegen.frameMap, codegen.returnType, val finallyCodegen = ExpressionCodegen(finallyNode, codegen.frameMap, codegen.returnType,
@@ -569,7 +568,7 @@ class InlineCodegen(
finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType, extension.finallyIntervalEnd.label) finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType, extension.finallyIntervalEnd.label)
//Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method //Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method
InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, curInstr) insertNodeBefore(finallyNode, intoNode, curInstr)
val splitBy = SimpleInterval(start.info as LabelNode, extension.finallyIntervalEnd) val splitBy = SimpleInterval(start.info as LabelNode, extension.finallyIntervalEnd)
processor.tryBlocksMetaInfo.splitCurrentIntervals(splitBy, true) processor.tryBlocksMetaInfo.splitCurrentIntervals(splitBy, true)
@@ -620,12 +619,12 @@ class InlineCodegen(
callDefault: Boolean, callDefault: Boolean,
resolvedCall: ResolvedCall<*>? resolvedCall: ResolvedCall<*>?
): SMAPAndMethodNode { ): SMAPAndMethodNode {
if (InlineCodegenUtil.isSpecialEnumMethod(functionDescriptor)) { if (isSpecialEnumMethod(functionDescriptor)) {
assert(resolvedCall != null) { "Resolved call for $functionDescriptor should be not null" } assert(resolvedCall != null) { "Resolved call for $functionDescriptor should be not null" }
val arguments = resolvedCall!!.typeArguments val arguments = resolvedCall!!.typeArguments
assert(arguments.size == 1) { "Resolved call for $functionDescriptor should have 1 type argument" } assert(arguments.size == 1) { "Resolved call for $functionDescriptor should have 1 type argument" }
val node = InlineCodegenUtil.createSpecialEnumMethodBody( val node = createSpecialEnumMethodBody(
codegen, codegen,
functionDescriptor.name.asString(), functionDescriptor.name.asString(),
arguments.keys.iterator().next().defaultType, arguments.keys.iterator().next().defaultType,
@@ -689,7 +688,7 @@ class InlineCodegen(
if (isBuiltInArrayIntrinsic(callableDescriptor)) { if (isBuiltInArrayIntrinsic(callableDescriptor)) {
val classId = classId val classId = classId
val bytes = state.inlineCache.classBytes.getOrPut(classId) { bytecode } val bytes = state.inlineCache.classBytes.getOrPut(classId) { bytecode }
return InlineCodegenUtil.getMethodNode(bytes, asmMethod.name, asmMethod.descriptor, classId.asString()) return getMethodNode(bytes, asmMethod.name, asmMethod.descriptor, classId.asString())
} }
assert(callableDescriptor is DeserializedCallableMemberDescriptor) { "Not a deserialized function or proper: " + callableDescriptor } assert(callableDescriptor is DeserializedCallableMemberDescriptor) { "Not a deserialized function or proper: " + callableDescriptor }
@@ -699,7 +698,7 @@ class InlineCodegen(
val containerId = containingClasses.implClassId val containerId = containingClasses.implClassId
val bytes = state.inlineCache.classBytes.getOrPut(containerId) { val bytes = state.inlineCache.classBytes.getOrPut(containerId) {
val file = InlineCodegenUtil.findVirtualFile(state, containerId) ?: throw IllegalStateException("Couldn't find declaration file for " + containerId) val file = findVirtualFile(state, containerId) ?: throw IllegalStateException("Couldn't find declaration file for " + containerId)
try { try {
file.contentsToByteArray() file.contentsToByteArray()
} }
@@ -708,7 +707,7 @@ class InlineCodegen(
} }
} }
return InlineCodegenUtil.getMethodNode(bytes, asmMethod.name, asmMethod.descriptor, containerId.asString()) return getMethodNode(bytes, asmMethod.name, asmMethod.descriptor, containerId.asString())
} }
private fun doCreateMethodNodeFromSource( private fun doCreateMethodNodeFromSource(
@@ -728,14 +727,14 @@ class InlineCodegen(
val inliningFunction = element as KtDeclarationWithBody? val inliningFunction = element as KtDeclarationWithBody?
val node = MethodNode( val node = MethodNode(
InlineCodegenUtil.API, API,
getMethodAsmFlags(callableDescriptor, context.contextKind, state) or if (callDefault) Opcodes.ACC_STATIC else 0, getMethodAsmFlags(callableDescriptor, context.contextKind, state) or if (callDefault) Opcodes.ACC_STATIC else 0,
asmMethod.name, asmMethod.name,
asmMethod.descriptor, null, null asmMethod.descriptor, null, null
) )
//for maxLocals calculation //for maxLocals calculation
val maxCalcAdapter = InlineCodegenUtil.wrapWithMaxLocalCalc(node) val maxCalcAdapter = wrapWithMaxLocalCalc(node)
val parentContext = context.parentContext ?: error("Context has no parent: " + context) val parentContext = context.parentContext ?: error("Context has no parent: " + context)
val methodContext = parentContext.intoFunction(callableDescriptor) val methodContext = parentContext.intoFunction(callableDescriptor)
@@ -919,7 +918,7 @@ class InlineCodegen(
if (!descriptor.name.isSpecial) { if (!descriptor.name.isSpecial) {
result.add(descriptor.name.asString()) result.add(descriptor.name.asString())
} }
result.add(InlineCodegenUtil.FIRST_FUN_LABEL) result.add(FIRST_FUN_LABEL)
} }
return result return result
} }
@@ -14,580 +14,534 @@
* limitations under the License. * limitations under the License.
*/ */
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.PsiFile; import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import kotlin.text.StringsKt; import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.codegen.MemberCodegen
import org.jetbrains.kotlin.backend.common.output.OutputFile; import org.jetbrains.kotlin.codegen.binding.CodegenBinding
import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.codegen.context.CodegenContext
import org.jetbrains.kotlin.codegen.AsmUtil; import org.jetbrains.kotlin.codegen.context.CodegenContextUtil
import org.jetbrains.kotlin.codegen.ExpressionCodegen; import org.jetbrains.kotlin.codegen.context.InlineLambdaContext
import org.jetbrains.kotlin.codegen.MemberCodegen; import org.jetbrains.kotlin.codegen.context.MethodContext
import org.jetbrains.kotlin.codegen.binding.CodegenBinding; import org.jetbrains.kotlin.codegen.intrinsics.*
import org.jetbrains.kotlin.codegen.context.CodegenContext; import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.codegen.context.CodegenContextUtil; import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.codegen.context.InlineLambdaContext; import org.jetbrains.kotlin.codegen.`when`.WhenByEnumsMapping
import org.jetbrains.kotlin.codegen.context.MethodContext; import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicArrayConstructorsKt; import org.jetbrains.kotlin.fileClasses.*
import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper; import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping; import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.fileClasses.FileClasses; import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider; import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.load.kotlin.VirtualFileFinder; import org.jetbrains.kotlin.resolve.jvm.JvmClassName
import org.jetbrains.kotlin.name.ClassId; import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.name.Name; import org.jetbrains.org.objectweb.asm.*
import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; import org.jetbrains.org.objectweb.asm.tree.*
import org.jetbrains.kotlin.resolve.jvm.AsmTypes; import org.jetbrains.org.objectweb.asm.util.Printer
import org.jetbrains.kotlin.resolve.jvm.JvmClassName; import org.jetbrains.org.objectweb.asm.util.Textifier
import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor
import org.jetbrains.kotlin.util.OperatorNameConventions;
import org.jetbrains.org.objectweb.asm.*;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.tree.*;
import org.jetbrains.org.objectweb.asm.util.Printer;
import org.jetbrains.org.objectweb.asm.util.Textifier;
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
import java.io.IOException; import java.io.IOException
import java.io.PrintWriter; import java.io.PrintWriter
import java.io.StringWriter; import java.io.StringWriter
import java.util.List;
import java.util.ListIterator;
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.ENUM_TYPE; import org.jetbrains.kotlin.resolve.jvm.AsmTypes.ENUM_TYPE
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_CLASS_TYPE; import org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_CLASS_TYPE
public class InlineCodegenUtil {
public static final boolean GENERATE_SMAP = true;
public static final int API = Opcodes.ASM5;
private static final String CAPTURED_FIELD_PREFIX = "$"; const val GENERATE_SMAP = true
private static final String NON_CAPTURED_FIELD_PREFIX = "$$"; const val API = Opcodes.ASM5
public static final String THIS$0 = "this$0"; const val THIS = "this"
public static final String THIS = "this"; const val THIS_0 = "this$0"
private static final String RECEIVER$0 = "receiver$0"; const val FIRST_FUN_LABEL = "$$$$\$ROOT$$$$$"
private static final String NON_LOCAL_RETURN = "$$$$$NON_LOCAL_RETURN$$$$$"; const val NUMBERED_FUNCTION_PREFIX = "kotlin/jvm/functions/Function"
public static final String FIRST_FUN_LABEL = "$$$$$ROOT$$$$$"; const val SPECIAL_TRANSFORMATION_NAME = "\$special"
public static final String NUMBERED_FUNCTION_PREFIX = "kotlin/jvm/functions/Function"; const val INLINE_TRANSFORMATION_SUFFIX = "\$inlined"
private static final String INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker"; const val INLINE_CALL_TRANSFORMATION_SUFFIX = "$" + INLINE_TRANSFORMATION_SUFFIX
private static final String INLINE_MARKER_BEFORE_METHOD_NAME = "beforeInlineCall"; const val INLINE_FUN_THIS_0_SUFFIX = "\$inline_fun"
private static final String INLINE_MARKER_AFTER_METHOD_NAME = "afterInlineCall"; const val INLINE_FUN_VAR_SUFFIX = "\$iv"
private static final String INLINE_MARKER_FINALLY_START = "finallyStart"; const val DEFAULT_LAMBDA_FAKE_CALL = "$$\$DEFAULT_LAMBDA_FAKE_CALL$$$"
private static final String INLINE_MARKER_FINALLY_END = "finallyEnd";
public static final String SPECIAL_TRANSFORMATION_NAME = "$special";
public static final String INLINE_TRANSFORMATION_SUFFIX = "$inlined";
public static final String INLINE_CALL_TRANSFORMATION_SUFFIX = "$" + INLINE_TRANSFORMATION_SUFFIX;
public static final String INLINE_FUN_THIS_0_SUFFIX = "$inline_fun";
public static final String INLINE_FUN_VAR_SUFFIX = "$iv";
public static final String DEFAULT_LAMBDA_FAKE_CALL = "$$$DEFAULT_LAMBDA_FAKE_CALL$$$"; const val CAPTURED_FIELD_FOLD_PREFIX = "$$$"
public static final String CAPTURED_FIELD_FOLD_PREFIX = "$$$"; private const val `RECEIVER$0` = "receiver$0"
private const val NON_LOCAL_RETURN = "$$$$\$NON_LOCAL_RETURN$$$$$"
private const val CAPTURED_FIELD_PREFIX = "$"
private const val NON_CAPTURED_FIELD_PREFIX = "$$"
private const val INLINE_MARKER_CLASS_NAME = "kotlin/jvm/internal/InlineMarker"
private const val INLINE_MARKER_BEFORE_METHOD_NAME = "beforeInlineCall"
private const val INLINE_MARKER_AFTER_METHOD_NAME = "afterInlineCall"
private const val INLINE_MARKER_FINALLY_START = "finallyStart"
@Nullable private const val INLINE_MARKER_FINALLY_END = "finallyEnd"
public static SMAPAndMethodNode getMethodNode(
byte[] classData,
@NotNull String methodName,
@NotNull String methodDescriptor,
@NotNull String classInternalName
) {
ClassReader cr = new ClassReader(classData);
MethodNode[] node = new MethodNode[1];
String[] debugInfo = new String[2];
int[] lines = new int[2];
lines[0] = Integer.MAX_VALUE;
lines[1] = Integer.MIN_VALUE;
//noinspection PointlessBitwiseExpression
cr.accept(new ClassVisitor(API) {
@Override fun getMethodNode(
public void visitSource(String source, String debug) { classData: ByteArray,
super.visitSource(source, debug); methodName: String,
debugInfo[0] = source; methodDescriptor: String,
debugInfo[1] = debug; classInternalName: String
): SMAPAndMethodNode? {
val cr = ClassReader(classData)
var node: MethodNode? = null
val debugInfo = arrayOfNulls<String>(2)
val lines = IntArray(2)
lines[0] = Integer.MAX_VALUE
lines[1] = Integer.MIN_VALUE
cr.accept(object : ClassVisitor(API) {
override fun visitSource(source: String?, debug: String?) {
super.visitSource(source, debug)
debugInfo[0] = source
debugInfo[1] = debug
} }
@Override override fun visitMethod(
public MethodVisitor visitMethod( access: Int,
int access, name: String,
@NotNull String name, desc: String,
@NotNull String desc, signature: String?,
String signature, exceptions: Array<String>?
String[] exceptions ): MethodVisitor? {
) { if (methodName == name && methodDescriptor == desc) {
if (methodName.equals(name) && methodDescriptor.equals(desc)) { node = object : MethodNode(API, access, name, desc, signature, exceptions) {
node[0] = new MethodNode(API, access, name, desc, signature, exceptions) { override fun visitLineNumber(line: Int, start: Label) {
@Override super.visitLineNumber(line, start)
public void visitLineNumber(int line, @NotNull Label start) { lines[0] = Math.min(lines[0], line)
super.visitLineNumber(line, start); lines[1] = Math.max(lines[1], line)
lines[0] = Math.min(lines[0], line);
lines[1] = Math.max(lines[1], line);
} }
};
return node[0];
} }
return null; return node
} }
}, ClassReader.SKIP_FRAMES | (GENERATE_SMAP ? 0 : ClassReader.SKIP_DEBUG)); return null
}
}, ClassReader.SKIP_FRAMES or if (GENERATE_SMAP) 0 else ClassReader.SKIP_DEBUG)
if (node[0] == null) { if (node == null) {
return null; return null
} }
if (IntrinsicArrayConstructorsKt.getClassId().asString().equals(classInternalName)) { if (classId.asString() == classInternalName) {
// Don't load source map for intrinsic array constructors // Don't load source map for intrinsic array constructors
debugInfo[0] = null; debugInfo[0] = null
} }
SMAP smap = SMAPParser.parseOrCreateDefault(debugInfo[1], debugInfo[0], classInternalName, lines[0], lines[1]); val smap = SMAPParser.parseOrCreateDefault(debugInfo[1], debugInfo[0], classInternalName, lines[0], lines[1])
return new SMAPAndMethodNode(node[0], smap); return SMAPAndMethodNode(node!!, smap)
} }
public static void initDefaultSourceMappingIfNeeded( fun initDefaultSourceMappingIfNeeded(
@NotNull CodegenContext context, @NotNull MemberCodegen codegen, @NotNull GenerationState state context: CodegenContext<*>, codegen: MemberCodegen<*>, state: GenerationState
) { ) {
if (state.isInlineDisabled()) return; if (state.isInlineDisabled) return
CodegenContext<?> parentContext = context.getParentContext(); var parentContext: CodegenContext<*>? = context.parentContext
while (parentContext != null) { while (parentContext != null) {
if (parentContext.isInlineMethodContext()) { if (parentContext.isInlineMethodContext) {
//just init default one to one mapping //just init default one to one mapping
codegen.getOrCreateSourceMapper(); codegen.orCreateSourceMapper
break; break
} }
parentContext = parentContext.getParentContext(); parentContext = parentContext.parentContext
} }
} }
@Nullable fun findVirtualFile(state: GenerationState, classId: ClassId): VirtualFile? {
public static VirtualFile findVirtualFile(@NotNull GenerationState state, @NotNull ClassId classId) { return VirtualFileFinder.getInstance(state.project).findVirtualFileWithHeader(classId)
return VirtualFileFinder.SERVICE.getInstance(state.getProject()).findVirtualFileWithHeader(classId);
} }
@Nullable fun findVirtualFileImprecise(state: GenerationState, internalClassName: String): VirtualFile? {
public static VirtualFile findVirtualFileImprecise(@NotNull GenerationState state, @NotNull String internalClassName) { val packageFqName = JvmClassName.byInternalName(internalClassName).packageFqName
FqName packageFqName = JvmClassName.byInternalName(internalClassName).getPackageFqName(); val classNameWithDollars = internalClassName.substringAfterLast("/", 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
// we construct valid.package.name/RelativeClassNameAsSingleName that should work in compiler, but fails for inner classes in IDE // we construct valid.package.name/RelativeClassNameAsSingleName that should work in compiler, but fails for inner classes in IDE
return findVirtualFile(state, new ClassId(packageFqName, Name.identifier(classNameWithDollars))); return findVirtualFile(state, ClassId(packageFqName, Name.identifier(classNameWithDollars)))
} }
@NotNull fun getInlineName(
public static String getInlineName( codegenContext: CodegenContext<*>,
@NotNull CodegenContext codegenContext, typeMapper: KotlinTypeMapper,
@NotNull KotlinTypeMapper typeMapper, fileClassesManager: JvmFileClassesProvider
@NotNull JvmFileClassesProvider fileClassesManager ): String {
) { return getInlineName(codegenContext, codegenContext.contextDescriptor, typeMapper, fileClassesManager)
return getInlineName(codegenContext, codegenContext.getContextDescriptor(), typeMapper, fileClassesManager);
} }
@NotNull private fun getInlineName(
private static String getInlineName( codegenContext: CodegenContext<*>,
@NotNull CodegenContext codegenContext, currentDescriptor: DeclarationDescriptor,
@NotNull DeclarationDescriptor currentDescriptor, typeMapper: KotlinTypeMapper,
@NotNull KotlinTypeMapper typeMapper, fileClassesProvider: JvmFileClassesProvider
@NotNull JvmFileClassesProvider fileClassesProvider ): String {
) { if (currentDescriptor is PackageFragmentDescriptor) {
if (currentDescriptor instanceof PackageFragmentDescriptor) { val file = DescriptorToSourceUtils.getContainingFile(codegenContext.contextDescriptor)
PsiFile file = DescriptorToSourceUtils.getContainingFile(codegenContext.getContextDescriptor());
Type implementationOwnerType; val implementationOwnerType: Type? =
if (file == null) { if (file == null) {
implementationOwnerType = CodegenContextUtil.getImplementationOwnerClassType(codegenContext); CodegenContextUtil.getImplementationOwnerClassType(codegenContext)
}
else {
implementationOwnerType = FileClasses.getFileClassType(fileClassesProvider, (KtFile) file);
} }
else fileClassesProvider.getFileClassType(file)
if (implementationOwnerType == null) { if (implementationOwnerType == null) {
DeclarationDescriptor contextDescriptor = codegenContext.getContextDescriptor(); val contextDescriptor = codegenContext.contextDescriptor
//noinspection ConstantConditions throw RuntimeException(
throw new RuntimeException(
"Couldn't find declaration for " + "Couldn't find declaration for " +
contextDescriptor.getContainingDeclaration().getName() + "." + contextDescriptor.getName() + contextDescriptor.containingDeclaration!!.name + "." + contextDescriptor.name +
"; context: " + codegenContext "; context: " + codegenContext
); )
} }
return implementationOwnerType.getInternalName(); return implementationOwnerType.internalName
} }
else if (currentDescriptor instanceof ClassifierDescriptor) { else if (currentDescriptor is ClassifierDescriptor) {
Type type = typeMapper.mapType((ClassifierDescriptor) currentDescriptor); return typeMapper.mapType(currentDescriptor).internalName
return type.getInternalName();
} }
else if (currentDescriptor instanceof FunctionDescriptor) { else if (currentDescriptor is FunctionDescriptor) {
ClassDescriptor descriptor = val descriptor = typeMapper.bindingContext.get(CodegenBinding.CLASS_FOR_CALLABLE, currentDescriptor)
typeMapper.getBindingContext().get(CodegenBinding.CLASS_FOR_CALLABLE, (FunctionDescriptor) currentDescriptor);
if (descriptor != null) { if (descriptor != null) {
return typeMapper.mapType(descriptor).getInternalName(); return typeMapper.mapType(descriptor).internalName
} }
} }
//TODO: add suffix for special case //TODO: add suffix for special case
String suffix = currentDescriptor.getName().isSpecial() ? "" : currentDescriptor.getName().asString(); val suffix = if (currentDescriptor.name.isSpecial) "" else currentDescriptor.name.asString()
//noinspection ConstantConditions
return getInlineName(codegenContext, currentDescriptor.getContainingDeclaration(), typeMapper, fileClassesProvider) + "$" + suffix; return getInlineName(codegenContext, currentDescriptor.containingDeclaration!!, typeMapper, fileClassesProvider) + "$" + suffix
} }
public static boolean isInvokeOnLambda(@NotNull String owner, @NotNull String name) { fun isInvokeOnLambda(owner: String, name: String): Boolean {
return OperatorNameConventions.INVOKE.asString().equals(name) && return OperatorNameConventions.INVOKE.asString() == name &&
owner.startsWith(NUMBERED_FUNCTION_PREFIX) && owner.startsWith(NUMBERED_FUNCTION_PREFIX) &&
isInteger(owner.substring(NUMBERED_FUNCTION_PREFIX.length())); isInteger(owner.substring(NUMBERED_FUNCTION_PREFIX.length))
} }
public static boolean isAnonymousConstructorCall(@NotNull String internalName, @NotNull String methodName) { fun isAnonymousConstructorCall(internalName: String, methodName: String): Boolean {
return "<init>".equals(methodName) && isAnonymousClass(internalName); return "<init>" == methodName && isAnonymousClass(internalName)
} }
public static boolean isWhenMappingAccess(@NotNull String internalName, @NotNull String fieldName) { fun isWhenMappingAccess(internalName: String, fieldName: String): Boolean {
return fieldName.startsWith(WhenByEnumsMapping.MAPPING_ARRAY_FIELD_PREFIX) && return fieldName.startsWith(WhenByEnumsMapping.MAPPING_ARRAY_FIELD_PREFIX) && internalName.endsWith(WhenByEnumsMapping.MAPPINGS_CLASS_NAME_POSTFIX)
internalName.endsWith(WhenByEnumsMapping.MAPPINGS_CLASS_NAME_POSTFIX);
} }
public static boolean isAnonymousSingletonLoad(@NotNull String internalName, @NotNull String fieldName) { fun isAnonymousSingletonLoad(internalName: String, fieldName: String): Boolean {
return JvmAbi.INSTANCE_FIELD.equals(fieldName) && isAnonymousClass(internalName); return JvmAbi.INSTANCE_FIELD == fieldName && isAnonymousClass(internalName)
} }
public static boolean isAnonymousClass(@NotNull String internalName) { fun isAnonymousClass(internalName: String): Boolean {
String shortName = getLastNamePart(internalName); val shortName = getLastNamePart(internalName)
int index = shortName.lastIndexOf("$"); val index = shortName.lastIndexOf("$")
if (index < 0) { if (index < 0) {
return false; return false
} }
String suffix = shortName.substring(index + 1); val suffix = shortName.substring(index + 1)
return isInteger(suffix); return isInteger(suffix)
} }
@NotNull private fun getLastNamePart(internalName: String): String {
private static String getLastNamePart(@NotNull String internalName) { val index = internalName.lastIndexOf("/")
int index = internalName.lastIndexOf("/"); return if (index < 0) internalName else internalName.substring(index + 1)
return index < 0 ? internalName : internalName.substring(index + 1);
} }
@NotNull fun wrapWithMaxLocalCalc(methodNode: MethodNode): MethodVisitor {
public static MethodVisitor wrapWithMaxLocalCalc(@NotNull MethodNode methodNode) { return MaxStackFrameSizeAndLocalsCalculator(API, methodNode.access, methodNode.desc, methodNode)
return new MaxStackFrameSizeAndLocalsCalculator(API, methodNode.access, methodNode.desc, methodNode);
} }
private static boolean isInteger(@NotNull String string) { private fun isInteger(string: String): Boolean {
if (string.isEmpty()) { if (string.isEmpty()) {
return false; return false
} }
for (int i = 0; i < string.length(); i++) { for (i in 0..string.length - 1) {
if (!Character.isDigit(string.charAt(i))) { if (!Character.isDigit(string[i])) {
return false; return false
} }
} }
return true; return true
} }
public static boolean isCapturedFieldName(@NotNull String fieldName) { fun isCapturedFieldName(fieldName: String): Boolean {
// TODO: improve this heuristic // TODO: improve this heuristic
return fieldName.startsWith(CAPTURED_FIELD_PREFIX) && return fieldName.startsWith(CAPTURED_FIELD_PREFIX) && !fieldName.startsWith(NON_CAPTURED_FIELD_PREFIX) ||
!fieldName.startsWith(NON_CAPTURED_FIELD_PREFIX) || THIS_0 == fieldName ||
THIS$0.equals(fieldName) || `RECEIVER$0` == fieldName
RECEIVER$0.equals(fieldName);
} }
public static boolean isReturnOpcode(int opcode) { fun isReturnOpcode(opcode: Int): Boolean {
return opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN; return opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN
} }
//marked return could be either non-local or local in case of labeled lambda self-returns //marked return could be either non-local or local in case of labeled lambda self-returns
public static boolean isMarkedReturn(@NotNull AbstractInsnNode returnIns) { fun isMarkedReturn(returnIns: AbstractInsnNode): Boolean {
return getMarkedReturnLabelOrNull(returnIns) != null; return getMarkedReturnLabelOrNull(returnIns) != null
} }
@Nullable fun getMarkedReturnLabelOrNull(returnInsn: AbstractInsnNode): String? {
public static String getMarkedReturnLabelOrNull(@NotNull AbstractInsnNode returnInsn) { if (!isReturnOpcode(returnInsn.opcode)) {
if (!isReturnOpcode(returnInsn.getOpcode())) { return null
return null;
} }
AbstractInsnNode previous = returnInsn.getPrevious(); val previous = returnInsn.previous
if (previous instanceof MethodInsnNode) { if (previous is MethodInsnNode) {
MethodInsnNode marker = (MethodInsnNode) previous; val marker = previous
if (NON_LOCAL_RETURN.equals(marker.owner)) { if (NON_LOCAL_RETURN == marker.owner) {
return marker.name; return marker.name
} }
} }
return null; return null
} }
public static void generateGlobalReturnFlag(@NotNull InstructionAdapter iv, @NotNull String labelName) { fun generateGlobalReturnFlag(iv: InstructionAdapter, labelName: String) {
iv.invokestatic(NON_LOCAL_RETURN, labelName, "()V", false); iv.invokestatic(NON_LOCAL_RETURN, labelName, "()V", false)
} }
@NotNull fun getReturnType(opcode: Int): Type {
public static Type getReturnType(int opcode) { when (opcode) {
switch (opcode) { Opcodes.RETURN -> return Type.VOID_TYPE
case Opcodes.RETURN: Opcodes.IRETURN -> return Type.INT_TYPE
return Type.VOID_TYPE; Opcodes.DRETURN -> return Type.DOUBLE_TYPE
case Opcodes.IRETURN: Opcodes.FRETURN -> return Type.FLOAT_TYPE
return Type.INT_TYPE; Opcodes.LRETURN -> return Type.LONG_TYPE
case Opcodes.DRETURN: else -> return AsmTypes.OBJECT_TYPE
return Type.DOUBLE_TYPE;
case Opcodes.FRETURN:
return Type.FLOAT_TYPE;
case Opcodes.LRETURN:
return Type.LONG_TYPE;
default:
return AsmTypes.OBJECT_TYPE;
} }
} }
public static void insertNodeBefore(@NotNull MethodNode from, @NotNull MethodNode to, @NotNull AbstractInsnNode beforeNode) { fun insertNodeBefore(from: MethodNode, to: MethodNode, beforeNode: AbstractInsnNode) {
ListIterator<AbstractInsnNode> iterator = from.instructions.iterator(); val iterator = from.instructions.iterator()
while (iterator.hasNext()) { while (iterator.hasNext()) {
AbstractInsnNode next = iterator.next(); val next = iterator.next()
to.instructions.insertBefore(beforeNode, next); to.instructions.insertBefore(beforeNode, next)
} }
} }
@NotNull fun createEmptyMethodNode(): MethodNode {
public static MethodNode createEmptyMethodNode() { return MethodNode(API, 0, "fake", "()V", null, null)
return new MethodNode(API, 0, "fake", "()V", null, null);
} }
@NotNull fun firstLabelInChain(node: LabelNode): LabelNode {
public static LabelNode firstLabelInChain(@NotNull LabelNode node) { var curNode = node
LabelNode curNode = node; while (curNode.previous is LabelNode) {
while (curNode.getPrevious() instanceof LabelNode) { curNode = curNode.previous as LabelNode
curNode = (LabelNode) curNode.getPrevious();
} }
return curNode; return curNode
} }
@NotNull fun getNodeText(node: MethodNode?): String {
public static String getNodeText(@Nullable MethodNode node) { val textifier = Textifier()
Textifier textifier = new Textifier();
if (node == null) { if (node == null) {
return "Not generated"; return "Not generated"
} }
node.accept(new TraceMethodVisitor(textifier)); node.accept(TraceMethodVisitor(textifier))
StringWriter sw = new StringWriter(); val sw = StringWriter()
textifier.print(new PrintWriter(sw)); textifier.print(PrintWriter(sw))
sw.flush(); sw.flush()
return node.name + " " + node.desc + ":\n" + sw.getBuffer().toString(); return node.name + " " + node.desc + ":\n" + sw.buffer.toString()
} }
@NotNull fun getInsnText(node: AbstractInsnNode?): String {
public static String getInsnText(@Nullable AbstractInsnNode node) { if (node == null) return "<null>"
if (node == null) return "<null>"; val textifier = Textifier()
Textifier textifier = new Textifier(); node.accept(TraceMethodVisitor(textifier))
node.accept(new TraceMethodVisitor(textifier)); val sw = StringWriter()
StringWriter sw = new StringWriter(); textifier.print(PrintWriter(sw))
textifier.print(new PrintWriter(sw)); sw.flush()
sw.flush(); return sw.toString().trim { it <= ' ' }
return sw.toString().trim();
} }
@NotNull fun getInsnOpcodeText(node: AbstractInsnNode?): String {
public static String getInsnOpcodeText(@Nullable AbstractInsnNode node) { return if (node == null) "null" else Printer.OPCODES[node.opcode]
return node == null ? "null" : Printer.OPCODES[node.getOpcode()];
} }
@NotNull internal /* package */ fun buildClassReaderByInternalName(state: GenerationState, internalName: String): ClassReader {
/* package */ static ClassReader buildClassReaderByInternalName(@NotNull GenerationState state, @NotNull String internalName) {
//try to find just compiled classes then in dependencies //try to find just compiled classes then in dependencies
try { try {
OutputFile outputFile = state.getFactory().get(internalName + ".class"); val outputFile = state.factory.get(internalName + ".class")
if (outputFile != null) { if (outputFile != null) {
return new ClassReader(outputFile.asByteArray()); return ClassReader(outputFile.asByteArray())
} }
VirtualFile file = findVirtualFileImprecise(state, internalName); val file = findVirtualFileImprecise(state, internalName)
if (file != null) { if (file != null) {
return new ClassReader(file.contentsToByteArray()); return ClassReader(file.contentsToByteArray())
} }
throw new RuntimeException("Couldn't find virtual file for " + internalName); throw RuntimeException("Couldn't find virtual file for " + internalName)
}
catch (IOException e) {
throw new RuntimeException(e);
} }
catch (e: IOException) {
throw RuntimeException(e)
} }
public static void generateFinallyMarker(@NotNull InstructionAdapter v, int depth, boolean start) {
v.iconst(depth);
v.invokestatic(INLINE_MARKER_CLASS_NAME, start ? INLINE_MARKER_FINALLY_START : INLINE_MARKER_FINALLY_END, "(I)V", false);
} }
public static boolean isFinallyEnd(@NotNull AbstractInsnNode node) { fun generateFinallyMarker(v: InstructionAdapter, depth: Int, start: Boolean) {
return isFinallyMarker(node, INLINE_MARKER_FINALLY_END); v.iconst(depth)
v.invokestatic(INLINE_MARKER_CLASS_NAME, if (start) INLINE_MARKER_FINALLY_START else INLINE_MARKER_FINALLY_END, "(I)V", false)
} }
public static boolean isFinallyStart(@NotNull AbstractInsnNode node) { fun isFinallyEnd(node: AbstractInsnNode): Boolean {
return isFinallyMarker(node, INLINE_MARKER_FINALLY_START); return isFinallyMarker(node, INLINE_MARKER_FINALLY_END)
} }
public static boolean isFinallyMarker(@Nullable AbstractInsnNode node) { fun isFinallyStart(node: AbstractInsnNode): Boolean {
return node != null && (isFinallyStart(node) || isFinallyEnd(node)); return isFinallyMarker(node, INLINE_MARKER_FINALLY_START)
} }
private static boolean isFinallyMarker(@NotNull AbstractInsnNode node, String name) { fun isFinallyMarker(node: AbstractInsnNode?): Boolean {
if (!(node instanceof MethodInsnNode)) return false; return node != null && (isFinallyStart(node) || isFinallyEnd(node))
MethodInsnNode method = (MethodInsnNode) node;
return INLINE_MARKER_CLASS_NAME.equals(method.owner) && name.equals(method.name);
} }
public static boolean isFinallyMarkerRequired(@NotNull MethodContext context) { private fun isFinallyMarker(node: AbstractInsnNode, name: String): Boolean {
return context.isInlineMethodContext() || context instanceof InlineLambdaContext; if (node !is MethodInsnNode) return false
val method = node
return INLINE_MARKER_CLASS_NAME == method.owner && name == method.name
} }
public static int getConstant(@NotNull AbstractInsnNode ins) { fun isFinallyMarkerRequired(context: MethodContext): Boolean {
int opcode = ins.getOpcode(); return context.isInlineMethodContext || context is InlineLambdaContext
}
fun getConstant(ins: AbstractInsnNode): Int {
val opcode = ins.opcode
if (opcode >= Opcodes.ICONST_0 && opcode <= Opcodes.ICONST_5) { if (opcode >= Opcodes.ICONST_0 && opcode <= Opcodes.ICONST_5) {
return 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) {
return ((IntInsnNode) ins).operand; return (ins as IntInsnNode).operand
} }
else { else {
LdcInsnNode index = (LdcInsnNode) ins; val index = ins as LdcInsnNode
return (Integer) index.cst; return index.cst as Int
} }
} }
public static void removeFinallyMarkers(@NotNull MethodNode intoNode) { fun removeFinallyMarkers(intoNode: MethodNode) {
InsnList instructions = intoNode.instructions; val instructions = intoNode.instructions
AbstractInsnNode curInstr = instructions.getFirst(); var curInstr: AbstractInsnNode? = instructions.first
while (curInstr != null) { while (curInstr != null) {
if (isFinallyMarker(curInstr)) { if (isFinallyMarker(curInstr)) {
AbstractInsnNode marker = curInstr; val marker = curInstr
//just to assert //just to assert
getConstant(marker.getPrevious()); getConstant(marker.previous)
curInstr = curInstr.getNext(); curInstr = curInstr.next
instructions.remove(marker.getPrevious()); instructions.remove(marker.previous)
instructions.remove(marker); instructions.remove(marker)
continue; continue
} }
curInstr = curInstr.getNext(); curInstr = curInstr.next
} }
} }
public static void addInlineMarker(@NotNull InstructionAdapter v, boolean isStartNotEnd) { fun addInlineMarker(v: InstructionAdapter, isStartNotEnd: Boolean) {
v.visitMethodInsn( v.visitMethodInsn(
Opcodes.INVOKESTATIC, INLINE_MARKER_CLASS_NAME, Opcodes.INVOKESTATIC, INLINE_MARKER_CLASS_NAME,
isStartNotEnd ? INLINE_MARKER_BEFORE_METHOD_NAME : INLINE_MARKER_AFTER_METHOD_NAME, if (isStartNotEnd) INLINE_MARKER_BEFORE_METHOD_NAME else INLINE_MARKER_AFTER_METHOD_NAME,
"()V", false "()V", false
); )
} }
public static boolean isInlineMarker(@NotNull AbstractInsnNode insn) { fun isInlineMarker(insn: AbstractInsnNode): Boolean {
return isInlineMarker(insn, null); return isInlineMarker(insn, null)
} }
private static boolean isInlineMarker(@NotNull AbstractInsnNode insn, @Nullable String name) { private fun isInlineMarker(insn: AbstractInsnNode, name: String?): Boolean {
if (!(insn instanceof MethodInsnNode)) { if (insn !is MethodInsnNode) {
return false; return false
} }
MethodInsnNode methodInsnNode = (MethodInsnNode) insn; val methodInsnNode = insn
return insn.getOpcode() == Opcodes.INVOKESTATIC && return insn.getOpcode() == Opcodes.INVOKESTATIC &&
methodInsnNode.owner.equals(INLINE_MARKER_CLASS_NAME) && methodInsnNode.owner == INLINE_MARKER_CLASS_NAME &&
(name != null ? methodInsnNode.name.equals(name) if (name != null)
: methodInsnNode.name.equals(INLINE_MARKER_BEFORE_METHOD_NAME) || methodInsnNode.name == name
methodInsnNode.name.equals(INLINE_MARKER_AFTER_METHOD_NAME)); else
methodInsnNode.name == INLINE_MARKER_BEFORE_METHOD_NAME || methodInsnNode.name == INLINE_MARKER_AFTER_METHOD_NAME
} }
public static boolean isBeforeInlineMarker(@NotNull AbstractInsnNode insn) { fun isBeforeInlineMarker(insn: AbstractInsnNode): Boolean {
return isInlineMarker(insn, INLINE_MARKER_BEFORE_METHOD_NAME); return isInlineMarker(insn, INLINE_MARKER_BEFORE_METHOD_NAME)
} }
public static boolean isAfterInlineMarker(@NotNull AbstractInsnNode insn) { fun isAfterInlineMarker(insn: AbstractInsnNode): Boolean {
return isInlineMarker(insn, INLINE_MARKER_AFTER_METHOD_NAME); return isInlineMarker(insn, INLINE_MARKER_AFTER_METHOD_NAME)
} }
public static int getLoadStoreArgSize(int opcode) { fun getLoadStoreArgSize(opcode: Int): Int {
return opcode == Opcodes.DSTORE || opcode == Opcodes.LSTORE || opcode == Opcodes.DLOAD || opcode == Opcodes.LLOAD ? 2 : 1; return if (opcode == Opcodes.DSTORE || opcode == Opcodes.LSTORE || opcode == Opcodes.DLOAD || opcode == Opcodes.LLOAD) 2 else 1
} }
public static boolean isStoreInstruction(int opcode) { fun isStoreInstruction(opcode: Int): Boolean {
return opcode >= Opcodes.ISTORE && opcode <= Opcodes.ASTORE; return opcode >= Opcodes.ISTORE && opcode <= Opcodes.ASTORE
} }
public static int calcMarkerShift(@NotNull Parameters parameters, @NotNull MethodNode node) { fun calcMarkerShift(parameters: Parameters, node: MethodNode): Int {
int markerShiftTemp = getIndexAfterLastMarker(node); val markerShiftTemp = getIndexAfterLastMarker(node)
return markerShiftTemp - parameters.getRealParametersSizeOnStack() + parameters.getArgsSizeOnStack(); return markerShiftTemp - parameters.realParametersSizeOnStack + parameters.argsSizeOnStack
} }
private static int getIndexAfterLastMarker(@NotNull MethodNode node) { private fun getIndexAfterLastMarker(node: MethodNode): Int {
int result = -1; var result = -1
for (LocalVariableNode variable : node.localVariables) { for (variable in node.localVariables) {
if (isFakeLocalVariableForInline(variable.name)) { if (isFakeLocalVariableForInline(variable.name)) {
result = Math.max(result, variable.index + 1); result = Math.max(result, variable.index + 1)
} }
} }
return result; return result
} }
public static boolean isFakeLocalVariableForInline(@NotNull String name) { fun isFakeLocalVariableForInline(name: String): Boolean {
return name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) || return name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION) || name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT)
name.startsWith(JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT);
} }
public static boolean isThis0(@NotNull String name) { fun isThis0(name: String): Boolean {
return THIS$0.equals(name); return THIS_0 == name
} }
public static boolean isSpecialEnumMethod(@NotNull FunctionDescriptor functionDescriptor) { fun isSpecialEnumMethod(functionDescriptor: FunctionDescriptor): Boolean {
DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration(); val containingDeclaration = functionDescriptor.containingDeclaration as? PackageFragmentDescriptor ?: return false
if (!(containingDeclaration instanceof PackageFragmentDescriptor)) { if (containingDeclaration.fqName != KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME) {
return false; return false
} }
if (!((PackageFragmentDescriptor) containingDeclaration).getFqName().equals(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME)) { if (functionDescriptor.typeParameters.size != 1) {
return false; return false
} }
if (functionDescriptor.getTypeParameters().size() != 1) { val name = functionDescriptor.name.asString()
return false; val parameters = functionDescriptor.valueParameters
} return "enumValues" == name && parameters.size == 0 || "enumValueOf" == name && parameters.size == 1 && KotlinBuiltIns.isString(parameters[0].type)
String name = functionDescriptor.getName().asString();
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
return "enumValues".equals(name) && parameters.size() == 0 ||
"enumValueOf".equals(name) && parameters.size() == 1 && KotlinBuiltIns.isString(parameters.get(0).getType());
} }
public static MethodNode createSpecialEnumMethodBody( fun createSpecialEnumMethodBody(
@NotNull ExpressionCodegen codegen, codegen: ExpressionCodegen,
@NotNull String name, name: String,
@NotNull KotlinType type, type: KotlinType,
@NotNull KotlinTypeMapper typeMapper typeMapper: KotlinTypeMapper
) { ): MethodNode {
boolean isValueOf = "enumValueOf".equals(name); val isValueOf = "enumValueOf" == name
Type invokeType = typeMapper.mapType(type); val invokeType = typeMapper.mapType(type)
String desc = getSpecialEnumFunDescriptor(invokeType, isValueOf); val desc = getSpecialEnumFunDescriptor(invokeType, isValueOf)
MethodNode node = new MethodNode(API, Opcodes.ACC_STATIC, "fake", desc, null, null); val node = MethodNode(API, Opcodes.ACC_STATIC, "fake", desc, null, null)
codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.ENUM_REIFIED, new InstructionAdapter(node)); codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.ENUM_REIFIED, InstructionAdapter(node))
if (isValueOf) { if (isValueOf) {
node.visitInsn(Opcodes.ACONST_NULL); node.visitInsn(Opcodes.ACONST_NULL)
node.visitVarInsn(Opcodes.ALOAD, 0); node.visitVarInsn(Opcodes.ALOAD, 0)
node.visitMethodInsn(Opcodes.INVOKESTATIC, ENUM_TYPE.getInternalName(), "valueOf", node.visitMethodInsn(Opcodes.INVOKESTATIC, ENUM_TYPE.internalName, "valueOf",
Type.getMethodDescriptor(ENUM_TYPE, JAVA_CLASS_TYPE, AsmTypes.JAVA_STRING_TYPE), false); Type.getMethodDescriptor(ENUM_TYPE, JAVA_CLASS_TYPE, AsmTypes.JAVA_STRING_TYPE), false)
} }
else { else {
node.visitInsn(Opcodes.ICONST_0); node.visitInsn(Opcodes.ICONST_0)
node.visitTypeInsn(Opcodes.ANEWARRAY, ENUM_TYPE.getInternalName()); node.visitTypeInsn(Opcodes.ANEWARRAY, ENUM_TYPE.internalName)
} }
node.visitInsn(Opcodes.ARETURN); node.visitInsn(Opcodes.ARETURN)
node.visitMaxs(isValueOf ? 3 : 2, isValueOf ? 1 : 0); node.visitMaxs(if (isValueOf) 3 else 2, if (isValueOf) 1 else 0)
return node; return node
} }
@NotNull fun getSpecialEnumFunDescriptor(type: Type, isValueOf: Boolean): String {
public static String getSpecialEnumFunDescriptor(@NotNull Type type, boolean isValueOf) { return if (isValueOf) Type.getMethodDescriptor(type, AsmTypes.JAVA_STRING_TYPE) else Type.getMethodDescriptor(AsmUtil.getArrayType(type))
return isValueOf ? Type.getMethodDescriptor(type, AsmTypes.JAVA_STRING_TYPE) : Type.getMethodDescriptor(AsmUtil.getArrayType(type));
} }
}
@@ -22,7 +22,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.ReadOnly; import org.jetbrains.annotations.ReadOnly;
import org.jetbrains.annotations.TestOnly; import org.jetbrains.annotations.TestOnly;
import org.jetbrains.kotlin.codegen.optimization.common.UtilKt;
import org.jetbrains.org.objectweb.asm.Label; import org.jetbrains.org.objectweb.asm.Label;
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.Type;
@@ -34,7 +33,7 @@ import java.io.PrintWriter;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.*; import java.util.*;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*; import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.*;
import static org.jetbrains.kotlin.codegen.inline.MethodInlinerUtilKt.getNextMeaningful; import static org.jetbrains.kotlin.codegen.inline.MethodInlinerUtilKt.getNextMeaningful;
public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor { public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
@@ -106,7 +105,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
private int initAndGetVarIndexForNonLocalReturnValue() { private int initAndGetVarIndexForNonLocalReturnValue() {
MaxLocalsCalculator tempCalcNode = new MaxLocalsCalculator( MaxLocalsCalculator tempCalcNode = new MaxLocalsCalculator(
InlineCodegenUtil.API, API,
inlineFun.access, inlineFun.desc, null inlineFun.access, inlineFun.desc, null
); );
inlineFun.accept(tempCalcNode); inlineFun.accept(tempCalcNode);
@@ -126,8 +125,8 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
processInstruction(curIns, false); processInstruction(curIns, false);
//At this point only global return is possible, local one already substituted with: goto endLabel //At this point only global return is possible, local one already substituted with: goto endLabel
if (!InlineCodegenUtil.isReturnOpcode(curIns.getOpcode()) || if (!isReturnOpcode(curIns.getOpcode()) ||
!InlineCodegenUtil.isMarkedReturn(curIns)) { !isMarkedReturn(curIns)) {
curIns = curIns.getPrevious(); curIns = curIns.getPrevious();
continue; continue;
} }
@@ -147,7 +146,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
AbstractInsnNode nextPrev = instrInsertFinallyBefore.getPrevious(); AbstractInsnNode nextPrev = instrInsertFinallyBefore.getPrevious();
assert markedReturn.getNext() instanceof LabelNode : "Label should be occurred after non-local return"; assert markedReturn.getNext() instanceof LabelNode : "Label should be occurred after non-local return";
LabelNode newFinallyEnd = (LabelNode) markedReturn.getNext(); LabelNode newFinallyEnd = (LabelNode) markedReturn.getNext();
Type nonLocalReturnType = InlineCodegenUtil.getReturnType(markedReturn.getOpcode()); Type nonLocalReturnType = getReturnType(markedReturn.getOpcode());
//Generally there could be several tryCatch blocks (group) on one code interval (same start and end labels, but maybe different handlers) - //Generally there could be several tryCatch blocks (group) on one code interval (same start and end labels, but maybe different handlers) -
// all of them refer to one try/*catches*/finally or try/catches. // all of them refer to one try/*catches*/finally or try/catches.
@@ -216,7 +215,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
finallyBlockCopy.visitLabel(insertedBlockEnd); finallyBlockCopy.visitLabel(insertedBlockEnd);
//Copying finally body before non-local return instruction //Copying finally body before non-local return instruction
InlineCodegenUtil.insertNodeBefore(finallyBlockCopy, inlineFun, instrInsertFinallyBefore); insertNodeBefore(finallyBlockCopy, inlineFun, instrInsertFinallyBefore);
updateExceptionTable(clusterBlocks, newFinallyStart, newFinallyEnd, updateExceptionTable(clusterBlocks, newFinallyStart, newFinallyEnd,
tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info); tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info);
@@ -251,7 +250,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
int depthShift int depthShift
) { ) {
if (isInsOrJumpInsideFinally) { if (isInsOrJumpInsideFinally) {
if (InlineCodegenUtil.isFinallyMarker(currentIns.getNext())) { if (isFinallyMarker(currentIns.getNext())) {
Integer constant = getConstant(currentIns); Integer constant = getConstant(currentIns);
finallyBlockCopy.visitLdcInsn(constant + depthShift); finallyBlockCopy.visitLdcInsn(constant + depthShift);
} else { } else {
@@ -455,7 +454,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor {
AbstractInsnNode current = meaningful.getNext(); AbstractInsnNode current = meaningful.getNext();
while (endFinallyChainExclusive != current) { while (endFinallyChainExclusive != current) {
current = current.getNext(); current = current.getNext();
if (InlineCodegenUtil.isFinallyEnd(current)) { if (isFinallyEnd(current)) {
Integer currentDepth = getConstant(current.getPrevious()); Integer currentDepth = getConstant(current.getPrevious());
if (currentDepth.equals(finallyDepth)) { if (currentDepth.equals(finallyDepth)) {
endFinallyChainExclusive = current.getNext(); endFinallyChainExclusive = current.getNext();
@@ -106,10 +106,10 @@ class DefaultLambda(
override fun isMyLabel(name: String): Boolean = false override fun isMyLabel(name: String): Boolean = false
override fun generateLambdaBody(codegen: ExpressionCodegen, reifiedTypeInliner: ReifiedTypeInliner) { override fun generateLambdaBody(codegen: ExpressionCodegen, reifiedTypeInliner: ReifiedTypeInliner) {
val classReader = InlineCodegenUtil.buildClassReaderByInternalName(codegen.state, lambdaClassType.internalName) val classReader = buildClassReaderByInternalName(codegen.state, lambdaClassType.internalName)
var isPropertyReference = false var isPropertyReference = false
var isFunctionReference = false var isFunctionReference = false
classReader.accept(object: ClassVisitor(InlineCodegenUtil.API){ classReader.accept(object: ClassVisitor(API){
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array<out String>?) { override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String?, interfaces: Array<out String>?) {
isPropertyReference = superName?.startsWith("kotlin/jvm/internal/PropertyReference") ?: false isPropertyReference = superName?.startsWith("kotlin/jvm/internal/PropertyReference") ?: false
isFunctionReference = "kotlin/jvm/internal/FunctionReference" == superName isFunctionReference = "kotlin/jvm/internal/FunctionReference" == superName
@@ -128,7 +128,7 @@ class DefaultLambda(
} }
val descriptor = Type.getMethodDescriptor(Type.VOID_TYPE, *capturedArgs) val descriptor = Type.getMethodDescriptor(Type.VOID_TYPE, *capturedArgs)
val constructor = InlineCodegenUtil.getMethodNode( val constructor = getMethodNode(
classReader.b, classReader.b,
"<init>", "<init>",
descriptor, descriptor,
@@ -156,7 +156,7 @@ class DefaultLambda(
codegen.state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor).asmMethod.descriptor codegen.state.typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor).asmMethod.descriptor
) )
node = InlineCodegenUtil.getMethodNode( node = getMethodNode(
classReader.b, classReader.b,
invokeMethod.name, invokeMethod.name,
invokeMethod.descriptor, invokeMethod.descriptor,
@@ -271,11 +271,11 @@ class ExpressionLambda(
val jvmMethodSignature = typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor) val jvmMethodSignature = typeMapper.mapSignatureSkipGeneric(invokeMethodDescriptor)
val asmMethod = jvmMethodSignature.asmMethod val asmMethod = jvmMethodSignature.asmMethod
val methodNode = MethodNode( val methodNode = MethodNode(
InlineCodegenUtil.API, AsmUtil.getMethodAsmFlags(invokeMethodDescriptor, context.contextKind, codegen.state), API, AsmUtil.getMethodAsmFlags(invokeMethodDescriptor, context.contextKind, codegen.state),
asmMethod.name, asmMethod.descriptor, null, null asmMethod.name, asmMethod.descriptor, null, null
) )
node = InlineCodegenUtil.wrapWithMaxLocalCalc(methodNode).let { adapter -> node = wrapWithMaxLocalCalc(methodNode).let { adapter ->
val smap = InlineCodegen.generateMethodBody( val smap = InlineCodegen.generateMethodBody(
adapter, invokeMethodDescriptor, context, functionWithBodyOrCallableReference, jvmMethodSignature, codegen, this adapter, invokeMethodDescriptor, context, functionWithBodyOrCallableReference, jvmMethodSignature, codegen, this
) )
@@ -103,7 +103,7 @@ class LocalVarRemapper(private val params: Parameters, private val additionalShi
val remapInfo = remap(`var`) val remapInfo = remap(`var`)
val value = remapInfo.value val value = remapInfo.value
if (value is StackValue.Local) { if (value is StackValue.Local) {
val isStore = InlineCodegenUtil.isStoreInstruction(opcode) val isStore = isStoreInstruction(opcode)
if (remapInfo.parameterInfo != null) { if (remapInfo.parameterInfo != null) {
//All remapped value parameters can't be rewritten except case of default ones. //All remapped value parameters can't be rewritten except case of default ones.
//On remapping default parameter to actual value there is only one instruction that writes to it according to mask value //On remapping default parameter to actual value there is only one instruction that writes to it according to mask value
@@ -23,10 +23,12 @@ import org.jetbrains.org.objectweb.asm.MethodVisitor;
import org.jetbrains.org.objectweb.asm.TypePath; import org.jetbrains.org.objectweb.asm.TypePath;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.API;
public class MethodBodyVisitor extends InstructionAdapter { public class MethodBodyVisitor extends InstructionAdapter {
public MethodBodyVisitor(MethodVisitor mv) { public MethodBodyVisitor(MethodVisitor mv) {
super(InlineCodegenUtil.API, mv); super(API, mv);
} }
@Override @Override
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.ClosureCodegen import org.jetbrains.kotlin.codegen.ClosureCodegen
import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
@@ -87,7 +86,7 @@ class MethodInliner(
transformedNode.instructions.resetLabels() transformedNode.instructions.resetLabels()
val resultNode = MethodNode( val resultNode = MethodNode(
InlineCodegenUtil.API, transformedNode.access, transformedNode.name, transformedNode.desc, API, transformedNode.access, transformedNode.name, transformedNode.desc,
transformedNode.signature, transformedNode.exceptions?.toTypedArray() transformedNode.signature, transformedNode.exceptions?.toTypedArray()
) )
val visitor = RemapVisitor(resultNode, remapper, nodeRemapper) val visitor = RemapVisitor(resultNode, remapper, nodeRemapper)
@@ -130,7 +129,7 @@ class MethodInliner(
AsmTypeRemapper(remapper, result) AsmTypeRemapper(remapper, result)
) )
val markerShift = InlineCodegenUtil.calcMarkerShift(parameters, node) val markerShift = calcMarkerShift(parameters, node)
val lambdaInliner = object : InlineAdapter(remappingMethodAdapter, parameters.argsSizeOnStack, sourceMapper) { val lambdaInliner = object : InlineAdapter(remappingMethodAdapter, parameters.argsSizeOnStack, sourceMapper) {
private var transformationInfo: TransformationInfo? = null private var transformationInfo: TransformationInfo? = null
@@ -312,11 +311,11 @@ class MethodInliner(
val realParametersSize = parameters.realParametersSizeOnStack val realParametersSize = parameters.realParametersSizeOnStack
val transformedNode = object : MethodNode( val transformedNode = object : MethodNode(
InlineCodegenUtil.API, node.access, node.name, API, node.access, node.name,
Type.getMethodDescriptor(Type.getReturnType(node.desc), *(Type.getArgumentTypes(node.desc) + parameters.capturedTypes)), Type.getMethodDescriptor(Type.getReturnType(node.desc), *(Type.getArgumentTypes(node.desc) + parameters.capturedTypes)),
node.signature, node.exceptions?.toTypedArray() node.signature, node.exceptions?.toTypedArray()
) { ) {
private val GENERATE_DEBUG_INFO = InlineCodegenUtil.GENERATE_SMAP && inlineOnlySmapSkipper == null private val GENERATE_DEBUG_INFO = GENERATE_SMAP && inlineOnlySmapSkipper == null
private val isInliningLambda = nodeRemapper.isInsideInliningLambda private val isInliningLambda = nodeRemapper.isInsideInliningLambda
@@ -343,8 +342,8 @@ class MethodInliner(
} }
override fun visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) { override fun visitMethodInsn(opcode: Int, owner: String, name: String, desc: String, itf: Boolean) {
if (InlineCodegenUtil.DEFAULT_LAMBDA_FAKE_CALL == owner) { if (DEFAULT_LAMBDA_FAKE_CALL == owner) {
val index = name.substringAfter(InlineCodegenUtil.DEFAULT_LAMBDA_FAKE_CALL).toInt() val index = name.substringAfter(DEFAULT_LAMBDA_FAKE_CALL).toInt()
val lambda = getLambdaIfExists(index) as DefaultLambda val lambda = getLambdaIfExists(index) as DefaultLambda
lambda.parameterOffsetsInDefault.zip(lambda.capturedVars).asReversed().forEach { lambda.parameterOffsetsInDefault.zip(lambda.capturedVars).asReversed().forEach {
(_, captured) -> (_, captured) ->
@@ -362,7 +361,7 @@ class MethodInliner(
name: String, desc: String, signature: String?, start: Label, end: Label, index: Int name: String, desc: String, signature: String?, start: Label, end: Label, index: Int
) { ) {
if (isInliningLambda || GENERATE_DEBUG_INFO) { if (isInliningLambda || GENERATE_DEBUG_INFO) {
val varSuffix = if (inliningContext.isRoot && !InlineCodegenUtil.isFakeLocalVariableForInline(name)) INLINE_FUN_VAR_SUFFIX else "" val varSuffix = if (inliningContext.isRoot && !isFakeLocalVariableForInline(name)) INLINE_FUN_VAR_SUFFIX else ""
val varName = if (!varSuffix.isEmpty() && name == "this") name + "_" else name val varName = if (!varSuffix.isEmpty() && name == "this") name + "_" else name
super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index)) super.visitLocalVariable(varName + varSuffix, desc, signature, start, end, getNewIndex(index))
} }
@@ -400,9 +399,9 @@ class MethodInliner(
awaitClassReification = true awaitClassReification = true
} }
else if (cur is MethodInsnNode) { else if (cur is MethodInsnNode) {
if (InlineCodegenUtil.isFinallyStart(cur)) { if (isFinallyStart(cur)) {
//TODO deep index calc could be more precise //TODO deep index calc could be more precise
currentFinallyDeep = InlineCodegenUtil.getConstant(cur.previous) currentFinallyDeep = getConstant(cur.previous)
} }
val owner = cur.owner val owner = cur.owner
@@ -668,7 +667,7 @@ class MethodInliner(
insertBeforeInsn: AbstractInsnNode, insertBeforeInsn: AbstractInsnNode,
sourceValueFrame: Frame<SourceValue> sourceValueFrame: Frame<SourceValue>
) { ) {
assert(InlineCodegenUtil.isReturnOpcode(returnInsn.opcode)) { "return instruction expected" } assert(isReturnOpcode(returnInsn.opcode)) { "return instruction expected" }
assert(returnOpcode < 0 || returnOpcode == returnInsn.opcode) { "Return op should be " + Printer.OPCODES[returnOpcode] + ", got " + Printer.OPCODES[returnInsn.opcode] } assert(returnOpcode < 0 || returnOpcode == returnInsn.opcode) { "Return op should be " + Printer.OPCODES[returnOpcode] + ", got " + Printer.OPCODES[returnInsn.opcode] }
returnOpcode = returnInsn.opcode returnOpcode = returnInsn.opcode
@@ -771,9 +770,9 @@ class MethodInliner(
var cur: AbstractInsnNode? = node.instructions.first var cur: AbstractInsnNode? = node.instructions.first
while (cur != null) { while (cur != null) {
if (cur is MethodInsnNode && InlineCodegenUtil.isFinallyMarker(cur)) { if (cur is MethodInsnNode && isFinallyMarker(cur)) {
val constant = cur.previous val constant = cur.previous
val curDeep = InlineCodegenUtil.getConstant(constant) val curDeep = getConstant(constant)
node.instructions.insert(constant, LdcInsnNode(curDeep + finallyDeepShift)) node.instructions.insert(constant, LdcInsnNode(curDeep + finallyDeepShift))
node.instructions.remove(constant) node.instructions.remove(constant)
} }
@@ -829,9 +828,9 @@ class MethodInliner(
val instructions = node.instructions val instructions = node.instructions
var insnNode: AbstractInsnNode? = instructions.first var insnNode: AbstractInsnNode? = instructions.first
while (insnNode != null) { while (insnNode != null) {
if (InlineCodegenUtil.isReturnOpcode(insnNode.opcode)) { if (isReturnOpcode(insnNode.opcode)) {
var isLocalReturn = true var isLocalReturn = true
val labelName = InlineCodegenUtil.getMarkedReturnLabelOrNull(insnNode) val labelName = getMarkedReturnLabelOrNull(insnNode)
if (labelName != null) { if (labelName != null) {
isLocalReturn = labelOwner.isMyLabel(labelName) isLocalReturn = labelOwner.isMyLabel(labelName)
@@ -121,7 +121,7 @@ fun MethodNode.findCapturedFieldAssignmentInstructions(): Sequence<FieldInsnNode
val prevPrev = fieldNode.previous?.previous as? VarInsnNode val prevPrev = fieldNode.previous?.previous as? VarInsnNode
fieldNode.opcode == Opcodes.PUTFIELD && fieldNode.opcode == Opcodes.PUTFIELD &&
InlineCodegenUtil.isCapturedFieldName(fieldNode.name) && isCapturedFieldName(fieldNode.name) &&
fieldNode.previous is VarInsnNode && prevPrev != null && prevPrev.`var` == 0 fieldNode.previous is VarInsnNode && prevPrev != null && prevPrev.`var` == 0
} }
} }
@@ -47,7 +47,7 @@ abstract class ObjectTransformer<out T : TransformationInfo>(@JvmField val trans
} }
fun createClassReader(): ClassReader { fun createClassReader(): ClassReader {
return InlineCodegenUtil.buildClassReaderByInternalName(state, transformationInfo.oldClassName) return buildClassReaderByInternalName(state, transformationInfo.oldClassName)
} }
} }
@@ -64,7 +64,7 @@ class WhenMappingTransformer(
/*MAPPING File could contains mappings for several enum classes, we should filter one*/ /*MAPPING File could contains mappings for several enum classes, we should filter one*/
val methodNodes = arrayListOf<MethodNode>() val methodNodes = arrayListOf<MethodNode>()
val fieldNode = transformationInfo.fieldNode val fieldNode = transformationInfo.fieldNode
classReader.accept(object : ClassVisitor(InlineCodegenUtil.API, classBuilder.visitor) { classReader.accept(object : ClassVisitor(API, classBuilder.visitor) {
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) { override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<String>) {
classBuilder.defineClass(null, version, access, name, signature, superName, interfaces) classBuilder.defineClass(null, version, access, name, signature, superName, interfaces)
} }
@@ -17,8 +17,6 @@
package org.jetbrains.kotlin.codegen.inline package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.CAPTURED_FIELD_FOLD_PREFIX
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.THIS
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.Type
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode
@@ -67,7 +65,7 @@ class RegeneratedLambdaFieldRemapper(
val field = findFieldInSuper(fin) ?: val field = findFieldInSuper(fin) ?:
//search in parent //search in parent
findFieldInSuper(FieldInsnNode( findFieldInSuper(FieldInsnNode(
Opcodes.GETSTATIC, originalLambdaInternalName, InlineCodegenUtil.`THIS$0`, Opcodes.GETSTATIC, originalLambdaInternalName, THIS_0,
Type.getObjectType(parent!!.originalLambdaInternalName!!).descriptor Type.getObjectType(parent!!.originalLambdaInternalName!!).descriptor
))?.also { fromParent = true } ?: ))?.also { fromParent = true } ?:
throw AssertionError("Couldn't find captured this $originalLambdaInternalName for $fieldName") throw AssertionError("Couldn't find captured this $originalLambdaInternalName for $fieldName")
@@ -169,7 +169,7 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
) = rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { stubCheckcast: AbstractInsnNode -> ) = rewriteNextTypeInsn(insn, Opcodes.CHECKCAST) { stubCheckcast: AbstractInsnNode ->
if (stubCheckcast !is TypeInsnNode) return false if (stubCheckcast !is TypeInsnNode) return false
val newMethodNode = MethodNode(InlineCodegenUtil.API) val newMethodNode = MethodNode(API)
generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe) generateAsCast(InstructionAdapter(newMethodNode), kotlinType, asmType, safe)
instructions.insert(insn, newMethodNode.instructions) instructions.insert(insn, newMethodNode.instructions)
@@ -189,7 +189,7 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
) = rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { stubInstanceOf: AbstractInsnNode -> ) = rewriteNextTypeInsn(insn, Opcodes.INSTANCEOF) { stubInstanceOf: AbstractInsnNode ->
if (stubInstanceOf !is TypeInsnNode) return false if (stubInstanceOf !is TypeInsnNode) return false
val newMethodNode = MethodNode(InlineCodegenUtil.API) val newMethodNode = MethodNode(API)
generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType) generateIsCheck(InstructionAdapter(newMethodNode), kotlinType, asmType)
instructions.insert(insn, newMethodNode.instructions) instructions.insert(insn, newMethodNode.instructions)
@@ -231,14 +231,14 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
if (next3 is MethodInsnNode && next3.name == "valueOf") { if (next3 is MethodInsnNode && next3.name == "valueOf") {
instructions.remove(next1) instructions.remove(next1)
next3.owner = parameter.internalName next3.owner = parameter.internalName
next3.desc = InlineCodegenUtil.getSpecialEnumFunDescriptor(parameter, true) next3.desc = getSpecialEnumFunDescriptor(parameter, true)
return true return true
} }
} }
else if (next1.opcode == Opcodes.ICONST_0 && next2.opcode == Opcodes.ANEWARRAY) { else if (next1.opcode == Opcodes.ICONST_0 && next2.opcode == Opcodes.ANEWARRAY) {
instructions.remove(next1) instructions.remove(next1)
instructions.remove(next2) instructions.remove(next2)
val desc = InlineCodegenUtil.getSpecialEnumFunDescriptor(parameter, false) val desc = getSpecialEnumFunDescriptor(parameter, false)
instructions.insert(insn, MethodInsnNode(Opcodes.INVOKESTATIC, parameter.internalName, "values", desc, false)) instructions.insert(insn, MethodInsnNode(Opcodes.INVOKESTATIC, parameter.internalName, "values", desc, false))
return true return true
} }
@@ -24,6 +24,8 @@ import org.jetbrains.org.objectweb.asm.Opcodes;
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter; import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode; import org.jetbrains.org.objectweb.asm.tree.FieldInsnNode;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.CAPTURED_FIELD_FOLD_PREFIX;
public class RemapVisitor extends MethodBodyVisitor { public class RemapVisitor extends MethodBodyVisitor {
private final LocalVarRemapper remapper; private final LocalVarRemapper remapper;
private final FieldRemapper nodeRemapper; private final FieldRemapper nodeRemapper;
@@ -59,7 +61,7 @@ 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(InlineCodegenUtil.CAPTURED_FIELD_FOLD_PREFIX) && if (name.startsWith(CAPTURED_FIELD_FOLD_PREFIX) &&
(nodeRemapper instanceof RegeneratedLambdaFieldRemapper || nodeRemapper.isRoot())) { (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);
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.codegen.inline package org.jetbrains.kotlin.codegen.inline
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.org.objectweb.asm.tree.TryCatchBlockNode import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode
@@ -18,8 +18,6 @@ package org.jetbrains.kotlin.codegen.inline
import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.OwnerKind import org.jetbrains.kotlin.codegen.OwnerKind
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.DEFAULT_LAMBDA_FAKE_CALL
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.getConstant
import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.isNeedClassReificationMarker import org.jetbrains.kotlin.codegen.inline.ReifiedTypeInliner.Companion.isNeedClassReificationMarker
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.asSequence import org.jetbrains.kotlin.codegen.optimization.common.asSequence
@@ -17,7 +17,8 @@
package org.jetbrains.kotlin.codegen.optimization.boxing package org.jetbrains.kotlin.codegen.optimization.boxing
import com.intellij.openapi.util.Pair import com.intellij.openapi.util.Pair
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.codegen.inline.getInsnOpcodeText
import org.jetbrains.kotlin.codegen.inline.getInsnText
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
import org.jetbrains.kotlin.codegen.optimization.common.* import org.jetbrains.kotlin.codegen.optimization.common.*
@@ -324,7 +325,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
} }
private fun throwCannotAdaptInstruction(insn: AbstractInsnNode): Nothing = private fun throwCannotAdaptInstruction(insn: AbstractInsnNode): Nothing =
throw AssertionError("Cannot adapt instruction: ${InlineCodegenUtil.getInsnText(insn)}") throw AssertionError("Cannot adapt instruction: ${getInsnText(insn)}")
private fun adaptAreEqualIntrinsic( private fun adaptAreEqualIntrinsic(
node: MethodNode, node: MethodNode,
@@ -392,7 +393,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
next.getOpcode() == Opcodes.IFNE -> next.getOpcode() == Opcodes.IFNE ->
insertBefore(insn, JumpInsnNode(ifNotEqualOpcode, nextLabel)) insertBefore(insn, JumpInsnNode(ifNotEqualOpcode, nextLabel))
else -> else ->
throw AssertionError("IFEQ or IFNE expected: " + InlineCodegenUtil.getInsnOpcodeText(next)) throw AssertionError("IFEQ or IFNE expected: " + getInsnOpcodeText(next))
} }
} }
} }
@@ -46,7 +46,7 @@
package org.jetbrains.kotlin.codegen.optimization.common package org.jetbrains.kotlin.codegen.optimization.common
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.codegen.inline.getInsnText
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.Type
import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.*
@@ -146,10 +146,10 @@ open class MethodAnalyzer<V : Value>(
} }
catch (e: AnalyzerException) { catch (e: AnalyzerException) {
throw AnalyzerException(e.node, "Error at instruction #" + insn + " ${InlineCodegenUtil.getInsnText(insnNode)}: " + e.message, e) throw AnalyzerException(e.node, "Error at instruction #" + insn + " ${getInsnText(insnNode)}: " + e.message, e)
} }
catch (e: Exception) { catch (e: Exception) {
throw AnalyzerException(insnNode, "Error at instruction #" + insn + " ${InlineCodegenUtil.getInsnText(insnNode)}: " + e.message, e) throw AnalyzerException(insnNode, "Error at instruction #" + insn + " ${getInsnText(insnNode)}: " + e.message, e)
} }
} }
@@ -19,7 +19,6 @@ package org.jetbrains.kotlin.codegen.optimization.common;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.codegen.AsmUtil; import org.jetbrains.kotlin.codegen.AsmUtil;
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil;
import org.jetbrains.org.objectweb.asm.Handle; import org.jetbrains.org.objectweb.asm.Handle;
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.Type;
@@ -30,6 +29,7 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter;
import java.util.List; import java.util.List;
import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.getInsnOpcodeText;
import static org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue.*; import static org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue.*;
public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implements Opcodes { public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implements Opcodes {
@@ -137,7 +137,7 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
case NEW: case NEW:
return newValue(Type.getObjectType(((TypeInsnNode) insn).desc)); return newValue(Type.getObjectType(((TypeInsnNode) insn).desc));
default: default:
throw new IllegalArgumentException("Unexpected instruction: " + InlineCodegenUtil.getInsnOpcodeText(insn)); throw new IllegalArgumentException("Unexpected instruction: " + getInsnOpcodeText(insn));
} }
} }
@@ -222,7 +222,7 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
case PUTFIELD: case PUTFIELD:
return null; return null;
default: default:
throw new IllegalArgumentException("Unexpected instruction: " + InlineCodegenUtil.getInsnOpcodeText(insn)); throw new IllegalArgumentException("Unexpected instruction: " + getInsnOpcodeText(insn));
} }
} }
@@ -341,7 +341,7 @@ public class OptimizationBasicInterpreter extends Interpreter<BasicValue> implem
case IFNONNULL: case IFNONNULL:
return null; return null;
default: default:
throw new IllegalArgumentException("Unexpected instruction: " + InlineCodegenUtil.getInsnOpcodeText(insn)); throw new IllegalArgumentException("Unexpected instruction: " + getInsnOpcodeText(insn));
} }
} }
@@ -16,7 +16,7 @@
package org.jetbrains.kotlin.codegen.optimization.common package org.jetbrains.kotlin.codegen.optimization.common
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.codegen.inline.getInsnText
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Opcodes.* import org.jetbrains.org.objectweb.asm.Opcodes.*
@@ -176,7 +176,7 @@ fun insnListOf(vararg insns: AbstractInsnNode) = InsnList().apply { insns.forEac
fun AbstractInsnNode.isStoreOperation(): Boolean = opcode in Opcodes.ISTORE..Opcodes.ASTORE fun AbstractInsnNode.isStoreOperation(): Boolean = opcode in Opcodes.ISTORE..Opcodes.ASTORE
fun AbstractInsnNode.isLoadOperation(): Boolean = opcode in Opcodes.ILOAD..Opcodes.ALOAD fun AbstractInsnNode.isLoadOperation(): Boolean = opcode in Opcodes.ILOAD..Opcodes.ALOAD
val AbstractInsnNode?.insnText get() = InlineCodegenUtil.getInsnText(this) val AbstractInsnNode?.insnText get() = getInsnText(this)
val AbstractInsnNode?.debugText get() = val AbstractInsnNode?.debugText get() =
if (this == null) "<null>" else "${this::class.java.simpleName}: $insnText" if (this == null) "<null>" else "${this::class.java.simpleName}: $insnText"
@@ -17,7 +17,9 @@
package org.jetbrains.kotlin.codegen.optimization.fixStack package org.jetbrains.kotlin.codegen.optimization.fixStack
import com.intellij.util.containers.Stack import com.intellij.util.containers.Stack
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.codegen.inline.isAfterInlineMarker
import org.jetbrains.kotlin.codegen.inline.isBeforeInlineMarker
import org.jetbrains.kotlin.codegen.inline.isMarkedReturn
import org.jetbrains.kotlin.codegen.optimization.common.MethodAnalyzer import org.jetbrains.kotlin.codegen.optimization.common.MethodAnalyzer
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
@@ -103,11 +105,11 @@ internal class FixStackAnalyzer(
executeSaveStackBeforeTry(insn) executeSaveStackBeforeTry(insn)
PseudoInsn.RESTORE_STACK_IN_TRY_CATCH.isa(insn) -> PseudoInsn.RESTORE_STACK_IN_TRY_CATCH.isa(insn) ->
executeRestoreStackInTryCatch(insn) executeRestoreStackInTryCatch(insn)
InlineCodegenUtil.isBeforeInlineMarker(insn) -> isBeforeInlineMarker(insn) ->
executeBeforeInlineCallMarker(insn) executeBeforeInlineCallMarker(insn)
InlineCodegenUtil.isAfterInlineMarker(insn) -> isAfterInlineMarker(insn) ->
executeAfterInlineCallMarker(insn) executeAfterInlineCallMarker(insn)
InlineCodegenUtil.isMarkedReturn(insn) -> { isMarkedReturn(insn) -> {
// KT-9644: might throw "Incompatible return type" on non-local return, in fact we don't care. // KT-9644: might throw "Incompatible return type" on non-local return, in fact we don't care.
if (insn.opcode == Opcodes.RETURN) return if (insn.opcode == Opcodes.RETURN) return
} }
@@ -18,7 +18,8 @@ package org.jetbrains.kotlin.codegen.optimization.fixStack
import com.intellij.util.SmartList import com.intellij.util.SmartList
import com.intellij.util.containers.Stack import com.intellij.util.containers.Stack
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.codegen.inline.isAfterInlineMarker
import org.jetbrains.kotlin.codegen.inline.isBeforeInlineMarker
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
import org.jetbrains.kotlin.codegen.pseudoInsns.parsePseudoInsnOrNull import org.jetbrains.kotlin.codegen.pseudoInsns.parsePseudoInsnOrNull
@@ -56,10 +57,10 @@ internal class FixStackContext(val methodNode: MethodNode) {
visitFakeAlwaysTrueIfeq(insnNode) visitFakeAlwaysTrueIfeq(insnNode)
pseudoInsn == PseudoInsn.FAKE_ALWAYS_FALSE_IFEQ -> pseudoInsn == PseudoInsn.FAKE_ALWAYS_FALSE_IFEQ ->
visitFakeAlwaysFalseIfeq(insnNode) visitFakeAlwaysFalseIfeq(insnNode)
InlineCodegenUtil.isBeforeInlineMarker(insnNode) -> { isBeforeInlineMarker(insnNode) -> {
inlineMarkersStack.push(insnNode) inlineMarkersStack.push(insnNode)
} }
InlineCodegenUtil.isAfterInlineMarker(insnNode) -> { isAfterInlineMarker(insnNode) -> {
assert(inlineMarkersStack.isNotEmpty()) { "Mismatching after inline method marker at ${indexOf(insnNode)}" } assert(inlineMarkersStack.isNotEmpty()) { "Mismatching after inline method marker at ${indexOf(insnNode)}" }
openingInlineMethodMarker[insnNode] = inlineMarkersStack.pop() openingInlineMethodMarker[insnNode] = inlineMarkersStack.pop()
} }
@@ -16,7 +16,9 @@
package org.jetbrains.kotlin.codegen.optimization.fixStack package org.jetbrains.kotlin.codegen.optimization.fixStack
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.codegen.inline.isAfterInlineMarker
import org.jetbrains.kotlin.codegen.inline.isBeforeInlineMarker
import org.jetbrains.kotlin.codegen.inline.isInlineMarker
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
@@ -33,7 +35,7 @@ class FixStackMethodTransformer : MethodTransformer() {
// If inline method markers are inconsistent, remove them now // If inline method markers are inconsistent, remove them now
if (!context.consistentInlineMarkers) { if (!context.consistentInlineMarkers) {
InsnSequence(methodNode.instructions).forEach { insnNode -> InsnSequence(methodNode.instructions).forEach { insnNode ->
if (InlineCodegenUtil.isInlineMarker(insnNode)) if (isInlineMarker(insnNode))
methodNode.instructions.remove(insnNode) methodNode.instructions.remove(insnNode)
} }
} }
@@ -131,9 +133,9 @@ class FixStackMethodTransformer : MethodTransformer() {
transformSaveStackMarker(methodNode, actions, analyzer, marker, localVariablesManager) transformSaveStackMarker(methodNode, actions, analyzer, marker, localVariablesManager)
pseudoInsn == PseudoInsn.RESTORE_STACK_IN_TRY_CATCH -> pseudoInsn == PseudoInsn.RESTORE_STACK_IN_TRY_CATCH ->
transformRestoreStackMarker(methodNode, actions, marker, localVariablesManager) transformRestoreStackMarker(methodNode, actions, marker, localVariablesManager)
InlineCodegenUtil.isBeforeInlineMarker(marker) -> isBeforeInlineMarker(marker) ->
transformBeforeInlineCallMarker(methodNode, actions, analyzer, marker, localVariablesManager) transformBeforeInlineCallMarker(methodNode, actions, analyzer, marker, localVariablesManager)
InlineCodegenUtil.isAfterInlineMarker(marker) -> isAfterInlineMarker(marker) ->
transformAfterInlineCallMarker(methodNode, actions, analyzer, marker, localVariablesManager) transformAfterInlineCallMarker(methodNode, actions, analyzer, marker, localVariablesManager)
} }
} }
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.codegen
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.backend.common.output.OutputFile import org.jetbrains.kotlin.backend.common.output.OutputFile
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.codegen.inline.GENERATE_SMAP
import org.jetbrains.kotlin.codegen.inline.RangeMapping import org.jetbrains.kotlin.codegen.inline.RangeMapping
import org.jetbrains.kotlin.codegen.inline.SMAPParser import org.jetbrains.kotlin.codegen.inline.SMAPParser
import org.jetbrains.kotlin.codegen.inline.toRange import org.jetbrains.kotlin.codegen.inline.toRange
@@ -61,7 +61,7 @@ object SMAPTestUtil {
} }
fun checkSMAP(inputFiles: List<CodegenTestCase.TestFile>, outputFiles: Iterable<OutputFile>) { fun checkSMAP(inputFiles: List<CodegenTestCase.TestFile>, outputFiles: Iterable<OutputFile>) {
if (!InlineCodegenUtil.GENERATE_SMAP) return if (!GENERATE_SMAP) return
val sourceData = inputFiles.mapNotNull { extractSmapFromTestDataFile(it) } val sourceData = inputFiles.mapNotNull { extractSmapFromTestDataFile(it) }
val compiledSmaps = extractSMAPFromClasses(outputFiles) val compiledSmaps = extractSMAPFromClasses(outputFiles)
@@ -29,7 +29,7 @@ import org.jetbrains.kotlin.cli.js.K2JSCompiler
import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.codegen.inline.GENERATE_SMAP
import org.jetbrains.kotlin.config.KotlinCompilerVersion.TEST_IS_PRE_RELEASE_SYSTEM_PROPERTY import org.jetbrains.kotlin.config.KotlinCompilerVersion.TEST_IS_PRE_RELEASE_SYSTEM_PROPERTY
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
@@ -433,7 +433,7 @@ class CompileKotlinAgainstCustomBinariesTest : TestCaseWithTmpdir() {
*E *E
""".trimIndent() + "\n" """.trimIndent() + "\n"
if (InlineCodegenUtil.GENERATE_SMAP) { if (GENERATE_SMAP) {
assertEquals(expected, debugInfo) assertEquals(expected, debugInfo)
} }
else { else {
@@ -21,12 +21,12 @@ import com.intellij.debugger.jdi.LocalVariableProxyImpl
import com.intellij.debugger.jdi.StackFrameProxyImpl import com.intellij.debugger.jdi.StackFrameProxyImpl
import com.intellij.debugger.ui.impl.watch.MethodsTracker import com.intellij.debugger.ui.impl.watch.MethodsTracker
import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl import com.intellij.debugger.ui.impl.watch.StackFrameDescriptorImpl
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.codegen.inline.isFakeLocalVariableForInline
class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDescriptorImpl(frame, MethodsTracker()), true) { class KotlinStackFrame(frame: StackFrameProxyImpl) : JavaStackFrame(StackFrameDescriptorImpl(frame, MethodsTracker()), true) {
override fun getVisibleVariables(): List<LocalVariableProxyImpl>? { override fun getVisibleVariables(): List<LocalVariableProxyImpl>? {
return super.getVisibleVariables().filter { return super.getVisibleVariables().filter {
!InlineCodegenUtil.isFakeLocalVariableForInline(it.name()) !isFakeLocalVariableForInline(it.name())
} }
} }
} }
@@ -29,7 +29,7 @@ import com.intellij.util.containers.ConcurrentWeakFactoryMap
import com.intellij.util.containers.ContainerUtil import com.intellij.util.containers.ContainerUtil
import com.sun.jdi.Location import com.sun.jdi.Location
import com.sun.jdi.ReferenceType import com.sun.jdi.ReferenceType
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.codegen.inline.API
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches import org.jetbrains.kotlin.idea.debugger.evaluate.KotlinDebuggerCaches
import org.jetbrains.kotlin.idea.refactoring.getLineCount import org.jetbrains.kotlin.idea.refactoring.getLineCount
@@ -199,7 +199,7 @@ private fun findClassFileByPath(packageName: String, className: String, outputDi
private fun readLineNumberTableMapping(bytes: ByteArray): Map<BytecodeMethodKey, Map<String, Set<Int>>> { private fun readLineNumberTableMapping(bytes: ByteArray): Map<BytecodeMethodKey, Map<String, Set<Int>>> {
val lineNumberMapping = HashMap<BytecodeMethodKey, Map<String, Set<Int>>>() val lineNumberMapping = HashMap<BytecodeMethodKey, Map<String, Set<Int>>>()
ClassReader(bytes).accept(object : ClassVisitor(InlineCodegenUtil.API) { ClassReader(bytes).accept(object : ClassVisitor(API) {
override fun visitMethod(access: Int, name: String?, desc: String?, signature: String?, exceptions: Array<out String>?): MethodVisitor? { override fun visitMethod(access: Int, name: String?, desc: String?, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
if (name == null || desc == null) { if (name == null || desc == null) {
return null return null
@@ -26,7 +26,8 @@ import org.jetbrains.eval4j.jdi.asJdiValue
import org.jetbrains.eval4j.jdi.asValue import org.jetbrains.eval4j.jdi.asValue
import org.jetbrains.eval4j.obj import org.jetbrains.eval4j.obj
import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil import org.jetbrains.kotlin.codegen.inline.INLINE_FUN_VAR_SUFFIX
import org.jetbrains.kotlin.codegen.inline.NUMBERED_FUNCTION_PREFIX
import org.jetbrains.kotlin.idea.debugger.isInsideInlineFunctionBody import org.jetbrains.kotlin.idea.debugger.isInsideInlineFunctionBody
import org.jetbrains.kotlin.idea.debugger.numberOfInlinedFunctions import org.jetbrains.kotlin.idea.debugger.numberOfInlinedFunctions
import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.idea.util.application.runReadAction
@@ -128,12 +129,12 @@ class FrameVisitor(context: EvaluationContextImpl) {
} }
private fun findLocalVariableForInlineArgument(name: String, number: Int, asmType: Type?, checkType: Boolean): Value? { private fun findLocalVariableForInlineArgument(name: String, number: Int, asmType: Type?, checkType: Boolean): Value? {
return findLocalVariable(name + InlineCodegenUtil.INLINE_FUN_VAR_SUFFIX.repeat(number), asmType, checkType) return findLocalVariable(name + INLINE_FUN_VAR_SUFFIX.repeat(number), asmType, checkType)
} }
private fun isFunctionType(type: Type?): Boolean { private fun isFunctionType(type: Type?): Boolean {
return type?.sort == Type.OBJECT && return type?.sort == Type.OBJECT &&
type.internalName.startsWith(InlineCodegenUtil.NUMBERED_FUNCTION_PREFIX) type.internalName.startsWith(NUMBERED_FUNCTION_PREFIX)
} }
private fun findLocalVariable(name: String, asmType: Type?, checkType: Boolean): Value? { private fun findLocalVariable(name: String, asmType: Type?, checkType: Boolean): Value? {
@@ -18,8 +18,8 @@ package org.jetbrains.kotlin.idea.debugger
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.kotlin.codegen.inline.API
import org.jetbrains.kotlin.codegen.inline.FileMapping import org.jetbrains.kotlin.codegen.inline.FileMapping
import org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil
import org.jetbrains.kotlin.codegen.inline.SMAP import org.jetbrains.kotlin.codegen.inline.SMAP
import org.jetbrains.kotlin.codegen.inline.SMAPParser import org.jetbrains.kotlin.codegen.inline.SMAPParser
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
@@ -62,7 +62,7 @@ fun mapStacktraceLineToSource(smapData: SmapData,
fun readDebugInfo(bytes: ByteArray): SmapData? { fun readDebugInfo(bytes: ByteArray): SmapData? {
val cr = ClassReader(bytes) val cr = ClassReader(bytes)
var debugInfo: String? = null var debugInfo: String? = null
cr.accept(object : ClassVisitor(InlineCodegenUtil.API) { cr.accept(object : ClassVisitor(API) {
override fun visitSource(source: String?, debug: String?) { override fun visitSource(source: String?, debug: String?) {
debugInfo = debug debugInfo = debug
} }